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;
18  
19  import java.io.InputStream;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.annotation.concurrent.NotThreadSafe;
24  import javax.xml.transform.Source;
25  import javax.xml.transform.stream.StreamSource;
26  
27  import org.oclc.purl.dsdl.svrl.SchematronOutputType;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  import org.w3c.dom.Document;
31  import org.w3c.dom.Node;
32  import org.xml.sax.EntityResolver;
33  
34  import com.helger.commons.ValueEnforcer;
35  import com.helger.commons.annotation.ReturnsMutableCopy;
36  import com.helger.commons.io.IHasInputStream;
37  import com.helger.commons.io.resource.IReadableResource;
38  import com.helger.commons.state.EValidity;
39  import com.helger.commons.string.ToStringGenerator;
40  import com.helger.xml.EXMLParserFeature;
41  import com.helger.xml.sax.DefaultEntityResolver;
42  import com.helger.xml.serialize.read.DOMReader;
43  import com.helger.xml.serialize.read.DOMReaderSettings;
44  import com.helger.xml.transform.TransformSourceFactory;
45  
46  /**
47   * Abstract implementation of the {@link ISchematronResource} interface handling
48   * the underlying resource and wrapping one method.
49   *
50   * @author Philip Helger
51   */
52  @NotThreadSafe
53  public abstract class AbstractSchematronResource implements ISchematronResource
54  {
55    private static final Logger s_aLogger = LoggerFactory.getLogger (AbstractSchematronResource.class);
56  
57    private final IReadableResource m_aResource;
58    private final String m_sResourceID;
59    private boolean m_bUseCache = true;
60    private EntityResolver m_aEntityResolver;
61  
62    /**
63     * Constructor
64     *
65     * @param aResource
66     *        The Schematron resource. May not be <code>null</code>.
67     */
68    public AbstractSchematronResource (@Nonnull final IReadableResource aResource)
69    {
70      m_aResource = ValueEnforcer.notNull (aResource, "Resource");
71      m_sResourceID = aResource.getResourceID ();
72      // Set a default entity resolver
73      m_aEntityResolver = DefaultEntityResolver.createOnDemand (aResource);
74    }
75  
76    @Nonnull
77    public final String getID ()
78    {
79      return m_sResourceID;
80    }
81  
82    @Nonnull
83    public final IReadableResource getResource ()
84    {
85      return m_aResource;
86    }
87  
88    public boolean isUseCache ()
89    {
90      return m_bUseCache;
91    }
92  
93    public void setUseCache (final boolean bUseCache)
94    {
95      m_bUseCache = bUseCache;
96    }
97  
98    /**
99     * @return The XML entity resolver to be used to read the Schematron or XML to
100    *         be validated. May be <code>null</code>.
101    * @since 4.1.1
102    */
103   @Nullable
104   public EntityResolver getEntityResolver ()
105   {
106     return m_aEntityResolver;
107   }
108 
109   /**
110    * Set the XML entity resolver to be used when reading the Schematron or the
111    * XML to be validated. This can only be set before the Schematron is bound.
112    * If it is already bound an exception is thrown to indicate the unnecessity
113    * of the call.
114    *
115    * @param aEntityResolver
116    *        The entity resolver to set. May be <code>null</code>.
117    * @since 4.2.3
118    */
119   protected final void internalSetEntityResolver (@Nullable final EntityResolver aEntityResolver)
120   {
121     m_aEntityResolver = aEntityResolver;
122   }
123 
124   /**
125    * @return The {@link DOMReaderSettings} to be used for reading the XML files
126    *         to be validated. This includes the {@link EntityResolver} to be
127    *         used.
128    * @see #getEntityResolver()
129    */
130   @Nonnull
131   @ReturnsMutableCopy
132   protected DOMReaderSettings internalCreateDOMReaderSettings ()
133   {
134     final DOMReaderSettings aDRS = new DOMReaderSettings ();
135     if (m_aEntityResolver != null)
136       aDRS.setEntityResolver (m_aEntityResolver);
137     if (false)
138     {
139       final boolean m_bLoadExternalSchemas = false;
140       aDRS.setFeatureValue (EXMLParserFeature.EXTERNAL_GENERAL_ENTITIES, m_bLoadExternalSchemas);
141       aDRS.setFeatureValue (EXMLParserFeature.EXTERNAL_PARAMETER_ENTITIES, m_bLoadExternalSchemas);
142       aDRS.setFeatureValue (EXMLParserFeature.LOAD_EXTERNAL_DTD, m_bLoadExternalSchemas);
143       aDRS.setFeatureValue (EXMLParserFeature.VALIDATION, true);
144       aDRS.setFeatureValue (EXMLParserFeature.NAMESPACES, true);
145     }
146     return aDRS;
147   }
148 
149   @Nullable
150   protected Node getAsNode (@Nonnull final IHasInputStream aXMLResource) throws Exception
151   {
152     final StreamSource aStreamSrc = TransformSourceFactory.create (aXMLResource);
153     InputStream aIS = null;
154     try
155     {
156       aIS = aStreamSrc.getInputStream ();
157     }
158     catch (final IllegalStateException ex)
159     {
160       // Fall through
161       // Happens e.g. for ResourceStreamSource with non-existing resources
162     }
163     if (aIS == null)
164     {
165       // Resource not found
166       s_aLogger.warn ("XML resource " + aXMLResource + " does not exist!");
167       return null;
168     }
169     final Document aDoc = DOMReader.readXMLDOM (aIS, internalCreateDOMReaderSettings ());
170     if (aDoc == null)
171       throw new IllegalArgumentException ("Failed to read resource " + aXMLResource + " as XML");
172 
173     s_aLogger.info ("Read XML resource " + aXMLResource);
174     return aDoc;
175   }
176 
177   @Nullable
178   protected Node getAsNode (@Nonnull final Source aXMLSource) throws Exception
179   {
180     // Convert to Node
181     final Node aNode = SchematronResourceHelper.getNodeOfSource (aXMLSource, internalCreateDOMReaderSettings ());
182     if (aNode == null)
183       return null;
184     return aNode;
185   }
186 
187   @Nonnull
188   public EValidity getSchematronValidity (@Nonnull final IHasInputStream aXMLResource) throws Exception
189   {
190     if (!isValidSchematron ())
191       return EValidity.INVALID;
192 
193     final Node aXMLNode = getAsNode (aXMLResource);
194     if (aXMLNode == null)
195       return EValidity.INVALID;
196 
197     return getSchematronValidity (aXMLNode);
198   }
199 
200   @Nonnull
201   public EValidity getSchematronValidity (@Nonnull final Source aXMLSource) throws Exception
202   {
203     if (!isValidSchematron ())
204       return EValidity.INVALID;
205 
206     final Node aXMLNode = getAsNode (aXMLSource);
207     if (aXMLNode == null)
208       return EValidity.INVALID;
209 
210     return getSchematronValidity (aXMLNode);
211   }
212 
213   @Nullable
214   public Document applySchematronValidation (@Nonnull final IHasInputStream aXMLResource) throws Exception
215   {
216     if (!isValidSchematron ())
217       return null;
218 
219     final Node aXMLNode = getAsNode (aXMLResource);
220     if (aXMLNode == null)
221       return null;
222 
223     return applySchematronValidation (aXMLNode);
224   }
225 
226   @Nullable
227   public Document applySchematronValidation (@Nonnull final Source aXMLSource) throws Exception
228   {
229     if (!isValidSchematron ())
230       return null;
231 
232     final Node aXMLNode = getAsNode (aXMLSource);
233     if (aXMLNode == null)
234       return null;
235 
236     return applySchematronValidation (aXMLNode);
237   }
238 
239   @Nullable
240   public SchematronOutputType applySchematronValidationToSVRL (@Nonnull final IHasInputStream aXMLResource) throws Exception
241   {
242     if (!isValidSchematron ())
243       return null;
244 
245     final Node aXMLNode = getAsNode (aXMLResource);
246     if (aXMLNode == null)
247       return null;
248 
249     return applySchematronValidationToSVRL (aXMLNode);
250   }
251 
252   @Nullable
253   public SchematronOutputType applySchematronValidationToSVRL (@Nonnull final Source aXMLSource) throws Exception
254   {
255     if (!isValidSchematron ())
256       return null;
257 
258     final Node aXMLNode = getAsNode (aXMLSource);
259     if (aXMLNode == null)
260       return null;
261 
262     return applySchematronValidationToSVRL (aXMLNode);
263   }
264 
265   @Override
266   public String toString ()
267   {
268     return new ToStringGenerator (this).append ("Resource", m_aResource)
269                                        .append ("UseCache", m_bUseCache)
270                                        .appendIfNotNull ("EntityResolver", m_aEntityResolver)
271                                        .getToString ();
272   }
273 }