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