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.Nonempty;
27  import com.helger.commons.annotation.ReturnsMutableCopy;
28  import com.helger.commons.collection.CollectionHelper;
29  import com.helger.commons.collection.ext.CommonsArrayList;
30  import com.helger.commons.collection.ext.CommonsLinkedHashMap;
31  import com.helger.commons.collection.ext.ICommonsList;
32  import com.helger.commons.collection.ext.ICommonsOrderedMap;
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  import com.helger.xml.microdom.IMicroElement;
39  import com.helger.xml.microdom.MicroElement;
40  
41  /**
42   * A single Schematron active-element.<br>
43   * The required pattern attribute is a reference to a pattern that is active in
44   * the current phase.<br>
45   * {@link PSActive} elements are only references from {@link PSPhase} elements.
46   *
47   * @author Philip Helger
48   */
49  @NotThreadSafe
50  public class PSActive implements IPSClonableElement <PSActive>, IPSHasForeignElements, IPSHasMixedContent
51  {
52    private String m_sPattern;
53    private final ICommonsList <Object> m_aContent = new CommonsArrayList <> ();
54    private ICommonsOrderedMap <String, String> m_aForeignAttrs;
55    private ICommonsList <IMicroElement> m_aForeignElements;
56  
57    public PSActive ()
58    {}
59  
60    public boolean isValid (@Nonnull final IPSErrorHandler aErrorHandler)
61    {
62      for (final Object aContent : m_aContent)
63        if (aContent instanceof IPSElement)
64          if (!((IPSElement) aContent).isValid (aErrorHandler))
65            return false;
66      if (StringHelper.hasNoText (m_sPattern))
67      {
68        aErrorHandler.error (this, "<active> has no 'pattern'");
69        return false;
70      }
71      return true;
72    }
73  
74    public void validateCompletely (@Nonnull final IPSErrorHandler aErrorHandler)
75    {
76      for (final Object aContent : m_aContent)
77        if (aContent instanceof IPSElement)
78          ((IPSElement) aContent).validateCompletely (aErrorHandler);
79      if (StringHelper.hasNoText (m_sPattern))
80        aErrorHandler.error (this, "<active> has no 'pattern'");
81    }
82  
83    public boolean isMinimal ()
84    {
85      for (final Object aContent : m_aContent)
86        if (aContent instanceof IPSElement)
87          if (!((IPSElement) aContent).isMinimal ())
88            return false;
89      return true;
90    }
91  
92    public void addForeignElement (@Nonnull final IMicroElement aForeignElement)
93    {
94      ValueEnforcer.notNull (aForeignElement, "ForeignElement");
95      if (aForeignElement.hasParent ())
96        throw new IllegalArgumentException ("ForeignElement already has a parent!");
97      if (m_aForeignElements == null)
98        m_aForeignElements = new CommonsArrayList <> ();
99      m_aForeignElements.add (aForeignElement);
100   }
101 
102   public boolean hasForeignElements ()
103   {
104     return m_aForeignElements != null && m_aForeignElements.isNotEmpty ();
105   }
106 
107   @Nonnull
108   @ReturnsMutableCopy
109   public ICommonsList <IMicroElement> getAllForeignElements ()
110   {
111     return new CommonsArrayList <> (m_aForeignElements);
112   }
113 
114   public void addForeignAttribute (@Nonnull final String sAttrName, @Nonnull final String sAttrValue)
115   {
116     ValueEnforcer.notNull (sAttrName, "AttrName");
117     ValueEnforcer.notNull (sAttrValue, "AttrValue");
118     if (m_aForeignAttrs == null)
119       m_aForeignAttrs = new CommonsLinkedHashMap <> ();
120     m_aForeignAttrs.put (sAttrName, sAttrValue);
121   }
122 
123   public boolean hasForeignAttributes ()
124   {
125     return m_aForeignAttrs != null && m_aForeignAttrs.isNotEmpty ();
126   }
127 
128   @Nonnull
129   @ReturnsMutableCopy
130   public ICommonsOrderedMap <String, String> getAllForeignAttributes ()
131   {
132     return new CommonsLinkedHashMap <> (m_aForeignAttrs);
133   }
134 
135   /**
136    * @param sPattern
137    *        The ID of the pattern to set active.
138    */
139   public void setPattern (@Nullable final String sPattern)
140   {
141     m_sPattern = sPattern;
142   }
143 
144   /**
145    * @return ID of the {@link PSPattern} to be marked active. May be
146    *         <code>null</code>.
147    */
148   @Nullable
149   public String getPattern ()
150   {
151     return m_sPattern;
152   }
153 
154   public void addText (@Nonnull @Nonempty final String sText)
155   {
156     ValueEnforcer.notEmpty (sText, "Text");
157     m_aContent.add (sText);
158   }
159 
160   public boolean hasAnyText ()
161   {
162     return m_aContent.containsAny (e -> e instanceof String);
163   }
164 
165   @Nonnull
166   @ReturnsMutableCopy
167   public ICommonsList <String> getAllTexts ()
168   {
169     return m_aContent.getAllInstanceOf (String.class);
170   }
171 
172   public void addDir (@Nonnull final PSDir aDir)
173   {
174     ValueEnforcer.notNull (aDir, "Dir");
175     m_aContent.add (aDir);
176   }
177 
178   @Nonnull
179   @ReturnsMutableCopy
180   public ICommonsList <PSDir> getAllDirs ()
181   {
182     return m_aContent.getAllInstanceOf (PSDir.class);
183   }
184 
185   public void addEmph (@Nonnull final PSEmph aEmph)
186   {
187     ValueEnforcer.notNull (aEmph, "Emph");
188     m_aContent.add (aEmph);
189   }
190 
191   @Nonnull
192   @ReturnsMutableCopy
193   public ICommonsList <PSEmph> getAllEmphs ()
194   {
195     return m_aContent.getAllInstanceOf (PSEmph.class);
196   }
197 
198   public void addSpan (@Nonnull final PSSpan aSpan)
199   {
200     ValueEnforcer.notNull (aSpan, "Span");
201     m_aContent.add (aSpan);
202   }
203 
204   @Nonnull
205   @ReturnsMutableCopy
206   public ICommonsList <PSSpan> getAllSpans ()
207   {
208     return m_aContent.getAllInstanceOf (PSSpan.class);
209   }
210 
211   /**
212    * @return A list of {@link String}, {@link PSDir}, {@link PSEmph} and
213    *         {@link PSSpan} elements.
214    */
215   @Nonnull
216   @ReturnsMutableCopy
217   public ICommonsList <Object> getAllContentElements ()
218   {
219     return m_aContent.getClone ();
220   }
221 
222   @Nonnull
223   public IMicroElement getAsMicroElement ()
224   {
225     final IMicroElement ret = new MicroElement (CSchematron.NAMESPACE_SCHEMATRON, CSchematronXML.ELEMENT_ACTIVE);
226     ret.setAttribute (CSchematronXML.ATTR_PATTERN, m_sPattern);
227     if (m_aForeignElements != null)
228       for (final IMicroElement aForeignElement : m_aForeignElements)
229         ret.appendChild (aForeignElement.getClone ());
230     for (final Object aContent : m_aContent)
231       if (aContent instanceof String)
232         ret.appendText ((String) aContent);
233       else
234         ret.appendChild (((IPSElement) aContent).getAsMicroElement ());
235     if (m_aForeignAttrs != null)
236       for (final Map.Entry <String, String> aEntry : m_aForeignAttrs.entrySet ())
237         ret.setAttribute (aEntry.getKey (), aEntry.getValue ());
238     return ret;
239   }
240 
241   @Nonnull
242   public PSActive getClone ()
243   {
244     final PSActive ret = new PSActive ();
245     ret.setPattern (m_sPattern);
246     for (final Object aContent : m_aContent)
247     {
248       if (aContent instanceof String)
249         ret.addText ((String) aContent);
250       else
251         if (aContent instanceof PSDir)
252           ret.addDir (((PSDir) aContent).getClone ());
253         else
254           if (aContent instanceof PSEmph)
255             ret.addEmph (((PSEmph) aContent).getClone ());
256           else
257             if (aContent instanceof PSSpan)
258               ret.addSpan (((PSSpan) aContent).getClone ());
259     }
260     if (hasForeignElements ())
261       ret.addForeignElements (m_aForeignElements);
262     if (hasForeignAttributes ())
263       ret.addForeignAttributes (m_aForeignAttrs);
264     return ret;
265   }
266 
267   @Override
268   public String toString ()
269   {
270     return new ToStringGenerator (this).appendIfNotNull ("pattern", m_sPattern)
271                                        .appendIf ("content", m_aContent, CollectionHelper::isNotEmpty)
272                                        .appendIf ("foreignAttrs", m_aForeignAttrs, CollectionHelper::isNotEmpty)
273                                        .appendIf ("foreignElements", m_aForeignElements, CollectionHelper::isNotEmpty)
274                                        .getToString ();
275   }
276 }