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 java.util.ArrayList;
20  import java.util.List;
21  
22  import javax.annotation.Nonnull;
23  import javax.annotation.Nullable;
24  import javax.annotation.OverridingMethodsMustInvokeSuper;
25  
26  import org.w3c.dom.Node;
27  
28  import com.helger.commons.ValueEnforcer;
29  import com.helger.commons.annotation.OverrideOnDemand;
30  import com.helger.commons.annotation.ReturnsMutableCopy;
31  import com.helger.commons.collection.CollectionHelper;
32  import com.helger.commons.state.EValidity;
33  import com.helger.commons.string.ToStringGenerator;
34  import com.helger.commons.xml.namespace.MapBasedNamespaceContext;
35  import com.helger.schematron.CSchematron;
36  import com.helger.schematron.pure.binding.IPSQueryBinding;
37  import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
38  import com.helger.schematron.pure.errorhandler.LoggingPSErrorHandler;
39  import com.helger.schematron.pure.model.IPSElement;
40  import com.helger.schematron.pure.model.PSActive;
41  import com.helger.schematron.pure.model.PSPattern;
42  import com.helger.schematron.pure.model.PSPhase;
43  import com.helger.schematron.pure.model.PSSchema;
44  import com.helger.schematron.pure.validation.AbstractPSPartialValidationHandler;
45  import com.helger.schematron.pure.validation.PSValidationHandlerBreakOnFirstError;
46  import com.helger.schematron.pure.validation.SchematronValidationException;
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 List <PSPattern> m_aPatterns = new ArrayList <PSPattern> ();
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, "Failed to resolve pattern with ID '" +
115                              sActivePatternID +
116                              "' - ignoring this pattern in phase '" +
117                              sRealPhase +
118                              "'");
119         }
120         else
121         {
122           // Add the pattern of this phase
123           m_aPatterns.add (aPattern);
124         }
125       }
126     }
127     if (m_aPatterns.isEmpty ())
128       if (m_aPhase == null)
129         error (aOrigSchema, "No patterns found in schema!");
130       else
131         error (aOrigSchema, "No patterns found in schema for phase '" + m_aPhase.getID () + "!");
132   }
133 
134   @Nonnull
135   protected IPSErrorHandler getErrorHandler ()
136   {
137     return m_aErrorHandler;
138   }
139 
140   protected boolean isDefaultErrorHandler ()
141   {
142     return m_bDefaultErrorHandler;
143   }
144 
145   @OverridingMethodsMustInvokeSuper
146   protected void warn (@Nonnull final IPSElement aSourceElement, @Nonnull final String sMsg)
147   {
148     getErrorHandler ().warn (m_aOrigSchema.getResource (), aSourceElement, sMsg);
149   }
150 
151   @OverridingMethodsMustInvokeSuper
152   protected void error (@Nonnull final IPSElement aSourceElement, @Nonnull final String sMsg)
153   {
154     error (aSourceElement, sMsg, (Throwable) null);
155   }
156 
157   @OverridingMethodsMustInvokeSuper
158   protected void error (@Nonnull final IPSElement aSourceElement,
159                         @Nonnull final String sMsg,
160                         @Nullable final Throwable t)
161   {
162     getErrorHandler ().error (m_aOrigSchema.getResource (), aSourceElement, sMsg, t);
163   }
164 
165   @Nonnull
166   public final IPSQueryBinding getQueryBinding ()
167   {
168     return m_aQueryBinding;
169   }
170 
171   @Nonnull
172   public final PSSchema getOriginalSchema ()
173   {
174     return m_aOrigSchema;
175   }
176 
177   @Nonnull
178   public final MapBasedNamespaceContext getNamespaceContext ()
179   {
180     return m_aNamespaceContext;
181   }
182 
183   @Nonnull
184   public final String getPhaseID ()
185   {
186     return m_sPhase;
187   }
188 
189   @Nullable
190   public final PSPhase getPhase ()
191   {
192     return m_aPhase;
193   }
194 
195   public final boolean isPhaseSpecified ()
196   {
197     return m_aPhase != null;
198   }
199 
200   @Nonnull
201   @ReturnsMutableCopy
202   public final List <PSPattern> getAllRelevantPatterns ()
203   {
204     return CollectionHelper.newList (m_aPatterns);
205   }
206 
207   /**
208    * Override this implementation in a derived class to modify the behavior.
209    *
210    * @return An implementation of {@link AbstractPSPartialValidationHandler} to
211    *         use for partial validation. May not be <code>null</code>.
212    */
213   @Nonnull
214   @OverrideOnDemand
215   protected AbstractPSPartialValidationHandler createPartialValidationHandler ()
216   {
217     return new PSValidationHandlerBreakOnFirstError ();
218   }
219 
220   @Nonnull
221   public EValidity validatePartially (@Nonnull final Node aNode) throws SchematronValidationException
222   {
223     final AbstractPSPartialValidationHandler aValidationHandler = createPartialValidationHandler ();
224     validate (aNode, aValidationHandler);
225     return aValidationHandler.getValidity ();
226   }
227 
228   @Override
229   public String toString ()
230   {
231     return new ToStringGenerator (this).append ("queryBinding", m_aQueryBinding)
232                                        .append ("origSchema", m_aOrigSchema)
233                                        .appendIfNotNull ("errorHandler", m_aErrorHandler)
234                                        .append ("namespaceContext", m_aNamespaceContext)
235                                        .appendIfNotNull ("phase", m_sPhase)
236                                        .appendIfNotNull ("phase", m_aPhase)
237                                        .append ("patterns", m_aPatterns)
238                                        .toString ();
239   }
240 }