1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.helger.schematron.pure.preprocess;
18
19 import javax.annotation.Nonnull;
20 import javax.annotation.Nullable;
21 import javax.annotation.concurrent.NotThreadSafe;
22
23 import com.helger.commons.ValueEnforcer;
24 import com.helger.commons.collection.impl.ICommonsList;
25 import com.helger.commons.collection.impl.ICommonsMap;
26 import com.helger.commons.collection.impl.ICommonsNavigableMap;
27 import com.helger.commons.string.ToStringGenerator;
28 import com.helger.schematron.pure.binding.IPSQueryBinding;
29 import com.helger.schematron.pure.model.IPSElement;
30 import com.helger.schematron.pure.model.PSActive;
31 import com.helger.schematron.pure.model.PSAssertReport;
32 import com.helger.schematron.pure.model.PSDiagnostic;
33 import com.helger.schematron.pure.model.PSDiagnostics;
34 import com.helger.schematron.pure.model.PSDir;
35 import com.helger.schematron.pure.model.PSEmph;
36 import com.helger.schematron.pure.model.PSExtends;
37 import com.helger.schematron.pure.model.PSLet;
38 import com.helger.schematron.pure.model.PSNS;
39 import com.helger.schematron.pure.model.PSName;
40 import com.helger.schematron.pure.model.PSPattern;
41 import com.helger.schematron.pure.model.PSPhase;
42 import com.helger.schematron.pure.model.PSRule;
43 import com.helger.schematron.pure.model.PSSchema;
44 import com.helger.schematron.pure.model.PSSpan;
45 import com.helger.schematron.pure.model.PSValueOf;
46
47
48
49
50
51
52
53
54
55
56
57 @NotThreadSafe
58 public class PSPreprocessor
59 {
60 public static final boolean DEFAULT_KEEP_TITLES = false;
61 public static final boolean DEFAULT_KEEP_DIAGNOSTICS = false;
62 public static final boolean DEFAULT_KEEP_REPORTS = false;
63 public static final boolean DEFAULT_KEEP_EMPTY_PATTERNS = true;
64 public static final boolean DEFAULT_KEEP_EMPTY_SCHEMA = true;
65
66 private final IPSQueryBinding m_aQueryBinding;
67 private boolean m_bKeepTitles = DEFAULT_KEEP_TITLES;
68 private boolean m_bKeepDiagnostics = DEFAULT_KEEP_DIAGNOSTICS;
69 private boolean m_bKeepReports = DEFAULT_KEEP_REPORTS;
70 private boolean m_bKeepEmptyPatterns = DEFAULT_KEEP_EMPTY_PATTERNS;
71 private boolean m_bKeepEmptySchema = DEFAULT_KEEP_EMPTY_SCHEMA;
72
73 public PSPreprocessor (@Nonnull final IPSQueryBinding aQueryBinding)
74 {
75 m_aQueryBinding = ValueEnforcer.notNull (aQueryBinding, "QueryBinding");
76 }
77
78
79
80
81 @Nonnull
82 public IPSQueryBinding getQueryBinding ()
83 {
84 return m_aQueryBinding;
85 }
86
87
88
89
90
91 public boolean isKeepTitles ()
92 {
93 return m_bKeepTitles;
94 }
95
96
97
98
99
100
101
102
103 @Nonnull
104 public PSPreprocessor setKeepTitles (final boolean bKeepTitles)
105 {
106 m_bKeepTitles = bKeepTitles;
107 return this;
108 }
109
110
111
112
113
114 public boolean isKeepDiagnostics ()
115 {
116 return m_bKeepDiagnostics;
117 }
118
119
120
121
122
123
124
125
126 @Nonnull
127 public PSPreprocessor setKeepDiagnostics (final boolean bKeepDiagnostics)
128 {
129 m_bKeepDiagnostics = bKeepDiagnostics;
130 return this;
131 }
132
133
134
135
136
137
138 public boolean isKeepReports ()
139 {
140 return m_bKeepReports;
141 }
142
143
144
145
146
147
148
149
150
151
152 @Nonnull
153 public PSPreprocessor setKeepReports (final boolean bKeepReports)
154 {
155 m_bKeepReports = bKeepReports;
156 return this;
157 }
158
159
160
161
162
163 public boolean isKeepEmptyPatterns ()
164 {
165 return m_bKeepEmptyPatterns;
166 }
167
168
169
170
171
172
173
174
175
176 @Nonnull
177 public PSPreprocessor setKeepEmptyPatterns (final boolean bKeepEmptyPatterns)
178 {
179 m_bKeepEmptyPatterns = bKeepEmptyPatterns;
180 return this;
181 }
182
183
184
185
186
187 public boolean isKeepEmptySchema ()
188 {
189 return m_bKeepEmptySchema;
190 }
191
192
193
194
195
196
197
198
199
200
201 @Nonnull
202 public PSPreprocessor setKeepEmptySchema (final boolean bKeepEmptySchema)
203 {
204 m_bKeepEmptySchema = bKeepEmptySchema;
205 return this;
206 }
207
208 @Nonnull
209 private static PSPhasese">PSPhase _getPreprocessedPhase (@Nonnull final PSPhase aPhase,
210 @Nonnull final PreprocessorIDPool aIDPool) throws SchematronPreprocessException
211 {
212 final PSPhaseure/model/PSPhase.html#PSPhase">PSPhase ret = new PSPhase ();
213 ret.setID (aIDPool.getUniqueID (aPhase.getID ()));
214 ret.setRich (aPhase.getRichClone ());
215 if (aPhase.hasAnyInclude ())
216 throw new SchematronPreprocessException ("Cannot preprocess <phase> with an <include>");
217 for (final IPSElement aElement : aPhase.getAllContentElements ())
218 {
219 if (aElement instanceof PSActive)
220 ret.addActive (((PSActive) aElement).getClone ());
221 else
222 if (aElement instanceof PSLet)
223 ret.addLet (((PSLet) aElement).getClone ());
224
225 }
226 ret.addForeignElements (aPhase.getAllForeignElements ());
227 ret.addForeignAttributes (aPhase.getAllForeignAttributes ());
228 return ret;
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243 private void _resolveRuleContent (@Nonnull final ICommonsList <IPSElement> aRuleContent,
244 @Nonnull final PreprocessorLookup aLookup,
245 @Nonnull final PreprocessorIDPool aIDPool,
246 @Nullable final ICommonsMap <String, String> aParamValueMap,
247 @Nonnull final PSRule aTargetRule) throws SchematronPreprocessException
248 {
249 for (final IPSElement aElement : aRuleContent)
250 {
251 if (aElement instanceof PSAssertReport)
252 {
253 final PSAssertReportcom/helger/schematron/pure/model/PSAssertReport.html#PSAssertReport">PSAssertReport aAssertReport = (PSAssertReport) aElement;
254 aTargetRule.addAssertReport (_getPreprocessedAssert (aAssertReport, aIDPool, aParamValueMap));
255 }
256 else
257 {
258 final PSExtends/../../../com/helger/schematron/pure/model/PSExtends.html#PSExtends">PSExtends aExtends = (PSExtends) aElement;
259 final String sRuleID = aExtends.getRule ();
260 final PSRule aBaseRule = aLookup.getAbstractRuleOfID (sRuleID);
261 if (aBaseRule == null)
262 throw new SchematronPreprocessException ("Failed to resolve rule ID '" +
263 sRuleID +
264 "' in extends statement. Available rules are: " +
265 aLookup.getAllAbstractRuleIDs ());
266
267
268 _resolveRuleContent (aBaseRule.getAllContentElements (), aLookup, aIDPool, aParamValueMap, aTargetRule);
269
270
271 for (final PSLet aBaseLet : aBaseRule.getAllLets ())
272 aTargetRule.addLet (aBaseLet.getClone ());
273 }
274 }
275 }
276
277 @Nonnull
278 private PSAssertReportrtReport">PSAssertReport _getPreprocessedAssert (@Nonnull final PSAssertReport aAssertReport,
279 @Nonnull final PreprocessorIDPool aIDPool,
280 @Nullable final ICommonsMap <String, String> aParamValueMap)
281 {
282 String sTest = aAssertReport.getTest ();
283 if (aAssertReport.isReport () && !m_bKeepReports)
284 {
285
286 sTest = m_aQueryBinding.getNegatedTestExpression (sTest);
287 }
288
289
290 final PSAssertReportel/PSAssertReport.html#PSAssertReport">PSAssertReport ret = new PSAssertReport (m_bKeepReports ? aAssertReport.isAssert () : true);
291 ret.setTest (m_aQueryBinding.getWithParamTextsReplaced (sTest, aParamValueMap));
292 ret.setFlag (aAssertReport.getFlag ());
293 ret.setID (aIDPool.getUniqueID (aAssertReport.getID ()));
294 if (m_bKeepDiagnostics)
295 ret.setDiagnostics (aAssertReport.getAllDiagnostics ());
296 ret.setRich (aAssertReport.getRichClone ());
297 ret.setLinkable (aAssertReport.getLinkableClone ());
298 for (final Object aContent : aAssertReport.getAllContentElements ())
299 {
300 if (aContent instanceof String)
301 ret.addText ((String) aContent);
302 else
303 if (aContent instanceof PSName)
304 ret.addName (((PSName) aContent).getClone ());
305 else
306 if (aContent instanceof PSValueOf)
307 {
308 final PSValueOf../../../com/helger/schematron/pure/model/PSValueOf.html#PSValueOf">PSValueOf aValueOf = ((PSValueOf) aContent).getClone ();
309 aValueOf.setSelect (m_aQueryBinding.getWithParamTextsReplaced (aValueOf.getSelect (), aParamValueMap));
310 ret.addValueOf (aValueOf);
311 }
312 else
313 if (aContent instanceof PSEmph)
314 ret.addEmph (((PSEmph) aContent).getClone ());
315 else
316 if (aContent instanceof PSDir)
317 ret.addDir (((PSDir) aContent).getClone ());
318 else
319 if (aContent instanceof PSSpan)
320 ret.addSpan (((PSSpan) aContent).getClone ());
321 }
322 ret.addForeignElements (aAssertReport.getAllForeignElements ());
323 ret.addForeignAttributes (aAssertReport.getAllForeignAttributes ());
324 return ret;
325 }
326
327 @Nullable
328 private PSRulele">PSRule _getPreprocessedRule (@Nonnull final PSRule aRule,
329 @Nonnull final PreprocessorLookup aLookup,
330 @Nonnull final PreprocessorIDPool aIDPool,
331 @Nullable final ICommonsMap <String, String> aParamValueMap) throws SchematronPreprocessException
332 {
333 if (aRule.isAbstract ())
334 {
335
336 return null;
337 }
338
339 final PSRulepure/model/PSRule.html#PSRule">PSRule ret = new PSRule ();
340 ret.setFlag (aRule.getFlag ());
341 ret.setRich (aRule.getRichClone ());
342 ret.setLinkable (aRule.getLinkableClone ());
343
344 ret.setContext (m_aQueryBinding.getWithParamTextsReplaced (aRule.getContext (), aParamValueMap));
345 ret.setID (aIDPool.getUniqueID (aRule.getID ()));
346 if (aRule.hasAnyInclude ())
347 throw new SchematronPreprocessException ("Cannot preprocess <rule> with an <include>");
348 for (final PSLet aLet : aRule.getAllLets ())
349 ret.addLet (aLet.getClone ());
350 _resolveRuleContent (aRule.getAllContentElements (), aLookup, aIDPool, aParamValueMap, ret);
351 ret.addForeignElements (aRule.getAllForeignElements ());
352 ret.addForeignAttributes (aRule.getAllForeignAttributes ());
353 return ret;
354 }
355
356 @Nullable
357 private PSPatternrn">PSPattern _getPreprocessedPattern (@Nonnull final PSPattern aPattern,
358 @Nonnull final PreprocessorLookup aLookup,
359 @Nonnull final PreprocessorIDPool aIDPool) throws SchematronPreprocessException
360 {
361 if (aPattern.isAbstract ())
362 {
363
364 return null;
365 }
366
367 final PSPatterne/model/PSPattern.html#PSPattern">PSPattern ret = new PSPattern ();
368
369
370 ret.setID (aIDPool.getUniqueID (aPattern.getID ()));
371 ret.setRich (aPattern.getRichClone ());
372 if (aPattern.hasAnyInclude ())
373 throw new SchematronPreprocessException ("Cannot preprocess <pattern> with an <include>");
374 if (m_bKeepTitles && aPattern.hasTitle ())
375 ret.setTitle (aPattern.getTitle ().getClone ());
376
377 final String sIsA = aPattern.getIsA ();
378 if (sIsA != null)
379 {
380 final PSPattern aBasePattern = aLookup.getAbstractPatternOfID (sIsA);
381 if (aBasePattern == null)
382 throw new SchematronPreprocessException ("Failed to resolve the pattern denoted by is-a='" + sIsA + "'");
383
384 if (!ret.hasID ())
385 ret.setID (aIDPool.getUniqueID (aBasePattern.getID ()));
386 if (!ret.hasRich ())
387 ret.setRich (aBasePattern.getRichClone ());
388
389
390 final ICommonsNavigableMap <String, String> aParamValueMap = m_aQueryBinding.getStringReplacementMap (aPattern.getAllParams ());
391
392 for (final IPSElement aElement : aBasePattern.getAllContentElements ())
393 {
394 if (aElement instanceof PSLet)
395 ret.addLet (((PSLet) aElement).getClone ());
396 else
397 if (aElement instanceof PSRule)
398 {
399 final PSRuleematron/pure/model/PSRule.html#PSRule">PSRule aMinifiedRule = _getPreprocessedRule ((PSRule) aElement, aLookup, aIDPool, aParamValueMap);
400 if (aMinifiedRule != null)
401 ret.addRule (aMinifiedRule);
402 }
403
404
405 }
406 }
407 else
408 {
409 for (final IPSElement aElement : aPattern.getAllContentElements ())
410 {
411 if (aElement instanceof PSLet)
412 ret.addLet (((PSLet) aElement).getClone ());
413 else
414 if (aElement instanceof PSRule)
415 {
416 final PSRuleematron/pure/model/PSRule.html#PSRule">PSRule aMinifiedRule = _getPreprocessedRule ((PSRule) aElement, aLookup, aIDPool, null);
417 if (aMinifiedRule != null)
418 ret.addRule (aMinifiedRule);
419 }
420
421
422 }
423 }
424 ret.addForeignElements (aPattern.getAllForeignElements ());
425 ret.addForeignAttributes (aPattern.getAllForeignAttributes ());
426 return ret;
427 }
428
429 @Nonnull
430 private static PSDiagnosticscs">PSDiagnostics _getPreprocessedDiagnostics (@Nonnull final PSDiagnostics aDiagnostics) throws SchematronPreprocessException
431 {
432 final PSDiagnosticsdel/PSDiagnostics.html#PSDiagnostics">PSDiagnostics ret = new PSDiagnostics ();
433 if (aDiagnostics.hasAnyInclude ())
434 throw new SchematronPreprocessException ("Cannot preprocess <diagnostics> with an <include>");
435 for (final PSDiagnostic aDiagnostic : aDiagnostics.getAllDiagnostics ())
436 ret.addDiagnostic (aDiagnostic.getClone ());
437 ret.addForeignElements (aDiagnostics.getAllForeignElements ());
438 ret.addForeignAttributes (aDiagnostics.getAllForeignAttributes ());
439 return ret;
440 }
441
442
443
444
445
446
447
448
449
450
451
452
453
454 @Nullable
455 public PSSchemaSchema">PSSchema getAsMinimalSchema (@Nonnull final PSSchema aSchema) throws SchematronPreprocessException
456 {
457 ValueEnforcer.notNull (aSchema, "Schema");
458
459
460 if (aSchema.isMinimal ())
461 return aSchema;
462
463 return getForcedPreprocessedSchema (aSchema);
464 }
465
466
467
468
469
470
471
472
473
474
475
476
477
478 @Nullable
479 public PSSchemaa">PSSchema getAsPreprocessedSchema (@Nonnull final PSSchema aSchema) throws SchematronPreprocessException
480 {
481 ValueEnforcer.notNull (aSchema, "Schema");
482
483
484 if (aSchema.isPreprocessed ())
485 return aSchema;
486
487 return getForcedPreprocessedSchema (aSchema);
488 }
489
490
491
492
493
494
495
496
497
498
499
500
501
502 @Nullable
503 public PSSchemaSSchema getForcedPreprocessedSchema (@Nonnull final PSSchema aSchema) throws SchematronPreprocessException
504 {
505 ValueEnforcer.notNull (aSchema, "Schema");
506
507 final PreprocessorLookupPreprocessorLookup.html#PreprocessorLookup">PreprocessorLookup aLookup = new PreprocessorLookup (aSchema);
508 final PreprocessorIDPoolPreprocessorIDPool.html#PreprocessorIDPool">PreprocessorIDPool aIDPool = new PreprocessorIDPool ();
509
510 final PSSchemare/model/PSSchema.html#PSSchema">PSSchema ret = new PSSchema (aSchema.getResource ());
511 ret.setID (aIDPool.getUniqueID (aSchema.getID ()));
512 ret.setRich (aSchema.getRichClone ());
513 ret.setSchemaVersion (aSchema.getSchemaVersion ());
514 ret.setDefaultPhase (aSchema.getDefaultPhase ());
515 ret.setQueryBinding (aSchema.getQueryBinding ());
516 if (m_bKeepTitles && aSchema.hasTitle ())
517 ret.setTitle (aSchema.getTitle ().getClone ());
518 if (aSchema.hasAnyInclude ())
519 throw new SchematronPreprocessException ("Cannot preprocess <schema> with an <include>");
520 for (final PSNS aNS : aSchema.getAllNSs ())
521 ret.addNS (aNS.getClone ());
522
523 for (final PSLet aLet : aSchema.getAllLets ())
524 ret.addLet (aLet.getClone ());
525 for (final PSPhase aPhase : aSchema.getAllPhases ())
526 ret.addPhase (_getPreprocessedPhase (aPhase, aIDPool));
527 for (final PSPattern aPattern : aSchema.getAllPatterns ())
528 {
529 final PSPattern aMinifiedPattern = _getPreprocessedPattern (aPattern, aLookup, aIDPool);
530 if (aMinifiedPattern != null)
531 {
532
533 if (aMinifiedPattern.getRuleCount () > 0 || m_bKeepEmptyPatterns)
534 ret.addPattern (aMinifiedPattern);
535 }
536 }
537
538
539 if (aSchema.getPatternCount () == 0 && !m_bKeepEmptySchema)
540 return null;
541
542
543 if (m_bKeepDiagnostics && aSchema.hasDiagnostics ())
544 ret.setDiagnostics (_getPreprocessedDiagnostics (aSchema.getDiagnostics ()));
545 ret.addForeignElements (aSchema.getAllForeignElements ());
546 ret.addForeignAttributes (aSchema.getAllForeignAttributes ());
547 return ret;
548 }
549
550 @Override
551 public String toString ()
552 {
553 return new ToStringGenerator (this).append ("queryBinding", m_aQueryBinding)
554 .append ("keepTitles", m_bKeepTitles)
555 .append ("keepDiagnostics", m_bKeepDiagnostics)
556 .append ("keepReports", m_bKeepReports)
557 .append ("keepEmptyPatterns", m_bKeepEmptyPatterns)
558 .append ("keepEmptySchema", m_bKeepEmptySchema)
559 .getToString ();
560 }
561
562 @Nonnull
563 public static PSPreprocessor createPreprocessorWithoutInformationLoss (@Nonnull final IPSQueryBinding aQueryBinding)
564 {
565 final PSPreprocessorPreprocessor.html#PSPreprocessor">PSPreprocessor aPreprocessor = new PSPreprocessor (aQueryBinding);
566
567
568
569 aPreprocessor.setKeepReports (true);
570 aPreprocessor.setKeepDiagnostics (true);
571 aPreprocessor.setKeepTitles (true);
572
573 return aPreprocessor;
574 }
575 }