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