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