View Javadoc
1   /**
2    * Copyright (C) 2014-2018 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.OverridingMethodsMustInvokeSuper;
22  
23  import org.oclc.purl.dsdl.svrl.SchematronOutputType;
24  import org.w3c.dom.Node;
25  
26  import com.helger.commons.ValueEnforcer;
27  import com.helger.commons.annotation.OverrideOnDemand;
28  import com.helger.commons.annotation.ReturnsMutableCopy;
29  import com.helger.commons.collection.impl.CommonsArrayList;
30  import com.helger.commons.collection.impl.ICommonsList;
31  import com.helger.commons.state.EValidity;
32  import com.helger.commons.string.ToStringGenerator;
33  import com.helger.schematron.CSchematron;
34  import com.helger.schematron.pure.binding.IPSQueryBinding;
35  import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
36  import com.helger.schematron.pure.errorhandler.LoggingPSErrorHandler;
37  import com.helger.schematron.pure.model.IPSElement;
38  import com.helger.schematron.pure.model.PSActive;
39  import com.helger.schematron.pure.model.PSPattern;
40  import com.helger.schematron.pure.model.PSPhase;
41  import com.helger.schematron.pure.model.PSSchema;
42  import com.helger.schematron.pure.validation.IPSPartialValidationHandler;
43  import com.helger.schematron.pure.validation.PSValidationHandlerBreakOnFirstError;
44  import com.helger.schematron.pure.validation.SchematronValidationException;
45  import com.helger.schematron.pure.validation.xpath.PSXPathValidationHandlerSVRL;
46  import com.helger.xml.namespace.MapBasedNamespaceContext;
47  
48  /**
49   * Base implementation of {@link IPSBoundSchema} with all common elements. It is
50   * independent of the used query binding.
51   *
52   * @author Philip Helger
53   */
54  public abstract class AbstractPSBoundSchema implements IPSBoundSchema
55  {
56    private final IPSQueryBinding m_aQueryBinding;
57    private final PSSchema m_aOrigSchema;
58    private final IPSErrorHandler m_aErrorHandler;
59    private final boolean m_bDefaultErrorHandler;
60    private final MapBasedNamespaceContext m_aNamespaceContext;
61    private final String m_sPhase;
62    private final PSPhase m_aPhase;
63    private final ICommonsList <PSPattern> m_aPatterns = new CommonsArrayList <> ();
64  
65    public AbstractPSBoundSchema (@Nonnull final IPSQueryBinding aQueryBinding,
66                                  @Nonnull final PSSchema aOrigSchema,
67                                  @Nullable final String sPhase,
68                                  @Nullable final IPSErrorHandler aCustomErrorHandler)
69    {
70      ValueEnforcer.notNull (aQueryBinding, "QueryBinding");
71      ValueEnforcer.notNull (aOrigSchema, "OrigSchema");
72  
73      m_aQueryBinding = aQueryBinding;
74      m_aOrigSchema = aOrigSchema;
75      m_aErrorHandler = aCustomErrorHandler != null ? aCustomErrorHandler : new LoggingPSErrorHandler ();
76      m_bDefaultErrorHandler = aCustomErrorHandler == null;
77  
78      // Determine all namespaces of the schema
79      m_aNamespaceContext = aOrigSchema.getAsNamespaceContext ();
80  
81      // Determine the phase ID to use
82      String sRealPhase = sPhase != null ? sPhase : CSchematron.PHASE_DEFAULT;
83      if (sRealPhase.equals (CSchematron.PHASE_DEFAULT))
84      {
85        sRealPhase = aOrigSchema.getDefaultPhase ();
86        if (sRealPhase == null)
87          sRealPhase = CSchematron.PHASE_ALL;
88      }
89      if (!sRealPhase.equals (CSchematron.PHASE_ALL))
90      {
91        m_aPhase = aOrigSchema.getPhaseOfID (sRealPhase);
92        if (m_aPhase == null)
93          warn (aOrigSchema, "Failed to resolve phase with ID '" + sRealPhase + "' - default to all patterns");
94      }
95      else
96        m_aPhase = null;
97      m_sPhase = sRealPhase;
98  
99      // Determine all patterns of the phase to use
100     if (m_aPhase == null)
101     {
102       // Apply all patterns
103       m_aPatterns.addAll (aOrigSchema.getAllPatterns ());
104     }
105     else
106     {
107       // Scan all active phases
108       for (final PSActive aActive : m_aPhase.getAllActives ())
109       {
110         final String sActivePatternID = aActive.getPattern ();
111         final PSPattern aPattern = aOrigSchema.getPatternOfID (sActivePatternID);
112         if (aPattern == null)
113         {
114           warn (aOrigSchema,
115                 "Failed to resolve pattern with ID '" +
116                              sActivePatternID +
117                              "' - ignoring this pattern in phase '" +
118                              sRealPhase +
119                              "'");
120         }
121         else
122         {
123           // Add the pattern of this phase
124           m_aPatterns.add (aPattern);
125         }
126       }
127     }
128     if (m_aPatterns.isEmpty ())
129       if (m_aPhase == null)
130         error (aOrigSchema, "No patterns found in schema!");
131       else
132         error (aOrigSchema, "No patterns found in schema for phase '" + m_aPhase.getID () + "!");
133   }
134 
135   @Nonnull
136   protected IPSErrorHandler getErrorHandler ()
137   {
138     return m_aErrorHandler;
139   }
140 
141   protected boolean isDefaultErrorHandler ()
142   {
143     return m_bDefaultErrorHandler;
144   }
145 
146   @OverridingMethodsMustInvokeSuper
147   protected void warn (@Nonnull final IPSElement aSourceElement, @Nonnull final String sMsg)
148   {
149     getErrorHandler ().warn (m_aOrigSchema.getResource (), aSourceElement, sMsg);
150   }
151 
152   @OverridingMethodsMustInvokeSuper
153   protected void error (@Nonnull final IPSElement aSourceElement, @Nonnull final String sMsg)
154   {
155     error (aSourceElement, sMsg, (Throwable) null);
156   }
157 
158   @OverridingMethodsMustInvokeSuper
159   protected void error (@Nonnull final IPSElement aSourceElement,
160                         @Nonnull final String sMsg,
161                         @Nullable final Throwable t)
162   {
163     getErrorHandler ().error (m_aOrigSchema.getResource (), aSourceElement, sMsg, t);
164   }
165 
166   @Nonnull
167   public final IPSQueryBinding getQueryBinding ()
168   {
169     return m_aQueryBinding;
170   }
171 
172   @Nonnull
173   public final PSSchema getOriginalSchema ()
174   {
175     return m_aOrigSchema;
176   }
177 
178   @Nonnull
179   public final MapBasedNamespaceContext getNamespaceContext ()
180   {
181     return m_aNamespaceContext;
182   }
183 
184   @Nonnull
185   public final String getPhaseID ()
186   {
187     return m_sPhase;
188   }
189 
190   @Nullable
191   public final PSPhase getPhase ()
192   {
193     return m_aPhase;
194   }
195 
196   public final boolean isPhaseSpecified ()
197   {
198     return m_aPhase != null;
199   }
200 
201   @Nonnull
202   @ReturnsMutableCopy
203   public final ICommonsList <PSPattern> getAllRelevantPatterns ()
204   {
205     return m_aPatterns.getClone ();
206   }
207 
208   /**
209    * Override this implementation in a derived class to modify the behavior.
210    *
211    * @return An implementation of {@link IPSPartialValidationHandler} to
212    *         use for partial validation. May not be <code>null</code>.
213    */
214   @Nonnull
215   @OverrideOnDemand
216   protected IPSPartialValidationHandler createPartialValidationHandler ()
217   {
218     return new PSValidationHandlerBreakOnFirstError ();
219   }
220 
221   @Nonnull
222   public EValidity validatePartially (@Nonnull final Node aNode,
223                                       @Nullable final String sBaseURI) throws SchematronValidationException
224   {
225     final IPSPartialValidationHandler aValidationHandler = createPartialValidationHandler ();
226     validate (aNode, sBaseURI, aValidationHandler);
227     return aValidationHandler.getValidity ();
228   }
229 
230   @Nonnull
231   public SchematronOutputType validateComplete (@Nonnull final Node aNode,
232                                                 @Nullable final String sBaseURI) throws SchematronValidationException
233   {
234     final PSXPathValidationHandlerSVRLonHandlerSVRL.html#PSXPathValidationHandlerSVRL">PSXPathValidationHandlerSVRL aValidationHandler = new PSXPathValidationHandlerSVRL (getErrorHandler ());
235     validate (aNode, sBaseURI, aValidationHandler);
236     return aValidationHandler.getSVRL ();
237   }
238 
239   @Override
240   public String toString ()
241   {
242     return new ToStringGenerator (this).append ("queryBinding", m_aQueryBinding)
243                                        .append ("origSchema", m_aOrigSchema)
244                                        .appendIfNotNull ("errorHandler", m_aErrorHandler)
245                                        .append ("namespaceContext", m_aNamespaceContext)
246                                        .appendIfNotNull ("phase", m_sPhase)
247                                        .appendIfNotNull ("phase", m_aPhase)
248                                        .append ("patterns", m_aPatterns)
249                                        .getToString ();
250   }
251 }