View Javadoc
1   /**
2    * Copyright (C) 2014-2016 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.schematron.saxon.SchematronTransformerFactory;
39  import com.helger.schematron.xslt.SCHTransformerCustomizer.EStep;
40  import com.helger.xml.serialize.write.XMLWriter;
41  import com.helger.xml.serialize.write.XMLWriterSettings;
42  import com.helger.xml.transform.TransformSourceFactory;
43  import com.helger.xml.transform.XMLTransformerFactory;
44  
45  /**
46   * The XSLT preprocessor used to convert a Schematron XML document into an XSLT
47   * document. This implementation uses JAXP with Saxon to be used as the
48   * respective parser.
49   *
50   * @author Philip Helger
51   */
52  @NotThreadSafe
53  final class SchematronProviderXSLTFromSCH implements ISchematronXSLTBasedProvider
54  {
55    private static final Logger s_aLogger = LoggerFactory.getLogger (SchematronProviderXSLTFromSCH.class);
56  
57    /**
58     * The classpath directory where the Schematron 2 XSLT files reside.
59     */
60    private static final String SCHEMATRON_DIRECTORY_XSLT2 = "schematron/20100414-xslt2/";
61  
62    /**
63     * The class path to first XSLT to be applied.
64     */
65    private static final String XSLT2_STEP1 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_dsdl_include.xsl";
66  
67    /**
68     * The class path to second XSLT to be applied.
69     */
70    private static final String XSLT2_STEP2 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_abstract_expand.xsl";
71  
72    /**
73     * The class path to third and last XSLT to be applied.
74     */
75    private static final String XSLT2_STEP3 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_svrl_for_xslt2.xsl";
76  
77    /**
78     * This flag is for debugging purposes only. Only used during development.
79     */
80    private static final boolean SAVE_INTERMEDIATE_FILES = false;
81  
82    private static Templates s_aStep1;
83    private static Templates s_aStep2;
84    private static Templates s_aStep3;
85  
86    private final IReadableResource m_aSchematronResource;
87    private Document m_aSchematronXSLTDoc;
88    private Templates m_aSchematronXSLTTemplates;
89  
90    /**
91     * Constructor
92     *
93     * @param aSchematronResource
94     *        SCH resource
95     * @param aTransformerCustomizer
96     *        The customizer for XSLT {@link Transformer} objects. May not be
97     *        <code>null</code>.
98     */
99    public SchematronProviderXSLTFromSCH (@Nonnull final IReadableResource aSchematronResource,
100                                         @Nonnull final SCHTransformerCustomizer aTransformerCustomizer)
101   {
102     m_aSchematronResource = ValueEnforcer.notNull (aSchematronResource, "SchematronResource");
103     ValueEnforcer.notNull (aTransformerCustomizer, "TransformerCustomizer");
104 
105     try
106     {
107       // prepare all steps
108       if (s_aStep1 == null)
109         s_aStep1 = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
110                                                        new ClassPathResource (XSLT2_STEP1));
111       if (s_aStep2 == null)
112         s_aStep2 = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
113                                                        new ClassPathResource (XSLT2_STEP2));
114       if (s_aStep3 == null)
115         s_aStep3 = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
116                                                        new ClassPathResource (XSLT2_STEP3));
117 
118       // perform step 1 (Schematron -> ResultStep1)
119       final DOMResult aResult1 = new DOMResult ();
120       final Transformer aTransformer1 = s_aStep1.newTransformer ();
121       aTransformerCustomizer.customize (EStep.SCH2XSLT_1, aTransformer1);
122       aTransformer1.transform (TransformSourceFactory.create (aSchematronResource), aResult1);
123 
124       // perform step 2 (ResultStep1 -> ResultStep2)
125       final DOMResult aResult2 = new DOMResult ();
126       final Transformer aTransformer2 = s_aStep2.newTransformer ();
127       aTransformerCustomizer.customize (EStep.SCH2XSLT_2, aTransformer2);
128       aTransformer2.transform (TransformSourceFactory.create (aResult1.getNode ()), aResult2);
129 
130       if (SAVE_INTERMEDIATE_FILES)
131       {
132         final String sXML = XMLWriter.getXMLString (aResult2.getNode ());
133         SimpleFileIO.writeFile (new File ("test-minified",
134                                           FilenameHelper.getWithoutPath (aSchematronResource.getPath ()) +
135                                                            ".min-xslt.sch"),
136                                 sXML,
137                                 XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ);
138       }
139 
140       // perform step 3 (ResultStep2 -> ResultStep3XSL)
141       final DOMResult aResult3 = new DOMResult ();
142       final Transformer aTransformer3 = s_aStep3.newTransformer ();
143       aTransformerCustomizer.customize (EStep.SCH2XSLT_3, aTransformer3);
144       aTransformer3.transform (TransformSourceFactory.create (aResult2.getNode ()), aResult3);
145 
146       // Save the underlying XSLT document....
147       // Note: Saxon 6.5.5 does not allow to clone the document node!!!!
148       m_aSchematronXSLTDoc = (Document) aResult3.getNode ();
149 
150       if (SAVE_INTERMEDIATE_FILES)
151       {
152         final String sXML = XMLWriter.getXMLString (m_aSchematronXSLTDoc);
153         SimpleFileIO.writeFile (new File ("test-final",
154                                           FilenameHelper.getWithoutPath (aSchematronResource.getPath ()) + ".xslt"),
155                                 sXML,
156                                 XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ);
157       }
158 
159       // compile result of step 3
160       m_aSchematronXSLTTemplates = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
161                                                                        TransformSourceFactory.create (m_aSchematronXSLTDoc));
162     }
163     catch (final Throwable t)
164     {
165       s_aLogger.error ("Schematron preprocessor error", t);
166     }
167   }
168 
169   @Nonnull
170   public IReadableResource getSchematronResource ()
171   {
172     return m_aSchematronResource;
173   }
174 
175   public boolean isValidSchematron ()
176   {
177     return m_aSchematronXSLTTemplates != null;
178   }
179 
180   @Nullable
181   public Document getXSLTDocument ()
182   {
183     return m_aSchematronXSLTDoc;
184   }
185 
186   @Nullable
187   public Transformer getXSLTTransformer () throws TransformerConfigurationException
188   {
189     return m_aSchematronXSLTTemplates == null ? null : m_aSchematronXSLTTemplates.newTransformer ();
190   }
191 }