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.Nonempty;
30  import com.helger.commons.annotation.ReturnsMutableCopy;
31  import com.helger.commons.collection.CollectionHelper;
32  import com.helger.commons.microdom.IMicroElement;
33  import com.helger.commons.microdom.MicroElement;
34  import com.helger.commons.string.StringHelper;
35  import com.helger.commons.string.ToStringGenerator;
36  import com.helger.schematron.CSchematron;
37  import com.helger.schematron.CSchematronXML;
38  import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
39  
40  /**
41   * A single Schematron diagnostic-element.<br>
42   * A natural-language message giving more specific details concerning a failed
43   * assertion, such as found versus expected values and repair hints.<br>
44   * NOTE: Diagnostics in multiple languages may be supported by using a different
45   * diagnostic element for each language, with the appropriate xml:lang language
46   * attribute, and referencing all the unique identifiers of the diagnostic
47   * elements in the diagnostics attribute of the assertion. Annex G gives a
48   * simple example of a multi-lingual schema.<br>
49   * An implementation is not required to make use of this element.
50   *
51   * @author Philip Helger
52   */
53  @NotThreadSafe
54  public class PSDiagnostic implements IPSClonableElement <PSDiagnostic>, IPSOptionalElement, IPSHasID, IPSHasForeignElements, IPSHasMixedContent, IPSHasRichGroup
55  {
56    private String m_sID;
57    private PSRichGroup m_aRich;
58    private final List <Object> m_aContent = new ArrayList <Object> ();
59    private Map <String, String> m_aForeignAttrs;
60    private List <IMicroElement> m_aForeignElements;
61  
62    public PSDiagnostic ()
63    {}
64  
65    public boolean isValid (@Nonnull final IPSErrorHandler aErrorHandler)
66    {
67      for (final Object aContent : m_aContent)
68        if (aContent instanceof IPSElement)
69          if (!((IPSElement) aContent).isValid (aErrorHandler))
70            return false;
71      if (StringHelper.hasNoText (m_sID))
72      {
73        aErrorHandler.error (this, "<diagnostic> has no 'id'");
74        return false;
75      }
76      return true;
77    }
78  
79    public void validateCompletely (@Nonnull final IPSErrorHandler aErrorHandler)
80    {
81      for (final Object aContent : m_aContent)
82        if (aContent instanceof IPSElement)
83          ((IPSElement) aContent).validateCompletely (aErrorHandler);
84      if (StringHelper.hasNoText (m_sID))
85        aErrorHandler.error (this, "<diagnostic> has no 'id'");
86    }
87  
88    public boolean isMinimal ()
89    {
90      return false;
91    }
92  
93    public void addForeignElement (@Nonnull final IMicroElement aForeignElement)
94    {
95      ValueEnforcer.notNull (aForeignElement, "ForeignElement");
96      if (aForeignElement.hasParent ())
97        throw new IllegalArgumentException ("ForeignElement already has a parent!");
98      if (m_aForeignElements == null)
99        m_aForeignElements = new ArrayList <IMicroElement> ();
100     m_aForeignElements.add (aForeignElement);
101   }
102 
103   public void addForeignElements (@Nonnull final List <IMicroElement> aForeignElements)
104   {
105     ValueEnforcer.notNull (aForeignElements, "ForeignElements");
106     for (final IMicroElement aForeignElement : aForeignElements)
107       addForeignElement (aForeignElement);
108   }
109 
110   public boolean hasForeignElements ()
111   {
112     return m_aForeignElements != null && !m_aForeignElements.isEmpty ();
113   }
114 
115   @Nonnull
116   @ReturnsMutableCopy
117   public List <IMicroElement> getAllForeignElements ()
118   {
119     return CollectionHelper.newList (m_aForeignElements);
120   }
121 
122   public void addForeignAttribute (@Nonnull final String sAttrName, @Nonnull final String sAttrValue)
123   {
124     ValueEnforcer.notNull (sAttrName, "AttrName");
125     ValueEnforcer.notNull (sAttrValue, "AttrValue");
126     if (m_aForeignAttrs == null)
127       m_aForeignAttrs = new LinkedHashMap <String, String> ();
128     m_aForeignAttrs.put (sAttrName, sAttrValue);
129   }
130 
131   public void addForeignAttributes (@Nonnull final Map <String, String> aForeignAttrs)
132   {
133     ValueEnforcer.notNull (aForeignAttrs, "ForeignAttrs");
134     for (final Map.Entry <String, String> aEntry : aForeignAttrs.entrySet ())
135       addForeignAttribute (aEntry.getKey (), aEntry.getValue ());
136   }
137 
138   public boolean hasForeignAttributes ()
139   {
140     return m_aForeignAttrs != null && !m_aForeignAttrs.isEmpty ();
141   }
142 
143   @Nonnull
144   @ReturnsMutableCopy
145   public Map <String, String> getAllForeignAttributes ()
146   {
147     return CollectionHelper.newOrderedMap (m_aForeignAttrs);
148   }
149 
150   public void setID (@Nullable final String sID)
151   {
152     m_sID = sID;
153   }
154 
155   public boolean hasID ()
156   {
157     return m_sID != null;
158   }
159 
160   @Nullable
161   public String getID ()
162   {
163     return m_sID;
164   }
165 
166   public void setRich (@Nullable final PSRichGroup aRich)
167   {
168     m_aRich = aRich;
169   }
170 
171   public boolean hasRich ()
172   {
173     return m_aRich != null;
174   }
175 
176   @Nullable
177   public PSRichGroup getRich ()
178   {
179     return m_aRich;
180   }
181 
182   @Nullable
183   public PSRichGroup getRichClone ()
184   {
185     return m_aRich == null ? null : m_aRich.getClone ();
186   }
187 
188   public void addText (@Nonnull @Nonempty final String sText)
189   {
190     ValueEnforcer.notEmpty (sText, "Text");
191     m_aContent.add (sText);
192   }
193 
194   public boolean hasAnyText ()
195   {
196     for (final Object aElement : m_aContent)
197       if (aElement instanceof String)
198         return true;
199     return false;
200   }
201 
202   @Nonnull
203   @ReturnsMutableCopy
204   public List <String> getAllTexts ()
205   {
206     final List <String> ret = new ArrayList <String> ();
207     for (final Object aElement : m_aContent)
208       if (aElement instanceof String)
209         ret.add ((String) aElement);
210     return ret;
211   }
212 
213   public void addValueOf (@Nonnull final PSValueOf aValueOf)
214   {
215     ValueEnforcer.notNull (aValueOf, "ValueOf");
216     m_aContent.add (aValueOf);
217   }
218 
219   @Nonnull
220   @ReturnsMutableCopy
221   public List <PSValueOf> getAllValueOfs ()
222   {
223     final List <PSValueOf> ret = new ArrayList <PSValueOf> ();
224     for (final Object aElement : m_aContent)
225       if (aElement instanceof PSValueOf)
226         ret.add ((PSValueOf) aElement);
227     return ret;
228   }
229 
230   public void addEmph (@Nonnull final PSEmph aEmph)
231   {
232     ValueEnforcer.notNull (aEmph, "Emph");
233     m_aContent.add (aEmph);
234   }
235 
236   @Nonnull
237   @ReturnsMutableCopy
238   public List <PSEmph> getAllEmphs ()
239   {
240     final List <PSEmph> ret = new ArrayList <PSEmph> ();
241     for (final Object aElement : m_aContent)
242       if (aElement instanceof PSEmph)
243         ret.add ((PSEmph) aElement);
244     return ret;
245   }
246 
247   public void addDir (@Nonnull final PSDir aDir)
248   {
249     ValueEnforcer.notNull (aDir, "Dir");
250     m_aContent.add (aDir);
251   }
252 
253   @Nonnull
254   @ReturnsMutableCopy
255   public List <PSDir> getAllDirs ()
256   {
257     final List <PSDir> ret = new ArrayList <PSDir> ();
258     for (final Object aElement : m_aContent)
259       if (aElement instanceof PSDir)
260         ret.add ((PSDir) aElement);
261     return ret;
262   }
263 
264   public void addSpan (@Nonnull final PSSpan aSpan)
265   {
266     ValueEnforcer.notNull (aSpan, "Span");
267     m_aContent.add (aSpan);
268   }
269 
270   @Nonnull
271   @ReturnsMutableCopy
272   public List <PSSpan> getAllSpans ()
273   {
274     final List <PSSpan> ret = new ArrayList <PSSpan> ();
275     for (final Object aElement : m_aContent)
276       if (aElement instanceof PSSpan)
277         ret.add ((PSSpan) aElement);
278     return ret;
279   }
280 
281   /**
282    * @return A list of {@link String}, {@link PSValueOf}, {@link PSEmph},
283    *         {@link PSDir} and {@link PSSpan} elements.
284    */
285   @Nonnull
286   @ReturnsMutableCopy
287   public List <Object> getAllContentElements ()
288   {
289     return CollectionHelper.newList (m_aContent);
290   }
291 
292   @Nonnull
293   public IMicroElement getAsMicroElement ()
294   {
295     final IMicroElement ret = new MicroElement (CSchematron.NAMESPACE_SCHEMATRON, CSchematronXML.ELEMENT_DIAGNOSTIC);
296     ret.setAttribute (CSchematronXML.ATTR_ID, m_sID);
297     if (m_aRich != null)
298       m_aRich.fillMicroElement (ret);
299     if (m_aForeignElements != null)
300       for (final IMicroElement aForeignElement : m_aForeignElements)
301         ret.appendChild (aForeignElement.getClone ());
302     for (final Object aContent : m_aContent)
303       if (aContent instanceof String)
304         ret.appendText ((String) aContent);
305       else
306         ret.appendChild (((IPSElement) aContent).getAsMicroElement ());
307     if (m_aForeignAttrs != null)
308       for (final Map.Entry <String, String> aEntry : m_aForeignAttrs.entrySet ())
309         ret.setAttribute (aEntry.getKey (), aEntry.getValue ());
310     return ret;
311   }
312 
313   @Nonnull
314   public PSDiagnostic getClone ()
315   {
316     final PSDiagnostic ret = new PSDiagnostic ();
317     ret.setID (m_sID);
318     ret.setRich (getRichClone ());
319     for (final Object aContent : m_aContent)
320     {
321       if (aContent instanceof String)
322         ret.addText ((String) aContent);
323       else
324         if (aContent instanceof PSValueOf)
325           ret.addValueOf (((PSValueOf) aContent).getClone ());
326         else
327           if (aContent instanceof PSEmph)
328             ret.addEmph (((PSEmph) aContent).getClone ());
329           else
330             if (aContent instanceof PSDir)
331               ret.addDir (((PSDir) aContent).getClone ());
332             else
333               if (aContent instanceof PSSpan)
334                 ret.addSpan (((PSSpan) aContent).getClone ());
335     }
336     if (hasForeignElements ())
337       ret.addForeignElements (m_aForeignElements);
338     if (hasForeignAttributes ())
339       ret.addForeignAttributes (m_aForeignAttrs);
340     return ret;
341   }
342 
343   @Override
344   public String toString ()
345   {
346     return new ToStringGenerator (this).appendIfNotNull ("id", m_sID)
347                                        .appendIfNotNull ("rich", m_aRich)
348                                        .appendIfNotEmpty ("content", m_aContent)
349                                        .appendIfNotEmpty ("foreignAttrs", m_aForeignAttrs)
350                                        .appendIfNotEmpty ("foreignElements", m_aForeignElements)
351                                        .toString ();
352   }
353 }