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