1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.Nonnegative;
25 import javax.annotation.Nonnull;
26 import javax.annotation.Nullable;
27 import javax.annotation.concurrent.NotThreadSafe;
28
29 import com.helger.commons.ValueEnforcer;
30 import com.helger.commons.annotation.ReturnsMutableCopy;
31 import com.helger.commons.collection.CollectionHelper;
32 import com.helger.commons.io.resource.IReadableResource;
33 import com.helger.commons.microdom.IMicroElement;
34 import com.helger.commons.microdom.MicroElement;
35 import com.helger.commons.string.StringHelper;
36 import com.helger.commons.string.ToStringGenerator;
37 import com.helger.commons.xml.namespace.MapBasedNamespaceContext;
38 import com.helger.schematron.CSchematron;
39 import com.helger.schematron.CSchematronXML;
40 import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 @NotThreadSafe
59 public class PSSchema implements IPSElement, IPSHasID, IPSHasForeignElements, IPSHasIncludes, IPSHasLets, IPSHasRichGroup
60 {
61 private final IReadableResource m_aResource;
62 private String m_sID;
63 private PSRichGroup m_aRich;
64 private String m_sSchemaVersion;
65 private String m_sDefaultPhase;
66 private String m_sQueryBinding;
67 private PSTitle m_aTitle;
68 private final List <PSInclude> m_aIncludes = new ArrayList <PSInclude> ();
69 private final List <PSNS> m_aNSs = new ArrayList <PSNS> ();
70 private final List <PSP> m_aStartPs = new ArrayList <PSP> ();
71 private final List <PSLet> m_aLets = new ArrayList <PSLet> ();
72 private final List <PSPhase> m_aPhases = new ArrayList <PSPhase> ();
73 private final List <PSPattern> m_aPatterns = new ArrayList <PSPattern> ();
74 private final List <PSP> m_aEndPs = new ArrayList <PSP> ();
75 private PSDiagnostics m_aDiagnostics;
76 private Map <String, String> m_aForeignAttrs;
77 private List <IMicroElement> m_aForeignElements;
78
79
80
81
82 public PSSchema ()
83 {
84 this (null);
85 }
86
87
88
89
90
91
92
93
94 public PSSchema (@Nullable final IReadableResource aResource)
95 {
96 m_aResource = aResource;
97 }
98
99
100
101
102
103 @Nullable
104 public IReadableResource getResource ()
105 {
106 return m_aResource;
107 }
108
109 public boolean isValid (@Nonnull final IPSErrorHandler aErrorHandler)
110 {
111 if (m_aPatterns.isEmpty ())
112 {
113 aErrorHandler.error (this, "<schema> has no <pattern>s");
114 return false;
115 }
116 if (m_aTitle != null && !m_aTitle.isValid (aErrorHandler))
117 return false;
118 for (final PSInclude aInclude : m_aIncludes)
119 if (!aInclude.isValid (aErrorHandler))
120 return false;
121 for (final PSNS aNS : m_aNSs)
122 if (!aNS.isValid (aErrorHandler))
123 return false;
124 for (final PSP aP : m_aStartPs)
125 if (!aP.isValid (aErrorHandler))
126 return false;
127 for (final PSLet aLet : m_aLets)
128 if (!aLet.isValid (aErrorHandler))
129 return false;
130 for (final PSPhase aPhase : m_aPhases)
131 if (!aPhase.isValid (aErrorHandler))
132 return false;
133 for (final PSPattern aPattern : m_aPatterns)
134 if (!aPattern.isValid (aErrorHandler))
135 return false;
136 for (final PSP aP : m_aEndPs)
137 if (!aP.isValid (aErrorHandler))
138 return false;
139 if (m_aDiagnostics != null && !m_aDiagnostics.isValid (aErrorHandler))
140 return false;
141 return true;
142 }
143
144 public void validateCompletely (@Nonnull final IPSErrorHandler aErrorHandler)
145 {
146 if (m_aPatterns.isEmpty ())
147 aErrorHandler.error (this, "<schema> has no <pattern>s");
148 if (m_aTitle != null)
149 m_aTitle.validateCompletely (aErrorHandler);
150 for (final PSInclude aInclude : m_aIncludes)
151 aInclude.validateCompletely (aErrorHandler);
152 for (final PSNS aNS : m_aNSs)
153 aNS.validateCompletely (aErrorHandler);
154 for (final PSP aP : m_aStartPs)
155 aP.validateCompletely (aErrorHandler);
156 for (final PSLet aLet : m_aLets)
157 aLet.validateCompletely (aErrorHandler);
158 for (final PSPhase aPhase : m_aPhases)
159 aPhase.validateCompletely (aErrorHandler);
160 for (final PSPattern aPattern : m_aPatterns)
161 aPattern.validateCompletely (aErrorHandler);
162 for (final PSP aP : m_aEndPs)
163 aP.validateCompletely (aErrorHandler);
164 if (m_aDiagnostics != null)
165 m_aDiagnostics.validateCompletely (aErrorHandler);
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180 public boolean isPreprocessed ()
181 {
182 if (hasAnyInclude ())
183 return false;
184
185 for (final PSPhase aPhase : m_aPhases)
186 if (aPhase.hasAnyInclude ())
187 return false;
188
189 for (final PSPattern aPattern : m_aPatterns)
190 {
191 if (aPattern.isAbstract () || aPattern.hasAnyInclude () || aPattern.hasAnyParam ())
192 return false;
193 for (final PSRule aRule : aPattern.getAllRules ())
194 {
195 if (aRule.isAbstract () || aRule.hasAnyInclude () || aRule.hasAnyExtends ())
196 return false;
197 }
198 }
199
200 if (m_aDiagnostics != null && m_aDiagnostics.hasAnyInclude ())
201 return false;
202 return true;
203 }
204
205 public boolean isMinimal ()
206 {
207 if (m_aTitle != null && !m_aTitle.isMinimal ())
208 return false;
209 for (final PSInclude aInclude : m_aIncludes)
210 if (!aInclude.isMinimal ())
211 return false;
212 for (final PSNS aNS : m_aNSs)
213 if (!aNS.isMinimal ())
214 return false;
215 for (final PSP aP : m_aStartPs)
216 if (!aP.isMinimal ())
217 return false;
218 for (final PSLet aLet : m_aLets)
219 if (!aLet.isMinimal ())
220 return false;
221 for (final PSPhase aPhase : m_aPhases)
222 if (!aPhase.isMinimal ())
223 return false;
224 for (final PSPattern aPattern : m_aPatterns)
225 if (!aPattern.isMinimal ())
226 return false;
227 for (final PSP aP : m_aEndPs)
228 if (!aP.isMinimal ())
229 return false;
230 if (m_aDiagnostics != null && !m_aDiagnostics.isMinimal ())
231 return false;
232 return true;
233 }
234
235 public void addForeignElement (@Nonnull final IMicroElement aForeignElement)
236 {
237 ValueEnforcer.notNull (aForeignElement, "ForeignElement");
238 if (aForeignElement.hasParent ())
239 throw new IllegalArgumentException ("ForeignElement already has a parent!");
240 if (m_aForeignElements == null)
241 m_aForeignElements = new ArrayList <IMicroElement> ();
242 m_aForeignElements.add (aForeignElement);
243 }
244
245 public void addForeignElements (@Nonnull final List <IMicroElement> aForeignElements)
246 {
247 ValueEnforcer.notNull (aForeignElements, "ForeignElements");
248 for (final IMicroElement aForeignElement : aForeignElements)
249 addForeignElement (aForeignElement);
250 }
251
252 public boolean hasForeignElements ()
253 {
254 return m_aForeignElements != null && !m_aForeignElements.isEmpty ();
255 }
256
257 @Nonnull
258 @ReturnsMutableCopy
259 public List <IMicroElement> getAllForeignElements ()
260 {
261 return CollectionHelper.newList (m_aForeignElements);
262 }
263
264 public void addForeignAttribute (@Nonnull final String sAttrName, @Nonnull final String sAttrValue)
265 {
266 ValueEnforcer.notNull (sAttrName, "AttrName");
267 ValueEnforcer.notNull (sAttrValue, "AttrValue");
268 if (m_aForeignAttrs == null)
269 m_aForeignAttrs = new LinkedHashMap <String, String> ();
270 m_aForeignAttrs.put (sAttrName, sAttrValue);
271 }
272
273 public void addForeignAttributes (@Nonnull final Map <String, String> aForeignAttrs)
274 {
275 ValueEnforcer.notNull (aForeignAttrs, "ForeignAttrs");
276 for (final Map.Entry <String, String> aEntry : aForeignAttrs.entrySet ())
277 addForeignAttribute (aEntry.getKey (), aEntry.getValue ());
278 }
279
280 public boolean hasForeignAttributes ()
281 {
282 return m_aForeignAttrs != null && !m_aForeignAttrs.isEmpty ();
283 }
284
285 @Nonnull
286 @ReturnsMutableCopy
287 public Map <String, String> getAllForeignAttributes ()
288 {
289 return CollectionHelper.newOrderedMap (m_aForeignAttrs);
290 }
291
292 public void setID (@Nullable final String sID)
293 {
294 m_sID = sID;
295 }
296
297 public boolean hasID ()
298 {
299 return m_sID != null;
300 }
301
302 @Nullable
303 public String getID ()
304 {
305 return m_sID;
306 }
307
308 public void setRich (@Nullable final PSRichGroup aRich)
309 {
310 m_aRich = aRich;
311 }
312
313 public boolean hasRich ()
314 {
315 return m_aRich != null;
316 }
317
318 @Nullable
319 public PSRichGroup getRich ()
320 {
321 return m_aRich;
322 }
323
324 @Nullable
325 public PSRichGroup getRichClone ()
326 {
327 return m_aRich == null ? null : m_aRich.getClone ();
328 }
329
330 public void setQueryBinding (@Nullable final String sQueryBinding)
331 {
332 if (sQueryBinding != null && sQueryBinding.length () == 0)
333 throw new IllegalArgumentException ("queryBinding may not be empty!");
334 m_sQueryBinding = sQueryBinding;
335 }
336
337 @Nullable
338 public String getQueryBinding ()
339 {
340 return m_sQueryBinding;
341 }
342
343 public void setSchemaVersion (@Nullable final String sSchemaVersion)
344 {
345 if (sSchemaVersion != null && sSchemaVersion.length () == 0)
346 throw new IllegalArgumentException ("schemaVersion may not be empty!");
347 m_sSchemaVersion = sSchemaVersion;
348 }
349
350 @Nullable
351 public String getSchemaVersion ()
352 {
353 return m_sSchemaVersion;
354 }
355
356 public void setDefaultPhase (@Nullable final String sDefaultPhase)
357 {
358 m_sDefaultPhase = sDefaultPhase;
359 }
360
361 @Nullable
362 public String getDefaultPhase ()
363 {
364 return m_sDefaultPhase;
365 }
366
367 public void setTitle (@Nullable final PSTitle aTitle)
368 {
369 m_aTitle = aTitle;
370 }
371
372 @Nullable
373 public PSTitle getTitle ()
374 {
375 return m_aTitle;
376 }
377
378 public boolean hasTitle ()
379 {
380 return m_aTitle != null;
381 }
382
383 public void addInclude (@Nonnull final PSInclude aInclude)
384 {
385 ValueEnforcer.notNull (aInclude, "Include");
386 m_aIncludes.add (aInclude);
387 }
388
389 public boolean hasAnyInclude ()
390 {
391 return !m_aIncludes.isEmpty ();
392 }
393
394 @Nonnull
395 @ReturnsMutableCopy
396 public List <PSInclude> getAllIncludes ()
397 {
398 return CollectionHelper.newList (m_aIncludes);
399 }
400
401 public void addNS (@Nonnull final PSNS aNS)
402 {
403 ValueEnforcer.notNull (aNS, "NS");
404 m_aNSs.add (aNS);
405 }
406
407 public boolean hasAnyNS ()
408 {
409 return !m_aNSs.isEmpty ();
410 }
411
412 @Nonnull
413 @ReturnsMutableCopy
414 public List <PSNS> getAllNSs ()
415 {
416 return CollectionHelper.newList (m_aNSs);
417 }
418
419
420
421
422 @Nonnull
423 @ReturnsMutableCopy
424 public MapBasedNamespaceContext getAsNamespaceContext ()
425 {
426 final MapBasedNamespaceContext ret = new MapBasedNamespaceContext ();
427 for (final PSNS aNS : m_aNSs)
428 ret.addMapping (aNS.getPrefix (), aNS.getUri ());
429 return ret;
430 }
431
432 public void addStartP (@Nonnull final PSP aP)
433 {
434 ValueEnforcer.notNull (aP, "P");
435 m_aStartPs.add (aP);
436 }
437
438 @Nonnull
439 @ReturnsMutableCopy
440 public List <PSP> getAllStartPs ()
441 {
442 return CollectionHelper.newList (m_aStartPs);
443 }
444
445 public void addLet (@Nonnull final PSLet aLet)
446 {
447 ValueEnforcer.notNull (aLet, "Let");
448 m_aLets.add (aLet);
449 }
450
451 public boolean hasAnyLet ()
452 {
453 return !m_aLets.isEmpty ();
454 }
455
456 @Nonnull
457 @ReturnsMutableCopy
458 public List <PSLet> getAllLets ()
459 {
460 return CollectionHelper.newList (m_aLets);
461 }
462
463 @Nonnull
464 @ReturnsMutableCopy
465 public Map <String, String> getAllLetsAsMap ()
466 {
467 final Map <String, String> ret = new LinkedHashMap <String, String> ();
468 for (final PSLet aLet : m_aLets)
469 ret.put (aLet.getName (), aLet.getValue ());
470 return ret;
471 }
472
473 public void addPhase (@Nonnull final PSPhase aPhase)
474 {
475 ValueEnforcer.notNull (aPhase, "Phase");
476 m_aPhases.add (aPhase);
477 }
478
479 @Nonnull
480 @ReturnsMutableCopy
481 public List <PSPhase> getAllPhases ()
482 {
483 return CollectionHelper.newList (m_aPhases);
484 }
485
486
487
488
489
490 @Nonnull
491 @ReturnsMutableCopy
492 public List <String> getAllPhaseIDs ()
493 {
494 final List <String> ret = new ArrayList <String> ();
495 for (final PSPhase aPhase : m_aPhases)
496 if (aPhase.hasID ())
497 ret.add (aPhase.getID ());
498 return ret;
499 }
500
501 @Nullable
502 public PSPhase getPhaseOfID (@Nullable final String sID)
503 {
504 if (StringHelper.hasText (sID))
505 for (final PSPhase aPhase : m_aPhases)
506 if (sID.equals (aPhase.getID ()))
507 return aPhase;
508 return null;
509 }
510
511 public void addPattern (@Nonnull final PSPattern aPattern)
512 {
513 ValueEnforcer.notNull (aPattern, "Pattern");
514 m_aPatterns.add (aPattern);
515 }
516
517 public boolean hasPatterns ()
518 {
519 return !m_aPatterns.isEmpty ();
520 }
521
522 public boolean hasNoPatterns ()
523 {
524 return m_aPatterns.isEmpty ();
525 }
526
527 @Nonnull
528 @ReturnsMutableCopy
529 public List <PSPattern> getAllPatterns ()
530 {
531 return CollectionHelper.newList (m_aPatterns);
532 }
533
534 @Nonnegative
535 public int getPatternCount ()
536 {
537 return m_aPatterns.size ();
538 }
539
540 @Nullable
541 public PSPattern getPatternOfID (@Nullable final String sID)
542 {
543 if (StringHelper.hasText (sID))
544 for (final PSPattern aPattern : m_aPatterns)
545 if (sID.equals (aPattern.getID ()))
546 return aPattern;
547 return null;
548 }
549
550 public void addEndP (@Nonnull final PSP aP)
551 {
552 ValueEnforcer.notNull (aP, "P");
553 m_aEndPs.add (aP);
554 }
555
556 @Nonnull
557 @ReturnsMutableCopy
558 public List <PSP> getAllEndPs ()
559 {
560 return CollectionHelper.newList (m_aEndPs);
561 }
562
563 public void setDiagnostics (@Nullable final PSDiagnostics aDiagnostics)
564 {
565 m_aDiagnostics = aDiagnostics;
566 }
567
568 public boolean hasDiagnostics ()
569 {
570 return m_aDiagnostics != null;
571 }
572
573 @Nullable
574 public PSDiagnostics getDiagnostics ()
575 {
576 return m_aDiagnostics;
577 }
578
579 @Nonnull
580 public IMicroElement getAsMicroElement ()
581 {
582 final IMicroElement ret = new MicroElement (CSchematron.NAMESPACE_SCHEMATRON, CSchematronXML.ELEMENT_SCHEMA);
583 ret.setAttribute (CSchematronXML.ATTR_ID, m_sID);
584 if (m_aRich != null)
585 m_aRich.fillMicroElement (ret);
586 ret.setAttribute (CSchematronXML.ATTR_SCHEMA_VERSION, m_sSchemaVersion);
587 ret.setAttribute (CSchematronXML.ATTR_DEFAULT_PHASE, m_sDefaultPhase);
588 ret.setAttribute (CSchematronXML.ATTR_QUERY_BINDING, m_sQueryBinding);
589 if (m_aForeignElements != null)
590 for (final IMicroElement aForeignElement : m_aForeignElements)
591 ret.appendChild (aForeignElement.getClone ());
592 for (final PSInclude aInclude : m_aIncludes)
593 ret.appendChild (aInclude.getAsMicroElement ());
594 if (m_aTitle != null)
595 ret.appendChild (m_aTitle.getAsMicroElement ());
596 for (final PSNS aNS : m_aNSs)
597 ret.appendChild (aNS.getAsMicroElement ());
598 for (final PSP aP : m_aStartPs)
599 ret.appendChild (aP.getAsMicroElement ());
600 for (final PSLet aLet : m_aLets)
601 ret.appendChild (aLet.getAsMicroElement ());
602 for (final PSPhase aPhase : m_aPhases)
603 ret.appendChild (aPhase.getAsMicroElement ());
604 for (final PSPattern aPattern : m_aPatterns)
605 ret.appendChild (aPattern.getAsMicroElement ());
606 for (final PSP aP : m_aEndPs)
607 ret.appendChild (aP.getAsMicroElement ());
608 if (m_aDiagnostics != null)
609 ret.appendChild (m_aDiagnostics.getAsMicroElement ());
610 if (m_aForeignAttrs != null)
611 for (final Map.Entry <String, String> aEntry : m_aForeignAttrs.entrySet ())
612 ret.setAttribute (aEntry.getKey (), aEntry.getValue ());
613 return ret;
614 }
615
616 @Override
617 public String toString ()
618 {
619 return new ToStringGenerator (this).appendIfNotNull ("resource", m_aResource)
620 .appendIfNotNull ("id", m_sID)
621 .appendIfNotNull ("rich", m_aRich)
622 .appendIfNotNull ("schemaVersion", m_sSchemaVersion)
623 .appendIfNotNull ("defaultPhase", m_sDefaultPhase)
624 .appendIfNotNull ("queryBinding", m_sQueryBinding)
625 .appendIfNotNull ("title", m_aTitle)
626 .appendIfNotEmpty ("includes", m_aIncludes)
627 .appendIfNotEmpty ("nss", m_aNSs)
628 .appendIfNotEmpty ("startps", m_aStartPs)
629 .appendIfNotEmpty ("lets", m_aLets)
630 .appendIfNotEmpty ("phases", m_aPhases)
631 .appendIfNotEmpty ("patterns", m_aPatterns)
632 .appendIfNotEmpty ("endps", m_aEndPs)
633 .appendIfNotNull ("diagnostics", m_aDiagnostics)
634 .appendIfNotEmpty ("foreignAttrs", m_aForeignAttrs)
635 .appendIfNotEmpty ("foreignElements", m_aForeignElements)
636 .toString ();
637 }
638 }