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.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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 @NotThreadSafe
62 public class PSRule implements IPSElement, IPSHasID, IPSHasFlag, IPSHasForeignElements, IPSHasIncludes, IPSHasLets, IPSHasRichGroup, IPSHasLinkableGroup
63 {
64 public static final boolean DEFAULT_ABSTRACT = false;
65
66 private String m_sFlag;
67 private PSRichGroup m_aRich;
68 private PSLinkableGroup m_aLinkable;
69 private boolean m_bAbstract = DEFAULT_ABSTRACT;
70 private String m_sContext;
71 private String m_sID;
72 private final List <PSInclude> m_aIncludes = new ArrayList <PSInclude> ();
73 private final List <PSLet> m_aLets = new ArrayList <PSLet> ();
74 private final List <IPSElement> m_aContent = new ArrayList <IPSElement> ();
75 private Map <String, String> m_aForeignAttrs;
76 private List <IMicroElement> m_aForeignElements;
77
78 public PSRule ()
79 {}
80
81 public boolean isValid (@Nonnull final IPSErrorHandler aErrorHandler)
82 {
83
84 if (m_bAbstract && StringHelper.hasNoText (m_sID))
85 {
86 aErrorHandler.error (this, "abstract <rule> has no 'id'");
87 return false;
88 }
89
90 if (m_bAbstract && StringHelper.hasText (m_sContext))
91 {
92 aErrorHandler.error (this, "abstract <rule> may not have a 'context'");
93 return false;
94 }
95
96 if (!m_bAbstract && StringHelper.hasNoText (m_sContext))
97 {
98 aErrorHandler.error (this, "<rule> must have a 'context'");
99 return false;
100 }
101
102 if (m_aContent.isEmpty ())
103 {
104 aErrorHandler.error (this, "<rule> has no content");
105 return false;
106 }
107 for (final PSInclude aInclude : m_aIncludes)
108 if (!aInclude.isValid (aErrorHandler))
109 return false;
110 for (final PSLet aLet : m_aLets)
111 if (!aLet.isValid (aErrorHandler))
112 return false;
113 for (final IPSElement aContent : m_aContent)
114 if (!aContent.isValid (aErrorHandler))
115 return false;
116 return true;
117 }
118
119 public void validateCompletely (@Nonnull final IPSErrorHandler aErrorHandler)
120 {
121
122 if (m_bAbstract && StringHelper.hasNoText (m_sID))
123 aErrorHandler.error (this, "abstract <rule> has no 'id'");
124
125 if (m_bAbstract && StringHelper.hasText (m_sContext))
126 aErrorHandler.error (this, "abstract <rule> may not have a 'context'");
127
128 if (!m_bAbstract && StringHelper.hasNoText (m_sContext))
129 aErrorHandler.error (this, "<rule> must have a 'context'");
130
131 if (m_aContent.isEmpty ())
132 aErrorHandler.error (this, "<rule> has no content");
133 for (final PSInclude aInclude : m_aIncludes)
134 aInclude.validateCompletely (aErrorHandler);
135 for (final PSLet aLet : m_aLets)
136 aLet.validateCompletely (aErrorHandler);
137 for (final IPSElement aContent : m_aContent)
138 aContent.validateCompletely (aErrorHandler);
139 }
140
141 public boolean isMinimal ()
142 {
143 for (final PSInclude aInclude : m_aIncludes)
144 if (!aInclude.isMinimal ())
145 return false;
146 for (final PSLet aLet : m_aLets)
147 if (!aLet.isMinimal ())
148 return false;
149 for (final IPSElement aContent : m_aContent)
150 if (!aContent.isMinimal ())
151 return false;
152 return true;
153 }
154
155 public void addForeignElement (@Nonnull final IMicroElement aForeignElement)
156 {
157 ValueEnforcer.notNull (aForeignElement, "ForeignElement");
158 if (aForeignElement.hasParent ())
159 throw new IllegalArgumentException ("ForeignElement already has a parent!");
160 if (m_aForeignElements == null)
161 m_aForeignElements = new ArrayList <IMicroElement> ();
162 m_aForeignElements.add (aForeignElement);
163 }
164
165 public void addForeignElements (@Nonnull final List <IMicroElement> aForeignElements)
166 {
167 ValueEnforcer.notNull (aForeignElements, "ForeignElements");
168 for (final IMicroElement aForeignElement : aForeignElements)
169 addForeignElement (aForeignElement);
170 }
171
172 public boolean hasForeignElements ()
173 {
174 return m_aForeignElements != null && !m_aForeignElements.isEmpty ();
175 }
176
177 @Nonnull
178 @ReturnsMutableCopy
179 public List <IMicroElement> getAllForeignElements ()
180 {
181 return CollectionHelper.newList (m_aForeignElements);
182 }
183
184 public void addForeignAttribute (@Nonnull final String sAttrName, @Nonnull final String sAttrValue)
185 {
186 ValueEnforcer.notNull (sAttrName, "AttrName");
187 ValueEnforcer.notNull (sAttrValue, "AttrValue");
188 if (m_aForeignAttrs == null)
189 m_aForeignAttrs = new LinkedHashMap <String, String> ();
190 m_aForeignAttrs.put (sAttrName, sAttrValue);
191 }
192
193 public void addForeignAttributes (@Nonnull final Map <String, String> aForeignAttrs)
194 {
195 ValueEnforcer.notNull (aForeignAttrs, "ForeignAttrs");
196 for (final Map.Entry <String, String> aEntry : aForeignAttrs.entrySet ())
197 addForeignAttribute (aEntry.getKey (), aEntry.getValue ());
198 }
199
200 public boolean hasForeignAttributes ()
201 {
202 return m_aForeignAttrs != null && !m_aForeignAttrs.isEmpty ();
203 }
204
205 @Nonnull
206 @ReturnsMutableCopy
207 public Map <String, String> getAllForeignAttributes ()
208 {
209 return CollectionHelper.newOrderedMap (m_aForeignAttrs);
210 }
211
212 public void setFlag (@Nullable final String sFlag)
213 {
214 m_sFlag = sFlag;
215 }
216
217 @Nullable
218 public String getFlag ()
219 {
220 return m_sFlag;
221 }
222
223 public void setRich (@Nullable final PSRichGroup aRich)
224 {
225 m_aRich = aRich;
226 }
227
228 public boolean hasRich ()
229 {
230 return m_aRich != null;
231 }
232
233 @Nullable
234 public PSRichGroup getRich ()
235 {
236 return m_aRich;
237 }
238
239 @Nullable
240 public PSRichGroup getRichClone ()
241 {
242 return m_aRich == null ? null : m_aRich.getClone ();
243 }
244
245 public void setLinkable (@Nullable final PSLinkableGroup aLinkable)
246 {
247 m_aLinkable = aLinkable;
248 }
249
250 public boolean hasLinkable ()
251 {
252 return m_aLinkable != null;
253 }
254
255 @Nullable
256 public PSLinkableGroup getLinkable ()
257 {
258 return m_aLinkable;
259 }
260
261 @Nullable
262 public PSLinkableGroup getLinkableClone ()
263 {
264 return m_aLinkable == null ? null : m_aLinkable.getClone ();
265 }
266
267
268
269
270
271 public void setAbstract (final boolean bAbstract)
272 {
273 m_bAbstract = bAbstract;
274 }
275
276
277
278
279
280 public boolean isAbstract ()
281 {
282 return m_bAbstract;
283 }
284
285 public void setContext (@Nullable final String sContext)
286 {
287 m_sContext = sContext;
288 }
289
290 @Nullable
291 public String getContext ()
292 {
293 return m_sContext;
294 }
295
296 public void setID (@Nullable final String sID)
297 {
298 m_sID = sID;
299 }
300
301 public boolean hasID ()
302 {
303 return m_sID != null;
304 }
305
306 @Nullable
307 public String getID ()
308 {
309 return m_sID;
310 }
311
312 public void addInclude (@Nonnull final PSInclude aInclude)
313 {
314 ValueEnforcer.notNull (aInclude, "Include");
315 m_aIncludes.add (aInclude);
316 }
317
318 public boolean hasAnyInclude ()
319 {
320 return !m_aIncludes.isEmpty ();
321 }
322
323 @Nonnull
324 @ReturnsMutableCopy
325 public List <PSInclude> getAllIncludes ()
326 {
327 return CollectionHelper.newList (m_aIncludes);
328 }
329
330 public void addLet (@Nonnull final PSLet aLet)
331 {
332 ValueEnforcer.notNull (aLet, "Let");
333 m_aLets.add (aLet);
334 }
335
336 public boolean hasAnyLet ()
337 {
338 return !m_aLets.isEmpty ();
339 }
340
341 @Nonnull
342 @ReturnsMutableCopy
343 public List <PSLet> getAllLets ()
344 {
345 return CollectionHelper.newList (m_aLets);
346 }
347
348 @Nonnull
349 @ReturnsMutableCopy
350 public Map <String, String> getAllLetsAsMap ()
351 {
352 final Map <String, String> ret = new LinkedHashMap <String, String> ();
353 for (final PSLet aLet : m_aLets)
354 ret.put (aLet.getName (), aLet.getValue ());
355 return ret;
356 }
357
358 public void addAssertReport (@Nonnull final PSAssertReport aAssertReport)
359 {
360 ValueEnforcer.notNull (aAssertReport, "AssertReport");
361 m_aContent.add (aAssertReport);
362 }
363
364 @Nonnull
365 @ReturnsMutableCopy
366 public List <PSAssertReport> getAllAssertReports ()
367 {
368 final List <PSAssertReport> ret = new ArrayList <PSAssertReport> ();
369 for (final IPSElement aElement : m_aContent)
370 if (aElement instanceof PSAssertReport)
371 ret.add ((PSAssertReport) aElement);
372 return ret;
373 }
374
375 public void addExtends (@Nonnull final PSExtends aExtends)
376 {
377 ValueEnforcer.notNull (aExtends, "Extends");
378 m_aContent.add (aExtends);
379 }
380
381 @Nonnull
382 @ReturnsMutableCopy
383 public List <PSExtends> getAllExtends ()
384 {
385 final List <PSExtends> ret = new ArrayList <PSExtends> ();
386 for (final IPSElement aElement : m_aContent)
387 if (aElement instanceof PSExtends)
388 ret.add ((PSExtends) aElement);
389 return ret;
390 }
391
392 @Nonnegative
393 public int getExtendsCount ()
394 {
395 int ret = 0;
396 for (final IPSElement aElement : m_aContent)
397 if (aElement instanceof PSExtends)
398 ++ret;
399 return ret;
400 }
401
402 public boolean hasAnyExtends ()
403 {
404 for (final IPSElement aElement : m_aContent)
405 if (aElement instanceof PSExtends)
406 return true;
407 return false;
408 }
409
410
411
412
413
414 @Nonnull
415 @ReturnsMutableCopy
416 public List <IPSElement> getAllContentElements ()
417 {
418 return CollectionHelper.newList (m_aContent);
419 }
420
421 @Nonnull
422 public IMicroElement getAsMicroElement ()
423 {
424 final IMicroElement ret = new MicroElement (CSchematron.NAMESPACE_SCHEMATRON, CSchematronXML.ELEMENT_RULE);
425 ret.setAttribute (CSchematronXML.ATTR_FLAG, m_sFlag);
426 if (m_bAbstract)
427 ret.setAttribute (CSchematronXML.ATTR_ABSTRACT, "true");
428 ret.setAttribute (CSchematronXML.ATTR_CONTEXT, m_sContext);
429 ret.setAttribute (CSchematronXML.ATTR_ID, m_sID);
430 if (m_aRich != null)
431 m_aRich.fillMicroElement (ret);
432 if (m_aLinkable != null)
433 m_aLinkable.fillMicroElement (ret);
434 if (m_aForeignElements != null)
435 for (final IMicroElement aForeignElement : m_aForeignElements)
436 ret.appendChild (aForeignElement.getClone ());
437 for (final PSInclude aInclude : m_aIncludes)
438 ret.appendChild (aInclude.getAsMicroElement ());
439 for (final PSLet aLet : m_aLets)
440 ret.appendChild (aLet.getAsMicroElement ());
441 for (final IPSElement aContent : m_aContent)
442 ret.appendChild (aContent.getAsMicroElement ());
443 if (m_aForeignAttrs != null)
444 for (final Map.Entry <String, String> aEntry : m_aForeignAttrs.entrySet ())
445 ret.setAttribute (aEntry.getKey (), aEntry.getValue ());
446 return ret;
447 }
448
449 @Override
450 public String toString ()
451 {
452 return new ToStringGenerator (this).appendIfNotNull ("flag", m_sFlag)
453 .appendIfNotNull ("rich", m_aRich)
454 .appendIfNotNull ("linkable", m_aLinkable)
455 .append ("abstract", m_bAbstract)
456 .appendIfNotNull ("context", m_sContext)
457 .appendIfNotNull ("id", m_sID)
458 .appendIfNotEmpty ("includes", m_aIncludes)
459 .appendIfNotEmpty ("lets", m_aLets)
460 .appendIfNotEmpty ("content", m_aContent)
461 .appendIfNotEmpty ("foreignAttrs", m_aForeignAttrs)
462 .appendIfNotEmpty ("foreignElements", m_aForeignElements)
463 .toString ();
464 }
465 }