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