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.pure.bound;
18  
19  import javax.annotation.Nonnull;
20  import javax.annotation.Nullable;
21  import javax.annotation.concurrent.Immutable;
22  import javax.xml.xpath.XPathFunctionResolver;
23  import javax.xml.xpath.XPathVariableResolver;
24  
25  import com.helger.commons.ValueEnforcer;
26  import com.helger.commons.annotation.OverrideOnDemand;
27  import com.helger.commons.equals.EqualsHelper;
28  import com.helger.commons.hashcode.HashCodeGenerator;
29  import com.helger.commons.hashcode.IHashCodeGenerator;
30  import com.helger.commons.io.resource.IReadableResource;
31  import com.helger.commons.string.ToStringGenerator;
32  import com.helger.schematron.SchematronException;
33  import com.helger.schematron.pure.binding.IPSQueryBinding;
34  import com.helger.schematron.pure.binding.PSQueryBindingRegistry;
35  import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
36  import com.helger.schematron.pure.exchange.PSReader;
37  import com.helger.schematron.pure.model.PSSchema;
38  import com.helger.schematron.pure.preprocess.PSPreprocessor;
39  import com.helger.schematron.pure.preprocess.SchematronPreprocessException;
40  
41  /**
42   * This class represents keys for the {@link PSBoundSchemaCache}. It is a
43   * combination of a resource and a phase. It is the responsible class for
44   * reading and binding a Schematron resource.
45   *
46   * @author Philip Helger
47   */
48  @Immutable
49  public class PSBoundSchemaCacheKey
50  {
51    private final IReadableResource m_aResource;
52    private final String m_sPhase;
53    private final IPSErrorHandler m_aErrorHandler;
54    private final XPathVariableResolver m_aVariableResolver;
55    private final XPathFunctionResolver m_aFunctionResolver;
56    // Status vars
57    private transient int m_nHashCode = IHashCodeGenerator.ILLEGAL_HASHCODE;
58  
59    public PSBoundSchemaCacheKey (@Nonnull final IReadableResource aResource,
60                                  @Nullable final String sPhase,
61                                  @Nullable final IPSErrorHandler aErrorHandler,
62                                  @Nullable final XPathVariableResolver aVariableResolver,
63                                  @Nullable final XPathFunctionResolver aFunctionResolver)
64    {
65      ValueEnforcer.notNull (aResource, "Resource");
66  
67      m_aResource = aResource;
68      m_sPhase = sPhase;
69      m_aErrorHandler = aErrorHandler;
70      m_aVariableResolver = aVariableResolver;
71      m_aFunctionResolver = aFunctionResolver;
72    }
73  
74    /**
75     * @return The resource passed in the constructor. Never <code>null</code>.
76     */
77    @Nonnull
78    public final IReadableResource getResource ()
79    {
80      return m_aResource;
81    }
82  
83    /**
84     * @return The phase selected in the constructor. May be <code>null</code>.
85     */
86    @Nullable
87    public final String getPhase ()
88    {
89      return m_sPhase;
90    }
91  
92    /**
93     * @return The error handler passed in the constructor. May be
94     *         <code>null</code>.
95     */
96    @Nullable
97    public final IPSErrorHandler getErrorHandler ()
98    {
99      return m_aErrorHandler;
100   }
101 
102   /**
103    * @return The variable resolver to be used. May be <code>null</code>.
104    */
105   @Nullable
106   public final XPathVariableResolver getVariableResolver ()
107   {
108     return m_aVariableResolver;
109   }
110 
111   /**
112    * @return The function resolver to be used. May be <code>null</code>.
113    */
114   @Nullable
115   public final XPathFunctionResolver getFunctionResolver ()
116   {
117     return m_aFunctionResolver;
118   }
119 
120   /**
121    * Read the specified schema from the passed resource.
122    *
123    * @param aResource
124    *        The resource to read from. Never <code>null</code>.
125    * @param aErrorHandler
126    *        The error handler to use. May be <code>null</code>.
127    * @return The read schema. May not be <code>null</code>.
128    * @throws SchematronException
129    *         In case there is an error reading.
130    */
131   @Nonnull
132   @OverrideOnDemand
133   public PSSchema readSchema (@Nonnull final IReadableResource aResource,
134                               @Nullable final IPSErrorHandler aErrorHandler) throws SchematronException
135   {
136     return new PSReader (aResource, aErrorHandler).readSchema ();
137   }
138 
139   /**
140    * Determine the query binding for the read schema.
141    *
142    * @param aSchema
143    *        The read schema. Never <code>null</code>.
144    * @return The query binding to use. Never <code>null</code>.
145    * @throws SchematronException
146    *         In case the determination fails.
147    */
148   @Nonnull
149   @OverrideOnDemand
150   public IPSQueryBinding getQueryBinding (@Nonnull final PSSchema aSchema) throws SchematronException
151   {
152     return PSQueryBindingRegistry.getQueryBindingOfNameOrThrow (aSchema.getQueryBinding ());
153   }
154 
155   /**
156    * Create the pre-processor to be used for
157    * {@link #createPreprocessedSchema(PSSchema, IPSQueryBinding)}.
158    *
159    * @param aQueryBinding
160    *        The query binding to be determined from the read schema. Never
161    *        <code>null</code>.
162    * @return The pre-processor to be used.
163    */
164   @Nonnull
165   @OverrideOnDemand
166   public PSPreprocessor createPreprocessor (@Nonnull final IPSQueryBinding aQueryBinding)
167   {
168     final PSPreprocessor aPreprocessor = PSPreprocessor.createPreprocessorWithoutInformationLoss (aQueryBinding);
169     return aPreprocessor;
170   }
171 
172   /**
173    * Pre-process the read schema, using the determined query binding.
174    *
175    * @param aSchema
176    *        The read schema. Never <code>null</code>.
177    * @param aQueryBinding
178    *        The determined query binding. Never <code>null</code>.
179    * @return The pre-processed schema and never <code>null</code>.
180    * @throws SchematronException
181    *         In case pre-processing fails
182    */
183   @Nonnull
184   @OverrideOnDemand
185   public PSSchema createPreprocessedSchema (@Nonnull final PSSchema aSchema,
186                                             @Nonnull final IPSQueryBinding aQueryBinding) throws SchematronException
187   {
188     final PSPreprocessor aPreprocessor = createPreprocessor (aQueryBinding);
189     final PSSchema aPreprocessedSchema = aPreprocessor.getAsPreprocessedSchema (aSchema);
190     if (aPreprocessedSchema == null)
191       throw new SchematronPreprocessException ("Failed to preprocess schema " +
192                                                aSchema +
193                                                " with query binding " +
194                                                aQueryBinding);
195     return aPreprocessedSchema;
196   }
197 
198   /**
199    * The main routine to create a bound schema from the passed resource and
200    * phase. The usual routine is to
201    * <ol>
202    * <li>read the schema from the resource - see
203    * {@link #readSchema(IReadableResource, IPSErrorHandler)}</li>
204    * <li>resolve the query binding - see {@link #getQueryBinding(PSSchema)}</li>
205    * <li>pre-process the schema -
206    * {@link #createPreprocessedSchema(PSSchema, IPSQueryBinding)}</li>
207    * <li>and finally bind it -
208    * {@link IPSQueryBinding#bind(PSSchema, String, IPSErrorHandler, javax.xml.xpath.XPathVariableResolver, javax.xml.xpath.XPathFunctionResolver)}
209    * </li>
210    * </ol>
211    *
212    * @return The bound schema. Never <code>null</code>.
213    * @throws SchematronException
214    *         In case reading or binding fails.
215    */
216   @Nonnull
217   public IPSBoundSchema createBoundSchema () throws SchematronException
218   {
219     // Read schema from resource
220     final PSSchema aSchema = readSchema (getResource (), getErrorHandler ());
221 
222     // Resolve the query binding to be used
223     final IPSQueryBinding aQueryBinding = getQueryBinding (aSchema);
224 
225     // Pre-process schema
226     final PSSchema aPreprocessedSchema = createPreprocessedSchema (aSchema, aQueryBinding);
227 
228     // And finally bind the pre-processed schema
229     return aQueryBinding.bind (aPreprocessedSchema,
230                                getPhase (),
231                                getErrorHandler (),
232                                getVariableResolver (),
233                                getFunctionResolver ());
234   }
235 
236   @Override
237   public boolean equals (final Object o)
238   {
239     if (o == this)
240       return true;
241     if (o == null || !getClass ().equals (o.getClass ()))
242       return false;
243     final PSBoundSchemaCacheKey rhs = (PSBoundSchemaCacheKey) o;
244     return m_aResource.equals (rhs.m_aResource) &&
245            EqualsHelper.equals (m_sPhase, rhs.m_sPhase) &&
246            EqualsHelper.equals (m_aVariableResolver, rhs.m_aVariableResolver) &&
247            EqualsHelper.equals (m_aFunctionResolver, rhs.m_aFunctionResolver);
248   }
249 
250   @Override
251   public int hashCode ()
252   {
253     int ret = m_nHashCode;
254     if (ret == IHashCodeGenerator.ILLEGAL_HASHCODE)
255       ret = m_nHashCode = new HashCodeGenerator (this).append (m_aResource)
256                                                       .append (m_sPhase)
257                                                       .append (m_aVariableResolver)
258                                                       .append (m_aFunctionResolver)
259                                                       .getHashCode ();
260     return ret;
261   }
262 
263   @Override
264   public String toString ()
265   {
266     return new ToStringGenerator (this).append ("resource", m_aResource)
267                                        .append ("phase", m_sPhase)
268                                        .appendIfNotNull ("errorHandler", m_aErrorHandler)
269                                        .appendIfNotNull ("variableResolver", m_aVariableResolver)
270                                        .appendIfNotNull ("functionResolver", m_aFunctionResolver)
271                                        .toString ();
272   }
273 }