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