View Javadoc
1   /**
2    * Copyright (C) 2014-2018 Philip Helger (www.helger.com)
3    * philip[at]helger[dot]com
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *         http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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   * This is the pre-processor class for pure Schematron. It converts an existing
49   * schema to the minimal syntax (by default) but allows for a certain degree of
50   * customization by keeping certain elements in the resulting schema. The actual
51   * query binding is used, so that report test expressions can be converted to
52   * assertions, and to replace the content of <param> elements into actual
53   * values.
54   *
55   * @author Philip Helger
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     * @return The query binding to be used. Never <code>null</code>!
80     */
81    @Nonnull
82    public IPSQueryBinding getQueryBinding ()
83    {
84      return m_aQueryBinding;
85    }
86  
87    /**
88     * @return <code>true</code> if &lt;title&gt;-elements should be kept. Default
89     *         is {@value #DEFAULT_KEEP_TITLES}.
90     */
91    public boolean isKeepTitles ()
92    {
93      return m_bKeepTitles;
94    }
95  
96    /**
97     * Should &lt;title&gt;-elements be kept?
98     *
99     * @param bKeepTitles
100    *        <code>true</code> to keep titles, <code>false</code> otherwise.
101    * @return this for chaining
102    */
103   @Nonnull
104   public PSPreprocessor setKeepTitles (final boolean bKeepTitles)
105   {
106     m_bKeepTitles = bKeepTitles;
107     return this;
108   }
109 
110   /**
111    * @return <code>true</code> if &lt;diagnostics&gt;-elements should be kept.
112    *         Default is {@value #DEFAULT_KEEP_DIAGNOSTICS}.
113    */
114   public boolean isKeepDiagnostics ()
115   {
116     return m_bKeepDiagnostics;
117   }
118 
119   /**
120    * Should &lt;diagnostics&gt;-elements be kept?
121    *
122    * @param bKeepDiagnostics
123    *        <code>true</code> to keep diagnostics, <code>false</code> otherwise.
124    * @return this for chaining
125    */
126   @Nonnull
127   public PSPreprocessor setKeepDiagnostics (final boolean bKeepDiagnostics)
128   {
129     m_bKeepDiagnostics = bKeepDiagnostics;
130     return this;
131   }
132 
133   /**
134    * @return <code>true</code> if &lt;report&gt;-elements should be kept,
135    *         <code>false</code> if they should be converted to
136    *         &lt;assert&gt;-elements. Default is {@value #DEFAULT_KEEP_REPORTS}.
137    */
138   public boolean isKeepReports ()
139   {
140     return m_bKeepReports;
141   }
142 
143   /**
144    * Should &lt;report&gt;-elements be kept or should they be converted to
145    * &lt;assert&gt;-elements?
146    *
147    * @param bKeepReports
148    *        <code>true</code> to keep &lt;report&gt;-elements,
149    *        <code>false</code> to change them to &lt;assert&gt;-elements
150    * @return this for chaining
151    */
152   @Nonnull
153   public PSPreprocessor setKeepReports (final boolean bKeepReports)
154   {
155     m_bKeepReports = bKeepReports;
156     return this;
157   }
158 
159   /**
160    * @return <code>true</code> if &lt;pattern&gt;-elements without a rule should
161    *         be kept. Default is {@value #DEFAULT_KEEP_EMPTY_PATTERNS}.
162    */
163   public boolean isKeepEmptyPatterns ()
164   {
165     return m_bKeepEmptyPatterns;
166   }
167 
168   /**
169    * Should &lt;pattern&gt;-elements without a single rule be kept or deleted?
170    *
171    * @param bKeepEmptyPatterns
172    *        <code>true</code> to keep &lt;pattern&gt;-elements without a rule,
173    *        <code>false</code> to delete them
174    * @return this for chaining
175    */
176   @Nonnull
177   public PSPreprocessor setKeepEmptyPatterns (final boolean bKeepEmptyPatterns)
178   {
179     m_bKeepEmptyPatterns = bKeepEmptyPatterns;
180     return this;
181   }
182 
183   /**
184    * @return <code>true</code> if &lt;schema&gt;-elements without a pattern
185    *         should be kept. Default is {@value #DEFAULT_KEEP_EMPTY_SCHEMA}.
186    */
187   public boolean isKeepEmptySchema ()
188   {
189     return m_bKeepEmptySchema;
190   }
191 
192   /**
193    * Should schema objects without a pattern be kept? It makes only sense to set
194    * it to <code>false</code> if {@link #setKeepEmptyPatterns(boolean)} is also
195    * set to false, because otherwise patterns without rules are kept.
196    *
197    * @param bKeepEmptySchema
198    *        <code>true</code> to keep them, <code>false</code> to discard them.
199    * @return this
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       // ps are ignored
225     }
226     ret.addForeignElements (aPhase.getAllForeignElements ());
227     ret.addForeignAttributes (aPhase.getAllForeignAttributes ());
228     return ret;
229   }
230 
231   /**
232    * Resolve all &lt;extends&gt; elements. This method calls itself recursively
233    * until all extends elements are resolved.
234    *
235    * @param aRuleContent
236    *        A list consisting of {@link PSAssertReport} and {@link PSExtends}
237    *        objects. Never <code>null</code>.
238    * @param aLookup
239    *        The rule lookup object
240    * @throws SchematronPreprocessException
241    *         If the base rule of an extends object could not be resolved.
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         // Recursively resolve the extends of the base rule
268         _resolveRuleContent (aBaseRule.getAllContentElements (), aLookup, aIDPool, aParamValueMap, aTargetRule);
269 
270         // Copy all lets
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       // Negate the expression!
286       sTest = m_aQueryBinding.getNegatedTestExpression (sTest);
287     }
288 
289     // Keep report or make it always an assert
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       // Will be inlined
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     // abstract is always false
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       // Will be inlined
364       return null;
365     }
366 
367     final PSPatterne/model/PSPattern.html#PSPattern">PSPattern ret = new PSPattern ();
368     // abstract always false
369     // is-a must be resolved
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       // get the string replacements
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         // params must have be resolved
404         // ps are ignored
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         // params must be resolved
421         // ps are ignored
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    * Convert the passed schema to a minimal schema.
444    *
445    * @param aSchema
446    *        The schema to be made minimal. May not be <code>null</code>
447    * @return The original schema object, if it is already minimal - a minimal
448    *         copy otherwise! May be <code>null</code> if the original schema is
449    *         not yet minimal and {@link #isKeepEmptySchema()} is set to
450    *         <code>false</code>.
451    * @throws SchematronPreprocessException
452    *         In case a preprocessing error occurs
453    */
454   @Nullable
455   public PSSchemaSchema">PSSchema getAsMinimalSchema (@Nonnull final PSSchema aSchema) throws SchematronPreprocessException
456   {
457     ValueEnforcer.notNull (aSchema, "Schema");
458 
459     // Anything to do?
460     if (aSchema.isMinimal ())
461       return aSchema;
462 
463     return getForcedPreprocessedSchema (aSchema);
464   }
465 
466   /**
467    * Convert the passed schema to a pre-processed schema.
468    *
469    * @param aSchema
470    *        The schema to pre-process. May not be <code>null</code>
471    * @return The original schema object, if it is already pre-processed - a
472    *         pre-processed copy otherwise! May be <code>null</code> if the
473    *         original schema is not yet pre-processed and
474    *         {@link #isKeepEmptySchema()} is set to <code>false</code>.
475    * @throws SchematronPreprocessException
476    *         In case a preprocessing error occurs
477    */
478   @Nullable
479   public PSSchemaa">PSSchema getAsPreprocessedSchema (@Nonnull final PSSchema aSchema) throws SchematronPreprocessException
480   {
481     ValueEnforcer.notNull (aSchema, "Schema");
482 
483     // Anything to do?
484     if (aSchema.isPreprocessed ())
485       return aSchema;
486 
487     return getForcedPreprocessedSchema (aSchema);
488   }
489 
490   /**
491    * Convert the passed schema to a pre-processed schema independent if it is
492    * already minimal or not.
493    *
494    * @param aSchema
495    *        The schema to be made minimal. May not be <code>null</code>
496    * @return A minimal copy of the schema. May be <code>null</code> if the
497    *         original schema is not yet minimal and {@link #isKeepEmptySchema()}
498    *         is set to <code>false</code>.
499    * @throws SchematronPreprocessException
500    *         In case a preprocessing error occurs
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     // start ps are skipped
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         // Pattern without rules?
533         if (aMinifiedPattern.getRuleCount () > 0 || m_bKeepEmptyPatterns)
534           ret.addPattern (aMinifiedPattern);
535       }
536     }
537 
538     // Schema without patterns?
539     if (aSchema.getPatternCount () == 0 && !m_bKeepEmptySchema)
540       return null;
541 
542     // end ps are skipped
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     // Keep as much of the original information as possible, as it is not our
568     // goal to minify the scheme
569     aPreprocessor.setKeepReports (true);
570     aPreprocessor.setKeepDiagnostics (true);
571     aPreprocessor.setKeepTitles (true);
572 
573     return aPreprocessor;
574   }
575 }