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.model;
18  
19  import java.util.Map;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.annotation.concurrent.NotThreadSafe;
24  
25  import com.helger.commons.ValueEnforcer;
26  import com.helger.commons.annotation.ReturnsMutableCopy;
27  import com.helger.commons.collection.CollectionHelper;
28  import com.helger.commons.collection.ext.CommonsArrayList;
29  import com.helger.commons.collection.ext.CommonsLinkedHashMap;
30  import com.helger.commons.collection.ext.ICommonsList;
31  import com.helger.commons.collection.ext.ICommonsOrderedMap;
32  import com.helger.commons.string.StringHelper;
33  import com.helger.commons.string.ToStringGenerator;
34  import com.helger.schematron.CSchematron;
35  import com.helger.schematron.CSchematronXML;
36  import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
37  import com.helger.xml.microdom.IMicroElement;
38  import com.helger.xml.microdom.MicroElement;
39  
40  /**
41   * A single Schematron phase-element.<br>
42   * A grouping of patterns, to name and declare variations in schemas, for
43   * example, to support progressive validation. The required id attribute is the
44   * name of the phase. The implementation determines which phase to use for
45   * validating documents, for example by user command.<br>
46   * Two names, #ALL and #DEFAULT, have special meanings. The name #ALL is
47   * reserved and available for use by implementations to denote that all patterns
48   * are active. The name #DEFAULT is reserved and available for use by
49   * implementations to denote that the name given in the defaultPhase attribute
50   * on the schema element should be used. If no defaultPhase is specified, then
51   * all patterns are active.<br>
52   * NOTE: The names #ALL and #DEFAULT shall not be used in a Schematron schema.
53   * They are for use when invoking or configuring schema validation, for example
54   * as a command-line parameter.<br>
55   * The icon, see and fpi attributes allow rich interfaces and documentation.<br>
56   * {@link PSPhase} elements are only referenced from {@link PSSchema} elements.
57   *
58   * @author Philip Helger
59   */
60  @NotThreadSafe
61  public class PSPhase implements IPSElement, IPSHasForeignElements, IPSHasIncludes, IPSHasLets, IPSHasID, IPSHasRichGroup
62  {
63    private String m_sID;
64    private PSRichGroup m_aRich;
65    private final ICommonsList <PSInclude> m_aIncludes = new CommonsArrayList <> ();
66    private final ICommonsList <IPSElement> m_aContent = new CommonsArrayList <> ();
67    private ICommonsOrderedMap <String, String> m_aForeignAttrs;
68    private ICommonsList <IMicroElement> m_aForeignElements;
69  
70    public PSPhase ()
71    {}
72  
73    public boolean isValid (@Nonnull final IPSErrorHandler aErrorHandler)
74    {
75      for (final PSInclude aInclude : m_aIncludes)
76        if (!aInclude.isValid (aErrorHandler))
77          return false;
78      for (final IPSElement aContent : m_aContent)
79        if (!aContent.isValid (aErrorHandler))
80          return false;
81      if (StringHelper.hasNoText (m_sID))
82      {
83        aErrorHandler.error (this, "<phase> has no 'id'");
84        return false;
85      }
86      return true;
87    }
88  
89    public void validateCompletely (@Nonnull final IPSErrorHandler aErrorHandler)
90    {
91      for (final PSInclude aInclude : m_aIncludes)
92        aInclude.validateCompletely (aErrorHandler);
93      for (final IPSElement aContent : m_aContent)
94        aContent.validateCompletely (aErrorHandler);
95      if (StringHelper.hasNoText (m_sID))
96        aErrorHandler.error (this, "<phase> has no 'id'");
97    }
98  
99    public boolean isMinimal ()
100   {
101     for (final PSInclude aInclude : m_aIncludes)
102       if (!aInclude.isMinimal ())
103         return false;
104     for (final IPSElement aContent : m_aContent)
105       if (!aContent.isMinimal ())
106         return false;
107     return true;
108   }
109 
110   public void addForeignElement (@Nonnull final IMicroElement aForeignElement)
111   {
112     ValueEnforcer.notNull (aForeignElement, "ForeignElement");
113     if (aForeignElement.hasParent ())
114       throw new IllegalArgumentException ("ForeignElement already has a parent!");
115     if (m_aForeignElements == null)
116       m_aForeignElements = new CommonsArrayList <> ();
117     m_aForeignElements.add (aForeignElement);
118   }
119 
120   public boolean hasForeignElements ()
121   {
122     return m_aForeignElements != null && m_aForeignElements.isNotEmpty ();
123   }
124 
125   @Nonnull
126   @ReturnsMutableCopy
127   public ICommonsList <IMicroElement> getAllForeignElements ()
128   {
129     return new CommonsArrayList <> (m_aForeignElements);
130   }
131 
132   public void addForeignAttribute (@Nonnull final String sAttrName, @Nonnull final String sAttrValue)
133   {
134     ValueEnforcer.notNull (sAttrName, "AttrName");
135     ValueEnforcer.notNull (sAttrValue, "AttrValue");
136     if (m_aForeignAttrs == null)
137       m_aForeignAttrs = new CommonsLinkedHashMap <> ();
138     m_aForeignAttrs.put (sAttrName, sAttrValue);
139   }
140 
141   public boolean hasForeignAttributes ()
142   {
143     return m_aForeignAttrs != null && m_aForeignAttrs.isNotEmpty ();
144   }
145 
146   @Nonnull
147   @ReturnsMutableCopy
148   public ICommonsOrderedMap <String, String> getAllForeignAttributes ()
149   {
150     return new CommonsLinkedHashMap <> (m_aForeignAttrs);
151   }
152 
153   public void setID (@Nullable final String sID)
154   {
155     m_sID = sID;
156   }
157 
158   @Nullable
159   public String getID ()
160   {
161     return m_sID;
162   }
163 
164   public void setRich (@Nullable final PSRichGroup aRich)
165   {
166     m_aRich = aRich;
167   }
168 
169   @Nullable
170   public PSRichGroup getRich ()
171   {
172     return m_aRich;
173   }
174 
175   public void addInclude (@Nonnull final PSInclude aInclude)
176   {
177     ValueEnforcer.notNull (aInclude, "Include");
178     m_aIncludes.add (aInclude);
179   }
180 
181   public boolean hasAnyInclude ()
182   {
183     return m_aIncludes.isNotEmpty ();
184   }
185 
186   @Nonnull
187   @ReturnsMutableCopy
188   public ICommonsList <PSInclude> getAllIncludes ()
189   {
190     return m_aIncludes.getClone ();
191   }
192 
193   public void addP (@Nonnull final PSP aP)
194   {
195     ValueEnforcer.notNull (aP, "P");
196     m_aContent.add (aP);
197   }
198 
199   @Nonnull
200   @ReturnsMutableCopy
201   public ICommonsList <PSP> getAllPs ()
202   {
203     return m_aContent.getAllInstanceOf (PSP.class);
204   }
205 
206   public void addLet (@Nonnull final PSLet aLet)
207   {
208     ValueEnforcer.notNull (aLet, "Let");
209     m_aContent.add (aLet);
210   }
211 
212   public boolean hasAnyLet ()
213   {
214     return m_aContent.containsAny (e -> e instanceof PSLet);
215   }
216 
217   @Nonnull
218   @ReturnsMutableCopy
219   public ICommonsList <PSLet> getAllLets ()
220   {
221     return m_aContent.getAllInstanceOf (PSLet.class);
222   }
223 
224   @Nonnull
225   @ReturnsMutableCopy
226   public ICommonsOrderedMap <String, String> getAllLetsAsMap ()
227   {
228     final ICommonsOrderedMap <String, String> ret = new CommonsLinkedHashMap <> ();
229     for (final IPSElement aElement : m_aContent)
230       if (aElement instanceof PSLet)
231       {
232         final PSLet aLet = (PSLet) aElement;
233         ret.put (aLet.getName (), aLet.getValue ());
234       }
235     return ret;
236   }
237 
238   public void addActive (@Nonnull final PSActive aActive)
239   {
240     ValueEnforcer.notNull (aActive, "Active");
241     m_aContent.add (aActive);
242   }
243 
244   @Nonnull
245   @ReturnsMutableCopy
246   public ICommonsList <PSActive> getAllActives ()
247   {
248     return m_aContent.getAllInstanceOf (PSActive.class);
249   }
250 
251   /**
252    * @return A list of {@link PSActive}, {@link PSLet} and {@link PSP} elements.
253    */
254   @Nonnull
255   @ReturnsMutableCopy
256   public ICommonsList <IPSElement> getAllContentElements ()
257   {
258     return m_aContent.getClone ();
259   }
260 
261   @Nonnull
262   public IMicroElement getAsMicroElement ()
263   {
264     final IMicroElement ret = new MicroElement (CSchematron.NAMESPACE_SCHEMATRON, CSchematronXML.ELEMENT_PHASE);
265     ret.setAttribute (CSchematronXML.ATTR_ID, m_sID);
266     if (m_aRich != null)
267       m_aRich.fillMicroElement (ret);
268     if (m_aForeignElements != null)
269       for (final IMicroElement aForeignElement : m_aForeignElements)
270         ret.appendChild (aForeignElement.getClone ());
271     for (final PSInclude aInclude : m_aIncludes)
272       ret.appendChild (aInclude.getAsMicroElement ());
273     for (final IPSElement aContent : m_aContent)
274       ret.appendChild (aContent.getAsMicroElement ());
275     if (m_aForeignAttrs != null)
276       for (final Map.Entry <String, String> aEntry : m_aForeignAttrs.entrySet ())
277         ret.setAttribute (aEntry.getKey (), aEntry.getValue ());
278     return ret;
279   }
280 
281   @Override
282   public String toString ()
283   {
284     return new ToStringGenerator (this).appendIfNotNull ("id", m_sID)
285                                        .appendIfNotNull ("rich", m_aRich)
286                                        .appendIf ("includes", m_aIncludes, CollectionHelper::isNotEmpty)
287                                        .appendIf ("content", m_aContent, CollectionHelper::isNotEmpty)
288                                        .appendIf ("foreignAttrs", m_aForeignAttrs, CollectionHelper::isNotEmpty)
289                                        .appendIf ("foreignElements", m_aForeignElements, CollectionHelper::isNotEmpty)
290                                        .getToString ();
291   }
292 }