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.svrl;
18  
19  import java.util.regex.Matcher;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.annotation.concurrent.ThreadSafe;
24  
25  import org.oclc.purl.dsdl.svrl.FailedAssert;
26  import org.oclc.purl.dsdl.svrl.SchematronOutputType;
27  import org.oclc.purl.dsdl.svrl.SuccessfulReport;
28  
29  import com.helger.commons.ValueEnforcer;
30  import com.helger.commons.annotation.PresentForCodeCoverage;
31  import com.helger.commons.annotation.ReturnsMutableCopy;
32  import com.helger.commons.collection.impl.CommonsArrayList;
33  import com.helger.commons.collection.impl.ICommonsList;
34  import com.helger.commons.concurrent.SimpleReadWriteLock;
35  import com.helger.commons.error.level.IErrorLevel;
36  import com.helger.commons.regex.RegExHelper;
37  import com.helger.commons.string.StringHelper;
38  
39  /**
40   * Miscellaneous utility methods for handling Schematron output (SVRL).
41   *
42   * @author Philip Helger
43   */
44  @ThreadSafe
45  public final class SVRLHelper
46  {
47    private static final SimpleReadWriteLock s_aRWLock = new SimpleReadWriteLock ();
48  
49    private static ISVRLErrorLevelDeterminator s_aELD = new DefaultSVRLErrorLevelDeterminator ();
50  
51    @PresentForCodeCoverage
52    private static final SVRLHelper.html#SVRLHelper">SVRLHelper s_aInstance = new SVRLHelper ();
53  
54    private SVRLHelper ()
55    {}
56  
57    /**
58     * Get a list of all failed assertions in a given schematron output.
59     *
60     * @param aSchematronOutput
61     *        The schematron output to be used. May be <code>null</code>.
62     * @return A non-<code>null</code> list with all failed assertions.
63     */
64    @Nonnull
65    @ReturnsMutableCopy
66    public static ICommonsList <SVRLFailedAssert> getAllFailedAssertions (@Nullable final SchematronOutputType aSchematronOutput)
67    {
68      final ICommonsList <SVRLFailedAssert> ret = new CommonsArrayList <> ();
69      if (aSchematronOutput != null)
70        for (final Object aObj : aSchematronOutput.getActivePatternAndFiredRuleAndFailedAssert ())
71          if (aObj instanceof FailedAssert)
72            ret.add (new SVRLFailedAssert ((FailedAssert) aObj));
73      return ret;
74    }
75  
76    /**
77     * Get a list of all failed assertions in a given schematron output, with an
78     * error level equally or more severe than the passed error level.
79     *
80     * @param aSchematronOutput
81     *        The schematron output to be used. May be <code>null</code>.
82     * @param aErrorLevel
83     *        Minimum error level to be queried
84     * @return A non-<code>null</code> list with all failed assertions.
85     */
86    @Nonnull
87    @ReturnsMutableCopy
88    public static ICommonsList <SVRLFailedAssert> getAllFailedAssertionsMoreOrEqualSevereThan (@Nullable final SchematronOutputType aSchematronOutput,
89                                                                                               @Nonnull final IErrorLevel aErrorLevel)
90    {
91      final ICommonsList <SVRLFailedAssert> ret = new CommonsArrayList <> ();
92      if (aSchematronOutput != null)
93        for (final Object aObj : aSchematronOutput.getActivePatternAndFiredRuleAndFailedAssert ())
94          if (aObj instanceof FailedAssert)
95          {
96            final SVRLFailedAssertedAssert.html#SVRLFailedAssert">SVRLFailedAssert aFA = new SVRLFailedAssert ((FailedAssert) aObj);
97            if (aFA.getFlag ().isGE (aErrorLevel))
98              ret.add (aFA);
99          }
100     return ret;
101   }
102 
103   /**
104    * Get a list of all successful reports in a given schematron output.
105    *
106    * @param aSchematronOutput
107    *        The schematron output to be used. May be <code>null</code>.
108    * @return A non-<code>null</code> list with all successful reports.
109    */
110   @Nonnull
111   @ReturnsMutableCopy
112   public static ICommonsList <SVRLSuccessfulReport> getAllSuccessfulReports (@Nullable final SchematronOutputType aSchematronOutput)
113   {
114     final ICommonsList <SVRLSuccessfulReport> ret = new CommonsArrayList <> ();
115     if (aSchematronOutput != null)
116       for (final Object aObj : aSchematronOutput.getActivePatternAndFiredRuleAndFailedAssert ())
117         if (aObj instanceof SuccessfulReport)
118           ret.add (new SVRLSuccessfulReport ((SuccessfulReport) aObj));
119     return ret;
120   }
121 
122   /**
123    * Get a list of all successful reports in a given schematron output, with an
124    * error level equally or more severe than the passed error level.
125    *
126    * @param aSchematronOutput
127    *        The schematron output to be used. May be <code>null</code>.
128    * @param aErrorLevel
129    *        Minimum error level to be queried
130    * @return A non-<code>null</code> list with all successful reports.
131    */
132   @Nonnull
133   @ReturnsMutableCopy
134   public static ICommonsList <SVRLSuccessfulReport> getAllSuccessfulReportsMoreOrEqualSevereThan (@Nullable final SchematronOutputType aSchematronOutput,
135                                                                                                   @Nonnull final IErrorLevel aErrorLevel)
136   {
137     final ICommonsList <SVRLSuccessfulReport> ret = new CommonsArrayList <> ();
138     if (aSchematronOutput != null)
139       for (final Object aObj : aSchematronOutput.getActivePatternAndFiredRuleAndFailedAssert ())
140         if (aObj instanceof SuccessfulReport)
141         {
142           final SVRLSuccessfulReportulReport.html#SVRLSuccessfulReport">SVRLSuccessfulReport aSR = new SVRLSuccessfulReport ((SuccessfulReport) aObj);
143           if (aSR.getFlag ().isGE (aErrorLevel))
144             ret.add (aSR);
145         }
146     return ret;
147   }
148 
149   /**
150    * Get a list of all failed assertions and successful reports in a given
151    * schematron output.
152    *
153    * @param aSchematronOutput
154    *        The schematron output to be used. May be <code>null</code>.
155    * @return A non-<code>null</code> list with all failed assertions and
156    *         successful reports. Maybe an empty list if the input is
157    *         <code>null</code>.
158    */
159   @Nonnull
160   @ReturnsMutableCopy
161   public static ICommonsList <AbstractSVRLMessage> getAllFailedAssertionsAndSuccessfulReports (@Nullable final SchematronOutputType aSchematronOutput)
162   {
163     final ICommonsList <AbstractSVRLMessage> ret = new CommonsArrayList <> ();
164     if (aSchematronOutput != null)
165       for (final Object aObj : aSchematronOutput.getActivePatternAndFiredRuleAndFailedAssert ())
166         if (aObj instanceof FailedAssert)
167           ret.add (new SVRLFailedAssert ((FailedAssert) aObj));
168         else
169           if (aObj instanceof SuccessfulReport)
170             ret.add (new SVRLSuccessfulReport ((SuccessfulReport) aObj));
171     return ret;
172   }
173 
174   /**
175    * Get the error level associated with a single failed assertion.
176    *
177    * @param aFailedAssert
178    *        The failed assert to be queried. May not be <code>null</code>.
179    * @return The error level and never <code>null</code>.
180    */
181   @Nonnull
182   public static IErrorLevel getErrorLevelFromFailedAssert (@Nonnull final FailedAssert aFailedAssert)
183   {
184     return getErrorLevelDeterminator ().getErrorLevelFromFailedAssert (aFailedAssert);
185   }
186 
187   /**
188    * Get the error level associated with a single successful report.
189    *
190    * @param aSuccessfulReport
191    *        The failed assert to be queried. May not be <code>null</code>.
192    * @return The error level and never <code>null</code>.
193    */
194   @Nonnull
195   public static IErrorLevel getErrorLevelFromSuccessfulReport (@Nonnull final SuccessfulReport aSuccessfulReport)
196   {
197     return getErrorLevelDeterminator ().getErrorLevelFromSuccessfulReport (aSuccessfulReport);
198   }
199 
200   /**
201    * @return The default error level determinator. May not be <code>null</code>.
202    */
203   @Nonnull
204   public static ISVRLErrorLevelDeterminator getErrorLevelDeterminator ()
205   {
206     return s_aRWLock.readLocked ( () -> s_aELD);
207   }
208 
209   /**
210    * Set the global error level determinator.
211    *
212    * @param aELD
213    *        The determinator to use. May not be <code>null</code>.
214    */
215   public static void setErrorLevelDeterminator (@Nonnull final ISVRLErrorLevelDeterminator aELD)
216   {
217     ValueEnforcer.notNull (aELD, "ErrorLevelDeterminator");
218 
219     s_aRWLock.readLocked ( () -> s_aELD = aELD);
220   }
221 
222   /**
223    * Convert an "unsexy" location string in the for, of
224    * <code>*:xx[namespace-uri()='yy']</code> to something more readable like
225    * <code>prefix:xx</code> by using the mapping registered in the
226    * {@link SVRLLocationBeautifierRegistry}.
227    *
228    * @param sLocation
229    *        The original location string. May not be <code>null</code>.
230    * @return The beautified string. Never <code>null</code>. Might be identical
231    *         to the original string if the pattern was not found.
232    * @since 5.0.1
233    */
234   @Nonnull
235   public static String getBeautifiedLocation (@Nonnull final String sLocation)
236   {
237     String sResult = sLocation;
238     // Handle namespaces:
239     // Search for "*:xx[namespace-uri()='yy']" where xx is the localname and yy
240     // is the namespace URI
241     final Matcher aMatcher = RegExHelper.getMatcher ("\\Q*:\\E([a-zA-Z0-9_]+)\\Q[namespace-uri()='\\E([^']+)\\Q']\\E",
242                                                      sResult);
243     while (aMatcher.find ())
244     {
245       final String sLocalName = aMatcher.group (1);
246       final String sNamespaceURI = aMatcher.group (2);
247 
248       // Check if there is a known beautifier for this pair of namespace and
249       // local name
250       final String sBeautified = SVRLLocationBeautifierRegistry.getBeautifiedLocation (sNamespaceURI, sLocalName);
251       if (sBeautified != null)
252         sResult = StringHelper.replaceAll (sResult, aMatcher.group (), sBeautified);
253     }
254     return sResult;
255   }
256 }