View Javadoc
1   /**
2    * Copyright (C) 2014-2017 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.SchematronDebug;
39  import com.helger.schematron.saxon.SchematronTransformerFactory;
40  import com.helger.schematron.xslt.SCHTransformerCustomizer.EStep;
41  import com.helger.xml.serialize.write.XMLWriter;
42  import com.helger.xml.serialize.write.XMLWriterSettings;
43  import com.helger.xml.transform.TransformSourceFactory;
44  import com.helger.xml.transform.XMLTransformerFactory;
45  
46  /**
47   * The XSLT preprocessor used to convert a Schematron XML document into an XSLT
48   * document. This implementation uses JAXP with Saxon to be used as the
49   * respective parser.
50   *
51   * @author Philip Helger
52   */
53  @NotThreadSafe
54  public class SchematronProviderXSLTFromSCH implements ISchematronXSLTBasedProvider
55  {
56    private static final Logger s_aLogger = LoggerFactory.getLogger (SchematronProviderXSLTFromSCH.class);
57  
58    /**
59     * The classpath directory where the Schematron 2 XSLT files reside.
60     */
61    public static final String SCHEMATRON_DIRECTORY_XSLT2 = "schematron/20100710-xslt2/";
62  
63    /**
64     * The class path to first XSLT to be applied.
65     */
66    public static final String XSLT2_STEP1 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_dsdl_include.xsl";
67  
68    /**
69     * The class path to second XSLT to be applied.
70     */
71    public static final String XSLT2_STEP2 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_abstract_expand.xsl";
72  
73    /**
74     * The class path to third and last XSLT to be applied.
75     */
76    public static final String XSLT2_STEP3 = SCHEMATRON_DIRECTORY_XSLT2 + "iso_svrl_for_xslt2.xsl";
77  
78    private static volatile Templates s_aStep1;
79    private static volatile Templates s_aStep2;
80    private static volatile Templates s_aStep3;
81  
82    private final IReadableResource m_aSchematronResource;
83    private Document m_aSchematronXSLTDoc;
84    private Templates m_aSchematronXSLTTemplates;
85  
86    @Deprecated
87    public static final boolean isSaveIntermediateFiles ()
88    {
89      return SchematronDebug.isSaveIntermediateXSLTFiles ();
90    }
91  
92    @Deprecated
93    public static final void setSaveIntermediateFiles (final boolean bSaveIntermediateFiles)
94    {
95      SchematronDebug.setSaveIntermediateXSLTFiles (bSaveIntermediateFiles);
96    }
97  
98    @Nonnull
99    @Deprecated
100   public static final File getIntermediateMinifiedSCHFolder ()
101   {
102     return SchematronDebug.getIntermediateMinifiedSCHFolder ();
103   }
104 
105   @Deprecated
106   public static final void setIntermediateMinifiedSCHFolder (@Nonnull final File aIntermediateMinifiedSCHFolder)
107   {
108     SchematronDebug.setIntermediateMinifiedSCHFolder (aIntermediateMinifiedSCHFolder);
109   }
110 
111   @Nonnull
112   @Deprecated
113   public static final File getIntermediateFinalXSLTFolder ()
114   {
115     return SchematronDebug.getIntermediateFinalXSLTFolder ();
116   }
117 
118   @Deprecated
119   public static final void setIntermediateFinalXSLTFolder (@Nonnull final File aIntermediateFinalXSLTFolder)
120   {
121     SchematronDebug.setIntermediateFinalXSLTFolder (aIntermediateFinalXSLTFolder);
122   }
123 
124   public static void cacheXSLTTemplates ()
125   {
126     // prepare all steps
127     if (s_aStep1 == null)
128     {
129       if (s_aLogger.isDebugEnabled ())
130         s_aLogger.debug ("Creating XSLT step 1 template");
131       s_aStep1 = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
132                                                      new ClassPathResource (XSLT2_STEP1));
133     }
134     if (s_aStep2 == null)
135     {
136       if (s_aLogger.isDebugEnabled ())
137         s_aLogger.debug ("Creating XSLT step 2 template");
138       s_aStep2 = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
139                                                      new ClassPathResource (XSLT2_STEP2));
140     }
141     if (s_aStep3 == null)
142     {
143       if (s_aLogger.isDebugEnabled ())
144         s_aLogger.debug ("Creating XSLT step 3 template");
145       s_aStep3 = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
146                                                      new ClassPathResource (XSLT2_STEP3));
147     }
148   }
149 
150   /**
151    * Constructor
152    *
153    * @param aSchematronResource
154    *        SCH resource
155    * @param aTransformerCustomizer
156    *        The customizer for XSLT {@link Transformer} objects. May not be
157    *        <code>null</code>.
158    */
159   public SchematronProviderXSLTFromSCH (@Nonnull final IReadableResource aSchematronResource,
160                                         @Nonnull final SCHTransformerCustomizer aTransformerCustomizer)
161   {
162     m_aSchematronResource = ValueEnforcer.notNull (aSchematronResource, "SchematronResource");
163     ValueEnforcer.notNull (aTransformerCustomizer, "TransformerCustomizer");
164 
165     try
166     {
167       cacheXSLTTemplates ();
168 
169       // perform step 1 (Schematron -> ResultStep1)
170       final DOMResult aResult1 = new DOMResult ();
171       final Transformer aTransformer1 = s_aStep1.newTransformer ();
172       aTransformerCustomizer.customize (EStep.SCH2XSLT_1, aTransformer1);
173       aTransformer1.transform (TransformSourceFactory.create (aSchematronResource), aResult1);
174 
175       if (s_aLogger.isDebugEnabled ())
176         s_aLogger.debug ("Finished applying XSLT step 1 on " + aSchematronResource);
177 
178       // perform step 2 (ResultStep1 -> ResultStep2)
179       final DOMResult aResult2 = new DOMResult ();
180       final Transformer aTransformer2 = s_aStep2.newTransformer ();
181       aTransformerCustomizer.customize (EStep.SCH2XSLT_2, aTransformer2);
182       aTransformer2.transform (TransformSourceFactory.create (aResult1.getNode ()), aResult2);
183 
184       if (s_aLogger.isDebugEnabled ())
185         s_aLogger.debug ("Finished applying XSLT step 2 on " + aSchematronResource);
186 
187       if (SchematronDebug.isSaveIntermediateXSLTFiles ())
188       {
189         final String sXML = XMLWriter.getNodeAsString (aResult2.getNode ());
190         SimpleFileIO.writeFile (new File (SchematronDebug.getIntermediateMinifiedSCHFolder (),
191                                           FilenameHelper.getWithoutPath (aSchematronResource.getPath ()) +
192                                                                                                ".min-xslt.sch"),
193                                 sXML,
194                                 XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ);
195       }
196 
197       // perform step 3 (ResultStep2 -> ResultStep3XSL)
198       final DOMResult aResult3 = new DOMResult ();
199       final Transformer aTransformer3 = s_aStep3.newTransformer ();
200       aTransformerCustomizer.customize (EStep.SCH2XSLT_3, aTransformer3);
201       aTransformer3.transform (TransformSourceFactory.create (aResult2.getNode ()), aResult3);
202 
203       if (s_aLogger.isDebugEnabled ())
204         s_aLogger.debug ("Finished applying XSLT step 3 on " + aSchematronResource);
205 
206       // Save the underlying XSLT document....
207       // Note: Saxon 6.5.5 does not allow to clone the document node!!!!
208       m_aSchematronXSLTDoc = (Document) aResult3.getNode ();
209 
210       if (SchematronDebug.isSaveIntermediateXSLTFiles ())
211       {
212         final String sXML = XMLWriter.getNodeAsString (m_aSchematronXSLTDoc);
213         SimpleFileIO.writeFile (new File (SchematronDebug.getIntermediateFinalXSLTFolder (),
214                                           FilenameHelper.getWithoutPath (aSchematronResource.getPath ()) + ".xslt"),
215                                 sXML,
216                                 XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ);
217       }
218 
219       // compile result of step 3
220       m_aSchematronXSLTTemplates = XMLTransformerFactory.newTemplates (SchematronTransformerFactory.getDefaultSaxonFirst (),
221                                                                        TransformSourceFactory.create (m_aSchematronXSLTDoc));
222     }
223     catch (final Throwable t)
224     {
225       s_aLogger.error ("Schematron preprocessor error", t);
226     }
227   }
228 
229   @Nonnull
230   public IReadableResource getSchematronResource ()
231   {
232     return m_aSchematronResource;
233   }
234 
235   public boolean isValidSchematron ()
236   {
237     return m_aSchematronXSLTTemplates != null;
238   }
239 
240   @Nullable
241   public Document getXSLTDocument ()
242   {
243     return m_aSchematronXSLTDoc;
244   }
245 
246   @Nullable
247   public Transformer getXSLTTransformer () throws TransformerConfigurationException
248   {
249     return m_aSchematronXSLTTemplates == null ? null : m_aSchematronXSLTTemplates.newTransformer ();
250   }
251 }