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.exchange;
18  
19  import java.util.Map;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.annotation.concurrent.Immutable;
24  
25  import com.helger.commons.ValueEnforcer;
26  import com.helger.commons.io.resource.IReadableResource;
27  import com.helger.commons.microdom.IMicroDocument;
28  import com.helger.commons.microdom.IMicroElement;
29  import com.helger.commons.microdom.IMicroNode;
30  import com.helger.commons.microdom.IMicroQName;
31  import com.helger.commons.microdom.IMicroText;
32  import com.helger.commons.microdom.serialize.MicroWriter;
33  import com.helger.commons.string.StringParser;
34  import com.helger.commons.string.ToStringGenerator;
35  import com.helger.commons.xml.serialize.read.ISAXReaderSettings;
36  import com.helger.schematron.CSchematron;
37  import com.helger.schematron.CSchematronXML;
38  import com.helger.schematron.SchematronHelper;
39  import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
40  import com.helger.schematron.pure.errorhandler.LoggingPSErrorHandler;
41  import com.helger.schematron.pure.model.IPSElement;
42  import com.helger.schematron.pure.model.PSActive;
43  import com.helger.schematron.pure.model.PSAssertReport;
44  import com.helger.schematron.pure.model.PSDiagnostic;
45  import com.helger.schematron.pure.model.PSDiagnostics;
46  import com.helger.schematron.pure.model.PSDir;
47  import com.helger.schematron.pure.model.PSDir.EDirValue;
48  import com.helger.schematron.pure.model.PSEmph;
49  import com.helger.schematron.pure.model.PSExtends;
50  import com.helger.schematron.pure.model.PSInclude;
51  import com.helger.schematron.pure.model.PSLet;
52  import com.helger.schematron.pure.model.PSLinkableGroup;
53  import com.helger.schematron.pure.model.PSNS;
54  import com.helger.schematron.pure.model.PSName;
55  import com.helger.schematron.pure.model.PSP;
56  import com.helger.schematron.pure.model.PSParam;
57  import com.helger.schematron.pure.model.PSPattern;
58  import com.helger.schematron.pure.model.PSPhase;
59  import com.helger.schematron.pure.model.PSRichGroup;
60  import com.helger.schematron.pure.model.PSRichGroup.ESpace;
61  import com.helger.schematron.pure.model.PSRule;
62  import com.helger.schematron.pure.model.PSSchema;
63  import com.helger.schematron.pure.model.PSSpan;
64  import com.helger.schematron.pure.model.PSTitle;
65  import com.helger.schematron.pure.model.PSValueOf;
66  
67  /**
68   * Utility class for reading all Schematron elements from a resource.
69   *
70   * @author Philip Helger
71   */
72  @Immutable
73  public class PSReader
74  {
75    private final IReadableResource m_aResource;
76    private final IPSErrorHandler m_aErrorHandler;
77  
78    /**
79     * Constructor without an error handler
80     *
81     * @param aResource
82     *        The resource to read the Schematron from. May not be
83     *        <code>null</code>.
84     */
85    public PSReader (@Nonnull final IReadableResource aResource)
86    {
87      this (aResource, null);
88    }
89  
90    /**
91     * Constructor with an error handler
92     *
93     * @param aResource
94     *        The resource to read the Schematron from. May not be
95     *        <code>null</code>.
96     * @param aErrorHandler
97     *        The error handler to use. May be <code>null</code>. If the error
98     *        handler is <code>null</code> a {@link LoggingPSErrorHandler} is
99     *        automatically created and used.
100    */
101   public PSReader (@Nonnull final IReadableResource aResource, @Nullable final IPSErrorHandler aErrorHandler)
102   {
103     ValueEnforcer.notNull (aResource, "Resource");
104     m_aResource = aResource;
105     m_aErrorHandler = aErrorHandler != null ? aErrorHandler : new LoggingPSErrorHandler ();
106   }
107 
108   /**
109    * @return The resource from which the Schematron schema is read. Never
110    *         <code>null</code>.
111    */
112   @Nonnull
113   public IReadableResource getResource ()
114   {
115     return m_aResource;
116   }
117 
118   /**
119    * @return The error handler used. If no error handler was passed in the
120    *         constructor, than a {@link LoggingPSErrorHandler} is automatically
121    *         used.
122    */
123   @Nonnull
124   public IPSErrorHandler getErrorHandler ()
125   {
126     return m_aErrorHandler;
127   }
128 
129   /**
130    * Utility method to get a real attribute value, by trimming spaces, if the
131    * value is non-<code>null</code>.
132    *
133    * @param sAttrValue
134    *        The source attribute value. May be <code>null</code>.
135    * @return <code>null</code> if the input parameter is <code>null</code>.
136    */
137   @Nullable
138   private static String _getAttributeValue (@Nullable final String sAttrValue)
139   {
140     return sAttrValue == null ? null : sAttrValue.trim ();
141   }
142 
143   /**
144    * Emit a warning with the registered error handler.
145    *
146    * @param aSourceElement
147    *        The source element where the error occurred.
148    * @param sMessage
149    *        The main warning message.
150    */
151   private void _warn (@Nonnull final IPSElement aSourceElement, @Nonnull final String sMessage)
152   {
153     ValueEnforcer.notNull (aSourceElement, "SourceElement");
154     ValueEnforcer.notNull (sMessage, "Message");
155 
156     m_aErrorHandler.warn (m_aResource, aSourceElement, sMessage);
157   }
158 
159   /**
160    * Read an &lt;active&gt; element
161    *
162    * @param eActive
163    *        The source micro element. Never <code>null</code>.
164    * @return The created domain object. May not be <code>null</code>.
165    */
166   @Nonnull
167   public PSActive readActiveFromXML (@Nonnull final IMicroElement eActive)
168   {
169     final PSActive ret = new PSActive ();
170     final Map <IMicroQName, String> aAttrs = eActive.getAllQAttributes ();
171     if (aAttrs != null)
172       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
173       {
174         final String sAttrName = aEntry.getKey ().getName ();
175         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
176         if (sAttrName.equals (CSchematronXML.ATTR_PATTERN))
177           ret.setPattern (sAttrValue);
178         else
179           ret.addForeignAttribute (sAttrName, sAttrValue);
180       }
181 
182     if (eActive.hasChildren ())
183       for (final IMicroNode aActiveChild : eActive.getAllChildren ())
184         switch (aActiveChild.getType ())
185         {
186           case TEXT:
187             ret.addText (((IMicroText) aActiveChild).getNodeValue ());
188             break;
189           case ELEMENT:
190             final IMicroElement eElement = (IMicroElement) aActiveChild;
191             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
192             {
193               final String sLocalName = eElement.getLocalName ();
194               if (sLocalName.equals (CSchematronXML.ELEMENT_DIR))
195                 ret.addDir (readDirFromXML (eElement));
196               else
197                 if (sLocalName.equals (CSchematronXML.ELEMENT_EMPH))
198                   ret.addEmph (readEmphFromXML (eElement));
199                 else
200                   if (sLocalName.equals (CSchematronXML.ELEMENT_SPAN))
201                     ret.addSpan (readSpanFromXML (eElement));
202                   else
203                     _warn (ret, "Unsupported Schematron element '" + sLocalName + "'");
204             }
205             else
206               ret.addForeignElement (eElement.getClone ());
207 
208             break;
209           case COMMENT:
210             // Ignore comments
211             break;
212           default:
213             _warn (ret, "Unsupported child node: " + aActiveChild);
214         }
215     return ret;
216   }
217 
218   /**
219    * Read an &lt;assert&gt; or a &lt;report&gt; element
220    *
221    * @param eAssertReport
222    *        The source micro element. Never <code>null</code>.
223    * @return The created domain object. May not be <code>null</code>.
224    */
225   @Nonnull
226   public PSAssertReport readAssertReportFromXML (@Nonnull final IMicroElement eAssertReport)
227   {
228     final PSAssertReport ret = new PSAssertReport (eAssertReport.getLocalName ()
229                                                                 .equals (CSchematronXML.ELEMENT_ASSERT));
230     final Map <IMicroQName, String> aAttrs = eAssertReport.getAllQAttributes ();
231     if (aAttrs != null)
232     {
233       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
234       {
235         final String sAttrName = aEntry.getKey ().getName ();
236         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
237         if (sAttrName.equals (CSchematronXML.ATTR_TEST))
238           ret.setTest (sAttrValue);
239         else
240           if (sAttrName.equals (CSchematronXML.ATTR_FLAG))
241             ret.setFlag (sAttrValue);
242           else
243             if (sAttrName.equals (CSchematronXML.ATTR_ID))
244               ret.setID (sAttrValue);
245             else
246               if (sAttrName.equals (CSchematronXML.ATTR_DIAGNOSTICS))
247                 ret.setDiagnostics (sAttrValue);
248               else
249                 if (!PSRichGroup.isRichAttribute (sAttrName) && !PSLinkableGroup.isLinkableAttribute (sAttrName))
250                   ret.addForeignAttribute (sAttrName, sAttrValue);
251       }
252       ret.setRich (readRichGroupFromXML (aAttrs));
253       ret.setLinkable (readLinkableGroupFromXML (aAttrs));
254     }
255 
256     if (eAssertReport.hasChildren ())
257       for (final IMicroNode aAssertReportChild : eAssertReport.getAllChildren ())
258         switch (aAssertReportChild.getType ())
259         {
260           case TEXT:
261             ret.addText (((IMicroText) aAssertReportChild).getNodeValue ());
262             break;
263           case ELEMENT:
264             final IMicroElement eElement = (IMicroElement) aAssertReportChild;
265             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
266             {
267               final String sLocalName = eElement.getLocalName ();
268               if (sLocalName.equals (CSchematronXML.ELEMENT_NAME))
269                 ret.addName (readNameFromXML (eElement));
270               else
271                 if (sLocalName.equals (CSchematronXML.ELEMENT_VALUE_OF))
272                   ret.addValueOf (readValueOfFromXML (eElement));
273                 else
274                   if (sLocalName.equals (CSchematronXML.ELEMENT_EMPH))
275                     ret.addEmph (readEmphFromXML (eElement));
276                   else
277                     if (sLocalName.equals (CSchematronXML.ELEMENT_DIR))
278                       ret.addDir (readDirFromXML (eElement));
279                     else
280                       if (sLocalName.equals (CSchematronXML.ELEMENT_SPAN))
281                         ret.addSpan (readSpanFromXML (eElement));
282                       else
283                         _warn (ret, "Unsupported Schematron element '" + sLocalName + "'");
284             }
285             else
286               ret.addForeignElement (eElement.getClone ());
287 
288             break;
289           case COMMENT:
290             // Ignore comments
291             break;
292           default:
293             _warn (ret, "Unsupported child node: " + aAssertReportChild);
294         }
295     return ret;
296   }
297 
298   /**
299    * Read a &lt;diagnostic&gt; element
300    *
301    * @param eDiagnostic
302    *        The source micro element. Never <code>null</code>.
303    * @return The created domain object. May not be <code>null</code>.
304    */
305   @Nonnull
306   public PSDiagnostic readDiagnosticFromXML (@Nonnull final IMicroElement eDiagnostic)
307   {
308     final PSDiagnostic ret = new PSDiagnostic ();
309     final Map <IMicroQName, String> aAttrs = eDiagnostic.getAllQAttributes ();
310     if (aAttrs != null)
311     {
312       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
313       {
314         final String sAttrName = aEntry.getKey ().getName ();
315         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
316         if (sAttrName.equals (CSchematronXML.ATTR_ID))
317           ret.setID (sAttrValue);
318         else
319           if (!PSRichGroup.isRichAttribute (sAttrName))
320             ret.addForeignAttribute (sAttrName, sAttrValue);
321       }
322       ret.setRich (readRichGroupFromXML (aAttrs));
323     }
324 
325     if (eDiagnostic.hasChildren ())
326       for (final IMicroNode aDiagnosticChild : eDiagnostic.getAllChildren ())
327         switch (aDiagnosticChild.getType ())
328         {
329           case TEXT:
330             ret.addText (((IMicroText) aDiagnosticChild).getNodeValue ());
331             break;
332           case ELEMENT:
333             final IMicroElement eElement = (IMicroElement) aDiagnosticChild;
334             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
335             {
336               final String sLocalName = eElement.getLocalName ();
337               if (sLocalName.equals (CSchematronXML.ELEMENT_VALUE_OF))
338                 ret.addValueOf (readValueOfFromXML (eElement));
339               else
340                 if (sLocalName.equals (CSchematronXML.ELEMENT_EMPH))
341                   ret.addEmph (readEmphFromXML (eElement));
342                 else
343                   if (sLocalName.equals (CSchematronXML.ELEMENT_DIR))
344                     ret.addDir (readDirFromXML (eElement));
345                   else
346                     if (sLocalName.equals (CSchematronXML.ELEMENT_SPAN))
347                       ret.addSpan (readSpanFromXML (eElement));
348                     else
349                       _warn (ret, "Unsupported Schematron element '" + sLocalName + "'");
350             }
351             else
352               ret.addForeignElement (eElement.getClone ());
353 
354             break;
355           case COMMENT:
356             // Ignore comments
357             break;
358           default:
359             _warn (ret, "Unsupported child node: " + aDiagnosticChild);
360         }
361     return ret;
362   }
363 
364   /**
365    * Read a &lt;diagnostics&gt; element
366    *
367    * @param eDiagnostics
368    *        The source micro element. Never <code>null</code>.
369    * @return The created domain object. May not be <code>null</code>.
370    */
371   @Nonnull
372   public PSDiagnostics readDiagnosticsFromXML (@Nonnull final IMicroElement eDiagnostics)
373   {
374     final PSDiagnostics ret = new PSDiagnostics ();
375 
376     final Map <IMicroQName, String> aAttrs = eDiagnostics.getAllQAttributes ();
377     if (aAttrs != null)
378       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
379       {
380         final String sAttrName = aEntry.getKey ().getName ();
381         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
382         ret.addForeignAttribute (sAttrName, sAttrValue);
383       }
384 
385     for (final IMicroElement eDiagnosticsChild : eDiagnostics.getAllChildElements ())
386     {
387       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eDiagnosticsChild.getNamespaceURI ()))
388       {
389         if (eDiagnosticsChild.getLocalName ().equals (CSchematronXML.ELEMENT_INCLUDE))
390           ret.addInclude (readIncludeFromXML (eDiagnosticsChild));
391         else
392           if (eDiagnosticsChild.getLocalName ().equals (CSchematronXML.ELEMENT_DIAGNOSTIC))
393             ret.addDiagnostic (readDiagnosticFromXML (eDiagnosticsChild));
394           else
395             _warn (ret, "Unsupported Schematron element '" + eDiagnosticsChild.getLocalName () + "'");
396       }
397       else
398         ret.addForeignElement (eDiagnosticsChild.getClone ());
399     }
400     return ret;
401   }
402 
403   /**
404    * Read a &lt;dir&gt; element
405    *
406    * @param eDir
407    *        The source micro element. Never <code>null</code>.
408    * @return The created domain object. May not be <code>null</code>.
409    */
410   @Nonnull
411   public PSDir readDirFromXML (@Nonnull final IMicroElement eDir)
412   {
413     final PSDir ret = new PSDir ();
414     final Map <IMicroQName, String> aAttrs = eDir.getAllQAttributes ();
415     if (aAttrs != null)
416       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
417       {
418         final String sAttrName = aEntry.getKey ().getName ();
419         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
420         if (sAttrName.equals (CSchematronXML.ATTR_VALUE))
421           ret.setValue (EDirValue.getFromIDOrNull (sAttrValue));
422         else
423           ret.addForeignAttribute (sAttrName, sAttrValue);
424       }
425 
426     if (eDir.hasChildren ())
427       for (final IMicroNode aDirChild : eDir.getAllChildren ())
428         switch (aDirChild.getType ())
429         {
430           case TEXT:
431             ret.addText (((IMicroText) aDirChild).getNodeValue ());
432             break;
433           case ELEMENT:
434             final IMicroElement eElement = (IMicroElement) aDirChild;
435             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
436             {
437               _warn (ret, "Unsupported Schematron element '" + eElement.getLocalName () + "'");
438             }
439             else
440               ret.addForeignElement (eElement.getClone ());
441 
442             break;
443           case COMMENT:
444             // Ignore comments
445             break;
446           default:
447             _warn (ret, "Unsupported child node: " + aDirChild);
448         }
449     return ret;
450   }
451 
452   /**
453    * Read an &lt;emph&gt; element
454    *
455    * @param eEmph
456    *        The source micro element. Never <code>null</code>.
457    * @return The created domain object. May not be <code>null</code>.
458    */
459   @Nonnull
460   public PSEmph readEmphFromXML (@Nonnull final IMicroElement eEmph)
461   {
462     final PSEmph ret = new PSEmph ();
463     final Map <IMicroQName, String> aAttrs = eEmph.getAllQAttributes ();
464     if (aAttrs != null)
465       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
466       {
467         final String sAttrName = aEntry.getKey ().getName ();
468         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
469         _warn (ret, "Unsupported attribute '" + sAttrName + "'='" + sAttrValue + "'");
470       }
471 
472     if (eEmph.hasChildren ())
473       for (final IMicroNode aEmphChild : eEmph.getAllChildren ())
474         switch (aEmphChild.getType ())
475         {
476           case TEXT:
477             ret.addText (((IMicroText) aEmphChild).getNodeValue ());
478             break;
479           case ELEMENT:
480             final IMicroElement eElement = (IMicroElement) aEmphChild;
481             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
482             {
483               _warn (ret, "Unsupported Schematron element '" + eElement.getLocalName () + "'");
484             }
485             else
486               _warn (ret, "Unsupported namespace URI '" + eElement.getNamespaceURI () + "'");
487 
488             break;
489           case COMMENT:
490             // Ignore comments
491             break;
492           default:
493             _warn (ret, "Unsupported child node: " + aEmphChild);
494         }
495     return ret;
496   }
497 
498   /**
499    * Read an &lt;extends&gt; element
500    *
501    * @param eExtends
502    *        The source micro element. Never <code>null</code>.
503    * @return The created domain object. May not be <code>null</code>.
504    */
505   @Nonnull
506   public PSExtends readExtendsFromXML (@Nonnull final IMicroElement eExtends)
507   {
508     final PSExtends ret = new PSExtends ();
509     final Map <IMicroQName, String> aAttrs = eExtends.getAllQAttributes ();
510     if (aAttrs != null)
511       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
512       {
513         final String sAttrName = aEntry.getKey ().getName ();
514         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
515         if (sAttrName.equals (CSchematronXML.ATTR_RULE))
516           ret.setRule (sAttrValue);
517         else
518           ret.addForeignAttribute (sAttrName, sAttrValue);
519       }
520 
521     for (final IMicroElement eChild : eExtends.getAllChildElements ())
522     {
523       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eChild.getNamespaceURI ()))
524       {
525         _warn (ret, "Unsupported Schematron element '" + eChild.getLocalName () + "'");
526       }
527       else
528         _warn (ret, "Unsupported namespace URI '" + eChild.getNamespaceURI () + "'");
529     }
530     return ret;
531   }
532 
533   /**
534    * Read an &lt;include&gt; element
535    *
536    * @param eInclude
537    *        The source micro element. Never <code>null</code>.
538    * @return The created domain object. May not be <code>null</code>.
539    */
540   @Nonnull
541   public PSInclude readIncludeFromXML (@Nonnull final IMicroElement eInclude)
542   {
543     final PSInclude ret = new PSInclude ();
544     final Map <IMicroQName, String> aAttrs = eInclude.getAllQAttributes ();
545     if (aAttrs != null)
546       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
547       {
548         final String sAttrName = aEntry.getKey ().getName ();
549         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
550         if (sAttrName.equals (CSchematronXML.ATTR_HREF))
551           ret.setHref (sAttrValue);
552         else
553           _warn (ret, "Unsupported attribute '" + sAttrName + "'='" + sAttrValue + "'");
554       }
555 
556     for (final IMicroElement eValueOfChild : eInclude.getAllChildElements ())
557     {
558       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eValueOfChild.getNamespaceURI ()))
559       {
560         _warn (ret, "Unsupported Schematron element '" + eValueOfChild.getLocalName () + "'");
561       }
562       else
563         _warn (ret, "Unsupported namespace URI '" + eValueOfChild.getNamespaceURI () + "'");
564     }
565     return ret;
566   }
567 
568   /**
569    * Read a &lt;let&gt; element
570    *
571    * @param eLet
572    *        The source micro element. Never <code>null</code>.
573    * @return The created domain object. May not be <code>null</code>.
574    */
575   @Nonnull
576   public PSLet readLetFromXML (@Nonnull final IMicroElement eLet)
577   {
578     final PSLet ret = new PSLet ();
579     final Map <IMicroQName, String> aAttrs = eLet.getAllQAttributes ();
580     if (aAttrs != null)
581       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
582       {
583         final String sAttrName = aEntry.getKey ().getName ();
584         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
585         if (sAttrName.equals (CSchematronXML.ATTR_NAME))
586           ret.setName (sAttrValue);
587         else
588           if (sAttrName.equals (CSchematronXML.ATTR_VALUE))
589             ret.setValue (sAttrValue);
590           else
591             _warn (ret, "Unsupported attribute '" + sAttrName + "'='" + sAttrValue + "'");
592       }
593 
594     for (final IMicroElement eLetChild : eLet.getAllChildElements ())
595     {
596       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eLetChild.getNamespaceURI ()))
597       {
598         _warn (ret, "Unsupported Schematron element '" + eLetChild.getLocalName () + "'");
599       }
600       else
601         _warn (ret, "Unsupported namespace URI '" + eLetChild.getNamespaceURI () + "'");
602     }
603     return ret;
604   }
605 
606   /**
607    * Read all attributes for a linkable group
608    *
609    * @param aAttrs
610    *        The attributes of a micro element. May be <code>null</code>.
611    * @return The created domain object. May not be <code>null</code>.
612    */
613   @Nonnull
614   public PSLinkableGroup readLinkableGroupFromXML (@Nullable final Map <IMicroQName, String> aAttrs)
615   {
616     final PSLinkableGroup ret = new PSLinkableGroup ();
617     if (aAttrs != null)
618       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
619       {
620         final String sAttrName = aEntry.getKey ().getName ();
621         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
622         if (sAttrName.equals (CSchematronXML.ATTR_ROLE))
623           ret.setRole (sAttrValue);
624         else
625           if (sAttrName.equals (CSchematronXML.ATTR_SUBJECT))
626             ret.setSubject (sAttrValue);
627       }
628     return ret;
629   }
630 
631   /**
632    * Read a &lt;name&gt; element
633    *
634    * @param eName
635    *        The source micro element. Never <code>null</code>.
636    * @return The created domain object. May not be <code>null</code>.
637    */
638   @Nonnull
639   public PSName readNameFromXML (@Nonnull final IMicroElement eName)
640   {
641     final PSName ret = new PSName ();
642     final Map <IMicroQName, String> aAttrs = eName.getAllQAttributes ();
643     if (aAttrs != null)
644       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
645       {
646         final String sAttrName = aEntry.getKey ().getName ();
647         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
648         if (sAttrName.equals (CSchematronXML.ATTR_PATH))
649           ret.setPath (sAttrValue);
650         else
651           ret.addForeignAttribute (sAttrName, sAttrValue);
652       }
653 
654     for (final IMicroElement eNameChild : eName.getAllChildElements ())
655     {
656       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eNameChild.getNamespaceURI ()))
657       {
658         _warn (ret, "Unsupported Schematron element '" + eNameChild.getLocalName () + "'");
659       }
660       else
661         _warn (ret, "Unsupported namespace URI '" + eNameChild.getNamespaceURI () + "'");
662     }
663     return ret;
664   }
665 
666   /**
667    * Read a &lt;ns&gt; element
668    *
669    * @param eNS
670    *        The source micro element. Never <code>null</code>.
671    * @return The created domain object. May not be <code>null</code>.
672    */
673   @Nonnull
674   public PSNS readNSFromXML (@Nonnull final IMicroElement eNS)
675   {
676     final PSNS ret = new PSNS ();
677     final Map <IMicroQName, String> aAttrs = eNS.getAllQAttributes ();
678     if (aAttrs != null)
679       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
680       {
681         final String sAttrName = aEntry.getKey ().getName ();
682         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
683         if (sAttrName.equals (CSchematronXML.ATTR_URI))
684           ret.setUri (sAttrValue);
685         else
686           if (sAttrName.equals (CSchematronXML.ATTR_PREFIX))
687             ret.setPrefix (sAttrValue);
688           else
689             ret.addForeignAttribute (sAttrName, sAttrValue);
690       }
691 
692     for (final IMicroElement eLetChild : eNS.getAllChildElements ())
693     {
694       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eLetChild.getNamespaceURI ()))
695       {
696         _warn (ret, "Unsupported Schematron element '" + eLetChild.getLocalName () + "'");
697       }
698       else
699         _warn (ret, "Unsupported namespace URI '" + eLetChild.getNamespaceURI () + "'");
700     }
701     return ret;
702   }
703 
704   /**
705    * Read a &lt;p&gt; element
706    *
707    * @param eP
708    *        The source micro element. Never <code>null</code>.
709    * @return The created domain object. May not be <code>null</code>.
710    */
711   @Nonnull
712   public PSP readPFromXML (@Nonnull final IMicroElement eP)
713   {
714     final PSP ret = new PSP ();
715     final Map <IMicroQName, String> aAttrs = eP.getAllQAttributes ();
716     if (aAttrs != null)
717       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
718       {
719         final String sAttrName = aEntry.getKey ().getName ();
720         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
721         if (sAttrName.equals (CSchematronXML.ATTR_ID))
722           ret.setID (sAttrValue);
723         else
724           if (sAttrName.equals (CSchematronXML.ATTR_CLASS))
725             ret.setClazz (sAttrValue);
726           else
727             if (sAttrName.equals (CSchematronXML.ATTR_ICON))
728               ret.setIcon (sAttrValue);
729             else
730               ret.addForeignAttribute (sAttrName, sAttrValue);
731       }
732 
733     if (eP.hasChildren ())
734       for (final IMicroNode aChild : eP.getAllChildren ())
735         switch (aChild.getType ())
736         {
737           case TEXT:
738             ret.addText (((IMicroText) aChild).getNodeValue ());
739             break;
740           case ELEMENT:
741             final IMicroElement eElement = (IMicroElement) aChild;
742             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
743             {
744               final String sLocalName = eElement.getLocalName ();
745               if (sLocalName.equals (CSchematronXML.ELEMENT_DIR))
746                 ret.addDir (readDirFromXML (eElement));
747               else
748                 if (sLocalName.equals (CSchematronXML.ELEMENT_EMPH))
749                   ret.addEmph (readEmphFromXML (eElement));
750                 else
751                   if (sLocalName.equals (CSchematronXML.ELEMENT_SPAN))
752                     ret.addSpan (readSpanFromXML (eElement));
753                   else
754                     _warn (ret, "Unsupported Schematron element '" + sLocalName + "'");
755             }
756             else
757               ret.addForeignElement (eElement.getClone ());
758 
759             break;
760           case COMMENT:
761             // Ignore comments
762             break;
763           default:
764             _warn (ret, "Unsupported child node: " + aChild);
765         }
766     return ret;
767   }
768 
769   /**
770    * Read a &lt;param&gt; element
771    *
772    * @param eParam
773    *        The source micro element. Never <code>null</code>.
774    * @return The created domain object. May not be <code>null</code>.
775    */
776   @Nonnull
777   public PSParam readParamFromXML (@Nonnull final IMicroElement eParam)
778   {
779     final PSParam ret = new PSParam ();
780     final Map <IMicroQName, String> aAttrs = eParam.getAllQAttributes ();
781     if (aAttrs != null)
782       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
783       {
784         final String sAttrName = aEntry.getKey ().getName ();
785         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
786         if (sAttrName.equals (CSchematronXML.ATTR_NAME))
787           ret.setName (sAttrValue);
788         else
789           if (sAttrName.equals (CSchematronXML.ATTR_VALUE))
790             ret.setValue (sAttrValue);
791           else
792             _warn (ret, "Unsupported attribute '" + sAttrName + "'='" + sAttrValue + "'");
793       }
794 
795     for (final IMicroElement eParamChild : eParam.getAllChildElements ())
796     {
797       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eParamChild.getNamespaceURI ()))
798       {
799         _warn (ret, "Unsupported Schematron element '" + eParamChild.getLocalName () + "'");
800       }
801       else
802         _warn (ret, "Unsupported namespace URI '" + eParamChild.getNamespaceURI () + "'");
803     }
804     return ret;
805   }
806 
807   /**
808    * Read a &lt;pattern&gt; element
809    *
810    * @param ePattern
811    *        The source micro element. Never <code>null</code>.
812    * @return The created domain object. May not be <code>null</code>.
813    */
814   @Nonnull
815   public PSPattern readPatternFromXML (@Nonnull final IMicroElement ePattern)
816   {
817     final PSPattern ret = new PSPattern ();
818     final Map <IMicroQName, String> aAttrs = ePattern.getAllQAttributes ();
819     if (aAttrs != null)
820     {
821       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
822       {
823         final String sAttrName = aEntry.getKey ().getName ();
824         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
825         if (sAttrName.equals (CSchematronXML.ATTR_ABSTRACT))
826           ret.setAbstract (StringParser.parseBool (sAttrValue));
827         else
828           if (sAttrName.equals (CSchematronXML.ATTR_ID))
829             ret.setID (sAttrValue);
830           else
831             if (sAttrName.equals (CSchematronXML.ATTR_IS_A))
832               ret.setIsA (sAttrValue);
833             else
834               if (!PSRichGroup.isRichAttribute (sAttrName))
835                 ret.addForeignAttribute (sAttrName, sAttrValue);
836       }
837       ret.setRich (readRichGroupFromXML (aAttrs));
838     }
839 
840     for (final IMicroElement ePatternChild : ePattern.getAllChildElements ())
841     {
842       if (CSchematron.NAMESPACE_SCHEMATRON.equals (ePatternChild.getNamespaceURI ()))
843       {
844         if (ePatternChild.getLocalName ().equals (CSchematronXML.ELEMENT_INCLUDE))
845           ret.addInclude (readIncludeFromXML (ePatternChild));
846         else
847           if (ePatternChild.getLocalName ().equals (CSchematronXML.ELEMENT_TITLE))
848             ret.setTitle (readTitleFromXML (ePatternChild));
849           else
850             if (ePatternChild.getLocalName ().equals (CSchematronXML.ELEMENT_P))
851               ret.addP (readPFromXML (ePatternChild));
852             else
853               if (ePatternChild.getLocalName ().equals (CSchematronXML.ELEMENT_LET))
854                 ret.addLet (readLetFromXML (ePatternChild));
855               else
856                 if (ePatternChild.getLocalName ().equals (CSchematronXML.ELEMENT_RULE))
857                   ret.addRule (readRuleFromXML (ePatternChild));
858                 else
859                   if (ePatternChild.getLocalName ().equals (CSchematronXML.ELEMENT_PARAM))
860                     ret.addParam (readParamFromXML (ePatternChild));
861                   else
862                     _warn (ret, "Unsupported Schematron element '" +
863                                 ePatternChild.getLocalName () +
864                                 "' in " +
865                                 ret.toString ());
866       }
867       else
868         ret.addForeignElement (ePatternChild.getClone ());
869     }
870     return ret;
871   }
872 
873   /**
874    * Read a &lt;phase&gt; element
875    *
876    * @param ePhase
877    *        The source micro element. Never <code>null</code>.
878    * @return The created domain object. May not be <code>null</code>.
879    */
880   @Nonnull
881   public PSPhase readPhaseFromXML (@Nonnull final IMicroElement ePhase)
882   {
883     final PSPhase ret = new PSPhase ();
884     final Map <IMicroQName, String> aAttrs = ePhase.getAllQAttributes ();
885     if (aAttrs != null)
886     {
887       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
888       {
889         final String sAttrName = aEntry.getKey ().getName ();
890         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
891         if (sAttrName.equals (CSchematronXML.ATTR_ID))
892           ret.setID (sAttrValue);
893         else
894           if (!PSRichGroup.isRichAttribute (sAttrName))
895             ret.addForeignAttribute (sAttrName, sAttrValue);
896       }
897       ret.setRich (readRichGroupFromXML (aAttrs));
898     }
899 
900     for (final IMicroElement ePhaseChild : ePhase.getAllChildElements ())
901     {
902       if (CSchematron.NAMESPACE_SCHEMATRON.equals (ePhaseChild.getNamespaceURI ()))
903       {
904         if (ePhaseChild.getLocalName ().equals (CSchematronXML.ELEMENT_INCLUDE))
905           ret.addInclude (readIncludeFromXML (ePhaseChild));
906         else
907           if (ePhaseChild.getLocalName ().equals (CSchematronXML.ELEMENT_P))
908             ret.addP (readPFromXML (ePhaseChild));
909           else
910             if (ePhaseChild.getLocalName ().equals (CSchematronXML.ELEMENT_LET))
911               ret.addLet (readLetFromXML (ePhaseChild));
912             else
913               if (ePhaseChild.getLocalName ().equals (CSchematronXML.ELEMENT_ACTIVE))
914                 ret.addActive (readActiveFromXML (ePhaseChild));
915               else
916                 _warn (ret, "Unsupported Schematron element '" + ePhaseChild.getLocalName () + "'");
917       }
918       else
919         ret.addForeignElement (ePhaseChild.getClone ());
920     }
921     return ret;
922   }
923 
924   /**
925    * Read all attributes that make up a rich group
926    *
927    * @param aAttrs
928    *        The attributes of a micro element. May be <code>null</code>.
929    * @return The created domain object. May not be <code>null</code>.
930    */
931   @Nonnull
932   public PSRichGroup readRichGroupFromXML (@Nullable final Map <IMicroQName, String> aAttrs)
933   {
934     final PSRichGroup ret = new PSRichGroup ();
935     if (aAttrs != null)
936       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
937       {
938         final String sAttrName = aEntry.getKey ().getName ();
939         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
940         if (sAttrName.equals (CSchematronXML.ATTR_ICON))
941           ret.setIcon (sAttrValue);
942         else
943           if (sAttrName.equals (CSchematronXML.ATTR_SEE))
944             ret.setSee (sAttrValue);
945           else
946             if (sAttrName.equals (CSchematronXML.ATTR_FPI))
947               ret.setFPI (sAttrValue);
948             else
949               if (sAttrName.equals (CSchematronXML.ATTR_XML_LANG))
950                 ret.setXmlLang (sAttrValue);
951               else
952                 if (sAttrName.equals (CSchematronXML.ATTR_XML_SPACE))
953                   ret.setXmlSpace (ESpace.getFromIDOrNull (sAttrValue));
954 
955       }
956     return ret;
957   }
958 
959   /**
960    * Read a &lt;rule&gt; element
961    *
962    * @param eRule
963    *        The source micro element. Never <code>null</code>.
964    * @return The created domain object. May not be <code>null</code>.
965    */
966   @Nonnull
967   public PSRule readRuleFromXML (@Nonnull final IMicroElement eRule)
968   {
969     final PSRule ret = new PSRule ();
970     final Map <IMicroQName, String> aAttrs = eRule.getAllQAttributes ();
971     if (aAttrs != null)
972     {
973       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
974       {
975         final String sAttrName = aEntry.getKey ().getName ();
976         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
977         if (sAttrName.equals (CSchematronXML.ATTR_FLAG))
978           ret.setFlag (sAttrValue);
979         else
980           if (sAttrName.equals (CSchematronXML.ATTR_ABSTRACT))
981             ret.setAbstract (StringParser.parseBool (sAttrValue));
982           else
983             if (sAttrName.equals (CSchematronXML.ATTR_CONTEXT))
984               ret.setContext (sAttrValue);
985             else
986               if (sAttrName.equals (CSchematronXML.ATTR_ID))
987                 ret.setID (sAttrValue);
988               else
989                 if (!PSRichGroup.isRichAttribute (sAttrName) && !PSLinkableGroup.isLinkableAttribute (sAttrName))
990                   ret.addForeignAttribute (sAttrName, sAttrValue);
991       }
992       ret.setRich (readRichGroupFromXML (aAttrs));
993       ret.setLinkable (readLinkableGroupFromXML (aAttrs));
994     }
995 
996     for (final IMicroElement eRuleChild : eRule.getAllChildElements ())
997     {
998       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eRuleChild.getNamespaceURI ()))
999       {
1000         if (eRuleChild.getLocalName ().equals (CSchematronXML.ELEMENT_INCLUDE))
1001           ret.addInclude (readIncludeFromXML (eRuleChild));
1002         else
1003           if (eRuleChild.getLocalName ().equals (CSchematronXML.ELEMENT_LET))
1004             ret.addLet (readLetFromXML (eRuleChild));
1005           else
1006             if (eRuleChild.getLocalName ().equals (CSchematronXML.ELEMENT_ASSERT) ||
1007                 eRuleChild.getLocalName ().equals (CSchematronXML.ELEMENT_REPORT))
1008               ret.addAssertReport (readAssertReportFromXML (eRuleChild));
1009             else
1010               if (eRuleChild.getLocalName ().equals (CSchematronXML.ELEMENT_EXTENDS))
1011                 ret.addExtends (readExtendsFromXML (eRuleChild));
1012               else
1013                 _warn (ret, "Unsupported Schematron element '" + eRuleChild.getLocalName () + "'");
1014       }
1015       else
1016         ret.addForeignElement (eRuleChild.getClone ());
1017     }
1018     return ret;
1019   }
1020 
1021   /**
1022    * Parse the Schematron into a pure Java object. This method makes no
1023    * assumptions on the validity of the document!
1024    *
1025    * @param eSchema
1026    *        The XML element to use. May not be <code>null</code>.
1027    * @return The created {@link PSSchema} object or <code>null</code> in case of
1028    *         <code>null</code> document or a fatal error.
1029    * @throws SchematronReadException
1030    *         If reading fails
1031    */
1032   @Nonnull
1033   public PSSchema readSchemaFromXML (@Nonnull final IMicroElement eSchema) throws SchematronReadException
1034   {
1035     ValueEnforcer.notNull (eSchema, "Schema");
1036     if (!CSchematron.NAMESPACE_SCHEMATRON.equals (eSchema.getNamespaceURI ()))
1037       throw new SchematronReadException (m_aResource, "The passed element is not an ISO Schematron element!");
1038 
1039     final PSSchema ret = new PSSchema (m_aResource);
1040     final Map <IMicroQName, String> aAttrs = eSchema.getAllQAttributes ();
1041     {
1042       if (aAttrs != null)
1043         for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
1044         {
1045           final String sAttrName = aEntry.getKey ().getName ();
1046           final String sAttrValue = _getAttributeValue (aEntry.getValue ());
1047           if (sAttrName.equals (CSchematronXML.ATTR_ID))
1048             ret.setID (sAttrValue);
1049           else
1050             if (sAttrName.equals (CSchematronXML.ATTR_SCHEMA_VERSION))
1051               ret.setSchemaVersion (sAttrValue);
1052             else
1053               if (sAttrName.equals (CSchematronXML.ATTR_DEFAULT_PHASE))
1054                 ret.setDefaultPhase (sAttrValue);
1055               else
1056                 if (sAttrName.equals (CSchematronXML.ATTR_QUERY_BINDING))
1057                   ret.setQueryBinding (sAttrValue);
1058                 else
1059                   if (!PSRichGroup.isRichAttribute (sAttrName))
1060                     ret.addForeignAttribute (sAttrName, sAttrValue);
1061         }
1062       ret.setRich (readRichGroupFromXML (aAttrs));
1063     }
1064 
1065     for (final IMicroElement eSchemaChild : eSchema.getAllChildElements ())
1066     {
1067       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eSchemaChild.getNamespaceURI ()))
1068       {
1069         if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_INCLUDE))
1070           ret.addInclude (readIncludeFromXML (eSchemaChild));
1071         else
1072           if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_TITLE))
1073             ret.setTitle (readTitleFromXML (eSchemaChild));
1074           else
1075             if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_NS))
1076               ret.addNS (readNSFromXML (eSchemaChild));
1077             else
1078               if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_P))
1079               {
1080                 final PSP aP = readPFromXML (eSchemaChild);
1081                 if (ret.hasNoPatterns ())
1082                   ret.addStartP (aP);
1083                 else
1084                   ret.addEndP (aP);
1085               }
1086               else
1087                 if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_LET))
1088                   ret.addLet (readLetFromXML (eSchemaChild));
1089                 else
1090                   if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_PHASE))
1091                     ret.addPhase (readPhaseFromXML (eSchemaChild));
1092                   else
1093                     if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_PATTERN))
1094                       ret.addPattern (readPatternFromXML (eSchemaChild));
1095                     else
1096                       if (eSchemaChild.getLocalName ().equals (CSchematronXML.ELEMENT_DIAGNOSTICS))
1097                         ret.setDiagnostics (readDiagnosticsFromXML (eSchemaChild));
1098                       else
1099                         _warn (ret, "Unsupported Schematron element '" + eSchemaChild.getLocalName () + "'");
1100       }
1101       else
1102         ret.addForeignElement (eSchemaChild.getClone ());
1103     }
1104     return ret;
1105   }
1106 
1107   /**
1108    * Read a &lt;span&gt; element
1109    *
1110    * @param eSpan
1111    *        The source micro element. Never <code>null</code>.
1112    * @return The created domain object. May not be <code>null</code>.
1113    */
1114   @Nonnull
1115   public PSSpan readSpanFromXML (@Nonnull final IMicroElement eSpan)
1116   {
1117     final PSSpan ret = new PSSpan ();
1118     final Map <IMicroQName, String> aAttrs = eSpan.getAllQAttributes ();
1119     if (aAttrs != null)
1120       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
1121       {
1122         final String sAttrName = aEntry.getKey ().getName ();
1123         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
1124         if (sAttrName.equals (CSchematronXML.ATTR_CLASS))
1125           ret.setClazz (sAttrValue);
1126         else
1127           ret.addForeignAttribute (sAttrName, sAttrValue);
1128       }
1129 
1130     if (eSpan.hasChildren ())
1131       for (final IMicroNode aSpanChild : eSpan.getAllChildren ())
1132         switch (aSpanChild.getType ())
1133         {
1134           case TEXT:
1135             ret.addText (((IMicroText) aSpanChild).getNodeValue ());
1136             break;
1137           case ELEMENT:
1138             final IMicroElement eElement = (IMicroElement) aSpanChild;
1139             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
1140             {
1141               _warn (ret, "Unsupported Schematron element '" + eElement.getLocalName () + "'");
1142             }
1143             else
1144               ret.addForeignElement (eElement.getClone ());
1145 
1146             break;
1147           case COMMENT:
1148             // Ignore comments
1149             break;
1150           default:
1151             _warn (ret, "Unsupported child node: " + aSpanChild);
1152         }
1153     return ret;
1154   }
1155 
1156   /**
1157    * Read a &lt;title&gt; element
1158    *
1159    * @param eTitle
1160    *        The source micro element. Never <code>null</code>.
1161    * @return The created domain object. May not be <code>null</code>.
1162    */
1163   @Nonnull
1164   public PSTitle readTitleFromXML (@Nonnull final IMicroElement eTitle)
1165   {
1166     final PSTitle ret = new PSTitle ();
1167     final Map <IMicroQName, String> aAttrs = eTitle.getAllQAttributes ();
1168     if (aAttrs != null)
1169       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
1170       {
1171         final String sAttrName = aEntry.getKey ().getName ();
1172         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
1173         _warn (ret, "Unsupported attribute '" + sAttrName + "'='" + sAttrValue + "'");
1174       }
1175 
1176     if (eTitle.hasChildren ())
1177       for (final IMicroNode aTitleChild : eTitle.getAllChildren ())
1178         switch (aTitleChild.getType ())
1179         {
1180           case TEXT:
1181             ret.addText (((IMicroText) aTitleChild).getNodeValue ());
1182             break;
1183           case ELEMENT:
1184             final IMicroElement eElement = (IMicroElement) aTitleChild;
1185             if (CSchematron.NAMESPACE_SCHEMATRON.equals (eElement.getNamespaceURI ()))
1186             {
1187               final String sLocalName = eElement.getLocalName ();
1188               if (sLocalName.equals (CSchematronXML.ELEMENT_DIR))
1189                 ret.addDir (readDirFromXML (eElement));
1190               else
1191                 _warn (ret, "Unsupported Schematron element '" + sLocalName + "'");
1192             }
1193             else
1194               _warn (ret, "Unsupported namespace URI '" + eElement.getNamespaceURI () + "'");
1195 
1196             break;
1197           case COMMENT:
1198             // Ignore comments
1199             break;
1200           default:
1201             _warn (ret, "Unsupported child node: " + aTitleChild);
1202         }
1203     return ret;
1204   }
1205 
1206   /**
1207    * Read a &lt;value-of&gt; element
1208    *
1209    * @param eValueOf
1210    *        The source micro element. Never <code>null</code>.
1211    * @return The created domain object. May not be <code>null</code>.
1212    */
1213   @Nonnull
1214   public PSValueOf readValueOfFromXML (@Nonnull final IMicroElement eValueOf)
1215   {
1216     final PSValueOf ret = new PSValueOf ();
1217     final Map <IMicroQName, String> aAttrs = eValueOf.getAllQAttributes ();
1218     if (aAttrs != null)
1219       for (final Map.Entry <IMicroQName, String> aEntry : aAttrs.entrySet ())
1220       {
1221         final String sAttrName = aEntry.getKey ().getName ();
1222         final String sAttrValue = _getAttributeValue (aEntry.getValue ());
1223         if (sAttrName.equals (CSchematronXML.ATTR_SELECT))
1224           ret.setSelect (sAttrValue);
1225         else
1226           ret.addForeignAttribute (sAttrName, sAttrValue);
1227       }
1228 
1229     for (final IMicroElement eValueOfChild : eValueOf.getAllChildElements ())
1230     {
1231       if (CSchematron.NAMESPACE_SCHEMATRON.equals (eValueOfChild.getNamespaceURI ()))
1232       {
1233         _warn (ret, "Unsupported Schematron element '" + eValueOfChild.getLocalName () + "'");
1234       }
1235       else
1236         _warn (ret, "Unsupported namespace URI '" + eValueOfChild.getNamespaceURI () + "'");
1237     }
1238     return ret;
1239   }
1240 
1241   /**
1242    * Read the schema from the resource supplied in the constructor. First all
1243    * includes are resolved and the {@link #readSchemaFromXML(IMicroElement)} is
1244    * called.
1245    *
1246    * @return The read {@link PSSchema}.
1247    * @throws SchematronReadException
1248    *         If reading fails
1249    */
1250   @Nonnull
1251   public PSSchema readSchema () throws SchematronReadException
1252   {
1253     // Resolve all includes as the first action
1254     final ISAXReaderSettings aSettings = null;
1255     final IMicroDocument aDoc = SchematronHelper.getWithResolvedSchematronIncludes (m_aResource,
1256                                                                                     aSettings,
1257                                                                                     m_aErrorHandler);
1258     if (aDoc == null || aDoc.getDocumentElement () == null)
1259       throw new SchematronReadException (m_aResource, "Failed to resolve includes in resource " + m_aResource);
1260 
1261     if (false)
1262       System.out.println (MicroWriter.getXMLString (aDoc));
1263 
1264     return readSchemaFromXML (aDoc.getDocumentElement ());
1265   }
1266 
1267   @Override
1268   public String toString ()
1269   {
1270     return new ToStringGenerator (this).append ("resource", m_aResource)
1271                                        .append ("errorHandler", m_aErrorHandler)
1272                                        .toString ();
1273   }
1274 }