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.InputStream;
20  import java.util.Locale;
21  import java.util.Map;
22  
23  import javax.annotation.Nonnull;
24  import javax.annotation.Nullable;
25  import javax.annotation.concurrent.NotThreadSafe;
26  import javax.xml.transform.ErrorListener;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.Transformer;
29  import javax.xml.transform.TransformerException;
30  import javax.xml.transform.URIResolver;
31  import javax.xml.transform.dom.DOMResult;
32  
33  import org.oclc.purl.dsdl.svrl.SchematronOutputType;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Node;
38  
39  import com.helger.commons.ValueEnforcer;
40  import com.helger.commons.annotation.ReturnsMutableCopy;
41  import com.helger.commons.collection.ext.CommonsLinkedHashMap;
42  import com.helger.commons.collection.ext.ICommonsOrderedMap;
43  import com.helger.commons.io.IHasInputStream;
44  import com.helger.commons.io.resource.IReadableResource;
45  import com.helger.commons.io.stream.StreamHelper;
46  import com.helger.commons.state.EValidity;
47  import com.helger.commons.string.ToStringGenerator;
48  import com.helger.commons.traits.IGenericImplTrait;
49  import com.helger.schematron.AbstractSchematronResource;
50  import com.helger.schematron.svrl.SVRLReader;
51  import com.helger.schematron.xslt.validator.ISchematronXSLTValidator;
52  import com.helger.schematron.xslt.validator.SchematronXSLTValidatorDefault;
53  import com.helger.xml.XMLFactory;
54  import com.helger.xml.serialize.write.XMLWriter;
55  import com.helger.xml.transform.LoggingTransformErrorListener;
56  import com.helger.xml.transform.TransformSourceFactory;
57  
58  /**
59   * Abstract implementation of a Schematron resource that is based on XSLT
60   * transformations.
61   *
62   * @author Philip Helger
63   * @param <IMPLTYPE>
64   *        Implementation type
65   */
66  @NotThreadSafe
67  public abstract class AbstractSchematronXSLTBasedResource <IMPLTYPE extends AbstractSchematronXSLTBasedResource <IMPLTYPE>>
68                                                            extends AbstractSchematronResource
69                                                            implements IGenericImplTrait <IMPLTYPE>
70  {
71    private static final Logger s_aLogger = LoggerFactory.getLogger (AbstractSchematronXSLTBasedResource.class);
72  
73    protected ErrorListener m_aCustomErrorListener;
74    protected URIResolver m_aCustomURIResolver;
75    protected ICommonsOrderedMap <String, ?> m_aCustomParameters;
76    private ISchematronXSLTValidator m_aXSLTValidator = new SchematronXSLTValidatorDefault ();
77  
78    public AbstractSchematronXSLTBasedResource (@Nonnull final IReadableResource aSCHResource)
79    {
80      super (aSCHResource);
81    }
82  
83    @Nullable
84    public ErrorListener getErrorListener ()
85    {
86      return m_aCustomErrorListener;
87    }
88  
89    @Nonnull
90    public IMPLTYPE setErrorListener (@Nullable final ErrorListener aCustomErrorListener)
91    {
92      m_aCustomErrorListener = aCustomErrorListener;
93      return thisAsT ();
94    }
95  
96    @Nullable
97    public URIResolver getURIResolver ()
98    {
99      return m_aCustomURIResolver;
100   }
101 
102   @Nonnull
103   public IMPLTYPE setURIResolver (@Nullable final URIResolver aCustomURIResolver)
104   {
105     m_aCustomURIResolver = aCustomURIResolver;
106     return thisAsT ();
107   }
108 
109   public boolean hasParameters ()
110   {
111     return m_aCustomParameters != null && m_aCustomParameters.isNotEmpty ();
112   }
113 
114   @Nonnull
115   @ReturnsMutableCopy
116   public ICommonsOrderedMap <String, ?> getParameters ()
117   {
118     return new CommonsLinkedHashMap <> (m_aCustomParameters);
119   }
120 
121   @Nonnull
122   public IMPLTYPE setParameters (@Nullable final Map <String, ?> aCustomParameters)
123   {
124     m_aCustomParameters = new CommonsLinkedHashMap <> (aCustomParameters);
125     return thisAsT ();
126   }
127 
128   /**
129    * @return The XSLT provider passed in the constructor. May be
130    *         <code>null</code>.
131    */
132   @Nullable
133   public abstract ISchematronXSLTBasedProvider getXSLTProvider ();
134 
135   /**
136    * @return The XSLT validator to be used. Never <code>null</code>.
137    */
138   @Nonnull
139   public ISchematronXSLTValidator getXSLTValidator ()
140   {
141     return m_aXSLTValidator;
142   }
143 
144   @Nonnull
145   public IMPLTYPE setXSLTValidator (@Nonnull final ISchematronXSLTValidator aXSLTValidator)
146   {
147     ValueEnforcer.notNull (aXSLTValidator, "XSLTValidator");
148     m_aXSLTValidator = aXSLTValidator;
149     return thisAsT ();
150   }
151 
152   public final boolean isValidSchematron ()
153   {
154     final ISchematronXSLTBasedProvider aXSLTProvider = getXSLTProvider ();
155     return aXSLTProvider != null && aXSLTProvider.isValidSchematron ();
156   }
157 
158   @Nonnull
159   public EValidity getSchematronValidity (@Nonnull final IHasInputStream aXMLResource) throws Exception
160   {
161     ValueEnforcer.notNull (aXMLResource, "XMLResource");
162 
163     final InputStream aIS = aXMLResource.getInputStream ();
164     if (aIS == null)
165     {
166       // Resource not found
167       s_aLogger.warn ("XML resource " + aXMLResource + " does not exist!");
168       return EValidity.INVALID;
169     }
170 
171     try
172     {
173       // InputStream to Source
174       return getSchematronValidity (TransformSourceFactory.create (aIS));
175     }
176     finally
177     {
178       // Ensure InputStream is closed
179       StreamHelper.close (aIS);
180     }
181   }
182 
183   @Nonnull
184   public EValidity getSchematronValidity (@Nonnull final Source aXMLSource) throws Exception
185   {
186     // We don't have a short circuit here - apply the full validation
187     final SchematronOutputType aSO = applySchematronValidationToSVRL (aXMLSource);
188     if (aSO == null)
189       return EValidity.INVALID;
190 
191     // And now filter all elements that make the passed source invalid
192     return m_aXSLTValidator.getSchematronValidity (aSO);
193   }
194 
195   @Nullable
196   public Document applySchematronValidation (@Nonnull final IHasInputStream aXMLResource) throws Exception
197   {
198     ValueEnforcer.notNull (aXMLResource, "XMLResource");
199 
200     final InputStream aIS = aXMLResource.getInputStream ();
201     if (aIS == null)
202     {
203       // Resource not found
204       s_aLogger.warn ("XML resource " + aXMLResource + " does not exist!");
205       return null;
206     }
207 
208     try
209     {
210       return applySchematronValidation (TransformSourceFactory.create (aIS));
211     }
212     finally
213     {
214       StreamHelper.close (aIS);
215     }
216   }
217 
218   @Nullable
219   public Document applySchematronValidation (@Nonnull final Node aXMLResource) throws Exception
220   {
221     ValueEnforcer.notNull (aXMLResource, "XMLResource");
222 
223     return applySchematronValidation (TransformSourceFactory.create (aXMLResource));
224   }
225 
226   @Nullable
227   public final Document applySchematronValidation (@Nonnull final Source aXMLSource) throws TransformerException
228   {
229     ValueEnforcer.notNull (aXMLSource, "XMLSource");
230 
231     final ISchematronXSLTBasedProvider aXSLTProvider = getXSLTProvider ();
232     if (aXSLTProvider == null || !aXSLTProvider.isValidSchematron ())
233     {
234       // We cannot progress because of invalid Schematron
235       return null;
236     }
237 
238     // Create result document
239     final Document ret = XMLFactory.newDocument ();
240 
241     // Create the transformer object from the templates specified in the
242     // constructor
243     final Transformer aTransformer = aXSLTProvider.getXSLTTransformer ();
244 
245     // Apply customizations
246     // Ensure an error listener is present
247     if (m_aCustomErrorListener != null)
248       aTransformer.setErrorListener (m_aCustomErrorListener);
249     else
250       aTransformer.setErrorListener (new LoggingTransformErrorListener (Locale.US));
251 
252     // Set the optional URI Resolver
253     if (m_aCustomURIResolver != null)
254       aTransformer.setURIResolver (m_aCustomURIResolver);
255 
256     // Set all custom parameters
257     if (m_aCustomParameters != null)
258       for (final Map.Entry <String, ?> aEntry : m_aCustomParameters.entrySet ())
259         aTransformer.setParameter (aEntry.getKey (), aEntry.getValue ());
260 
261     // Debug print the created XSLT document
262     if (false)
263       System.out.println (XMLWriter.getXMLString (aXSLTProvider.getXSLTDocument ()));
264 
265     // Do the main transformation
266     aTransformer.transform (aXMLSource, new DOMResult (ret));
267 
268     // Debug print the created SVRL document
269     if (false)
270       System.out.println (XMLWriter.getXMLString (ret));
271 
272     return ret;
273   }
274 
275   @Nullable
276   public SchematronOutputType applySchematronValidationToSVRL (@Nonnull final IHasInputStream aXMLResource) throws Exception
277   {
278     final Document aDoc = applySchematronValidation (aXMLResource);
279     return aDoc == null ? null : SVRLReader.readXML (aDoc);
280   }
281 
282   @Nullable
283   public SchematronOutputType applySchematronValidationToSVRL (@Nonnull final Source aXMLSource) throws Exception
284   {
285     final Document aDoc = applySchematronValidation (aXMLSource);
286     return aDoc == null ? null : SVRLReader.readXML (aDoc);
287   }
288 
289   @Nullable
290   public SchematronOutputType applySchematronValidationToSVRL (@Nonnull final Node aXMLSource) throws Exception
291   {
292     final Document aDoc = applySchematronValidation (aXMLSource);
293     return aDoc == null ? null : SVRLReader.readXML (aDoc);
294   }
295 
296   @Override
297   public String toString ()
298   {
299     return ToStringGenerator.getDerived (super.toString ()).append ("XSLTValidator", m_aXSLTValidator).toString ();
300   }
301 }