View Javadoc
1   /**
2    * Copyright (C) 2014-2015 Philip Helger (www.helger.com)
3    * philip[at]helger[dot]com
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *         http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package com.helger.schematron.xslt;
18  
19  import java.io.File;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.annotation.concurrent.NotThreadSafe;
24  import javax.xml.transform.Templates;
25  import javax.xml.transform.Transformer;
26  import javax.xml.transform.TransformerConfigurationException;
27  import javax.xml.transform.dom.DOMResult;
28  
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  import org.w3c.dom.Document;
32  
33  import com.helger.commons.ValueEnforcer;
34  import com.helger.commons.io.file.FilenameHelper;
35  import com.helger.commons.io.file.SimpleFileIO;
36  import com.helger.commons.io.resource.ClassPathResource;
37  import com.helger.commons.io.resource.IReadableResource;
38  import com.helger.commons.xml.serialize.write.XMLWriter;
39  import com.helger.commons.xml.serialize.write.XMLWriterSettings;
40  import com.helger.commons.xml.transform.TransformSourceFactory;
41  import com.helger.commons.xml.transform.XMLTransformerFactory;
42  import com.helger.schematron.xslt.SCHTransformerCustomizer.EStep;
43  
44  /**
45   * The XSLT preprocessor used to convert a Schematron XML document into an XSLT
46   * document. This implementation uses JAXP with Saxon to be used as the
47   * respective parser.
48   *
49   * @author Philip Helger
50   */
51  @NotThreadSafe
52  final class SchematronProviderXSLTFromSCH implements ISchematronXSLTBasedProvider
53  {
54    private static final Logger s_aLogger = LoggerFactory.getLogger (SchematronProviderXSLTFromSCH.class);
55  
56    /**
57     * The classpath directory where the Schematron 2 XSLT files reside.
58     */
59    private static final String SCHEMATRON_DIRECTORY_XSLT2 = "schematron/20100414-xslt2/";
60  
61    /**
62     * The class path to first XSLT to be applied.
63     */
64    private static final String XSLT2_STEP1 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_dsdl_include.xsl";
65  
66    /**
67     * The class path to second XSLT to be applied.
68     */
69    private static final String XSLT2_STEP2 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_abstract_expand.xsl";
70  
71    /**
72     * The class path to third and last XSLT to be applied.
73     */
74    private static final String XSLT2_STEP3 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_svrl_for_xslt2.xsl";
75  
76    /**
77     * This flag is for debugging purposes only. Only used during development.
78     */
79    private static final boolean SAVE_INTERMEDIATE_FILES = false;
80  
81    private static Templates s_aStep1;
82    private static Templates s_aStep2;
83    private static Templates s_aStep3;
84  
85    private final IReadableResource m_aSchematronResource;
86    private Document m_aSchematronXSLTDoc;
87    private Templates m_aSchematronXSLTTemplates;
88  
89    /**
90     * Constructor
91     *
92     * @param aSchematronResource
93     *        SCH resource
94     * @param aTransformerCustomizer
95     *        The customizer for XSLT {@link Transformer} objects. May not be
96     *        <code>null</code>.
97     */
98    public SchematronProviderXSLTFromSCH (@Nonnull final IReadableResource aSchematronResource,
99                                          @Nonnull final SCHTransformerCustomizer aTransformerCustomizer)
100   {
101     m_aSchematronResource = ValueEnforcer.notNull (aSchematronResource, "SchematronResource");
102     ValueEnforcer.notNull (aTransformerCustomizer, "TransformerCustomizer");
103 
104     try
105     {
106       // prepare all steps
107       if (s_aStep1 == null)
108         s_aStep1 = XMLTransformerFactory.newTemplates (new ClassPathResource (XSLT2_STEP1));
109       if (s_aStep2 == null)
110         s_aStep2 = XMLTransformerFactory.newTemplates (new ClassPathResource (XSLT2_STEP2));
111       if (s_aStep3 == null)
112         s_aStep3 = XMLTransformerFactory.newTemplates (new ClassPathResource (XSLT2_STEP3));
113 
114       // perform step 1 (Schematron -> ResultStep1)
115       final DOMResult aResult1 = new DOMResult ();
116       final Transformer aTransformer1 = s_aStep1.newTransformer ();
117       aTransformerCustomizer.customize (EStep.SCH2XSLT_1, aTransformer1);
118       aTransformer1.transform (TransformSourceFactory.create (aSchematronResource), aResult1);
119 
120       // perform step 2 (ResultStep1 -> ResultStep2)
121       final DOMResult aResult2 = new DOMResult ();
122       final Transformer aTransformer2 = s_aStep2.newTransformer ();
123       aTransformerCustomizer.customize (EStep.SCH2XSLT_2, aTransformer2);
124       aTransformer2.transform (TransformSourceFactory.create (aResult1.getNode ()), aResult2);
125 
126       if (SAVE_INTERMEDIATE_FILES)
127       {
128         final String sXML = XMLWriter.getXMLString (aResult2.getNode ());
129         SimpleFileIO.writeFile (new File ("test-minified",
130                                           FilenameHelper.getWithoutPath (aSchematronResource.getPath ()) +
131                                               ".min-xslt.sch"),
132                                 sXML,
133                                 XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ);
134       }
135 
136       // perform step 3 (ResultStep2 -> ResultStep3XSL)
137       final DOMResult aResult3 = new DOMResult ();
138       final Transformer aTransformer3 = s_aStep3.newTransformer ();
139       aTransformerCustomizer.customize (EStep.SCH2XSLT_3, aTransformer3);
140       aTransformer3.transform (TransformSourceFactory.create (aResult2.getNode ()), aResult3);
141 
142       // Save the underlying XSLT document....
143       // Note: Saxon 6.5.5 does not allow to clone the document node!!!!
144       m_aSchematronXSLTDoc = (Document) aResult3.getNode ();
145 
146       if (SAVE_INTERMEDIATE_FILES)
147       {
148         final String sXML = XMLWriter.getXMLString (m_aSchematronXSLTDoc);
149         SimpleFileIO.writeFile (new File ("test-final", FilenameHelper.getWithoutPath (aSchematronResource.getPath ()) +
150                                                         ".xslt"), sXML, XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ);
151       }
152 
153       // compile result of step 3
154       m_aSchematronXSLTTemplates = XMLTransformerFactory.newTemplates (TransformSourceFactory.create (m_aSchematronXSLTDoc));
155     }
156     catch (final Throwable t)
157     {
158       s_aLogger.error ("Schematron preprocessor error", t);
159     }
160   }
161 
162   @Nonnull
163   public IReadableResource getSchematronResource ()
164   {
165     return m_aSchematronResource;
166   }
167 
168   public boolean isValidSchematron ()
169   {
170     return m_aSchematronXSLTTemplates != null;
171   }
172 
173   @Nullable
174   public Document getXSLTDocument ()
175   {
176     return m_aSchematronXSLTDoc;
177   }
178 
179   @Nullable
180   public Transformer getXSLTTransformer () throws TransformerConfigurationException
181   {
182     return m_aSchematronXSLTTemplates == null ? null : m_aSchematronXSLTTemplates.newTransformer ();
183   }
184 }