001package ca.uhn.hl7v2.parser;
002
003import java.util.Arrays;
004import java.util.Collections;
005import java.util.HashSet;
006import java.util.Set;
007
008import ca.uhn.hl7v2.HapiContext;
009import ca.uhn.hl7v2.model.GenericMessage;
010import ca.uhn.hl7v2.util.Terser;
011import ca.uhn.hl7v2.util.idgenerator.FileBasedHiLoGenerator;
012import ca.uhn.hl7v2.util.idgenerator.IDGenerator;
013import ca.uhn.hl7v2.validation.ValidationContext;
014
015/**
016 * Contains configuration which will be applied to any parsers which are a part of the given
017 * HAPI Context.
018 * 
019 * @see HapiContext#getParserConfiguration()
020 */
021public class ParserConfiguration {
022
023        /**
024         * @link {@link UnexpectedSegmentBehaviourEnum#ADD_INLINE}
025         */
026        // NB if you change the default, edit the javadoc for the enum itself
027        public static final UnexpectedSegmentBehaviourEnum DEFAULT_UNEXPECTED_SEGMENT_BEHAVIOUR = UnexpectedSegmentBehaviourEnum.ADD_INLINE;
028
029        private boolean allowUnknownVersions;
030        private boolean escapeSubcomponentDelimiterInPrimitive = false;
031        private IDGenerator idGenerator = new FileBasedHiLoGenerator();
032        private String myDefaultObx2Type;
033        private boolean myEncodeEmptyMandatorySegments = true;
034        private final Set<String> myForcedEncode = new HashSet<>();
035        private String myInvalidObx2Type;
036        private UnexpectedSegmentBehaviourEnum myUnexpectedSegmentBehaviour;
037        private boolean nonGreedyMode = false;
038        private boolean prettyPrintWhenEncodingXml = true;
039        private boolean validating = true;
040    private Escaping escaping = new DefaultEscaping();
041        private boolean xmlDisableWhitespaceTrimmingOnAllNodes = false;
042        private Set<String> xmlDisableWhitespaceTrimmingOnNodeNames = Collections.emptySet();
043    private String myInvalidMfe5Type;
044    private String myDefaultMfe5Type;
045
046        /**
047         * <p>
048         * Forces the parser to encode certain segments/fields, even if they contain
049         * no content. This method may be called multiple times with multiple path
050         * definitions, and each path definition contains the path to the segment or
051         * field which needs to be forced.
052         * </p>
053         * <p>
054         * Path definitions are similar in format to {@link Terser Terser} paths.
055         * They contain a slash-separated lookup path to reach a given segment, and
056         * optionally a field number. The following are examples of paths which
057         * could be added here, as well as the sample output for an otherwise empty
058         * ORU^R01 message:
059         * </p>
060         * <table cellpadding="2" cellspacing="2" border="0">
061         * <thead>
062         * <tr>
063         * <th style="background: #FFA0FF;">Forced Encode Path</th>
064         * <th style="background: #FFA0FF;">Encode Output</th>
065         * </tr>
066         * </thead>
067         * <tr>
068         * <td>None (for illustration purposes)</td>
069         * <td style=" font-family: monospace;">
070         * MSH|^~\&amp;|||||||ORU^R01^ORU_R01||T|2.4</td>
071         * </tr>
072         * <tr>
073         * <td style="background: #E0E0E0;">PATIENT_RESULT/ORDER_OBSERVATION/ORC</td>
074         * <td style="background: #E0E0E0; font-family: monospace;">
075         * MSH|^~\&amp;|||||||ORU^R01^ORU_R01||T|2.4<br>
076         * ORC|</td>
077         * </tr>
078         * <tr>
079         * <td>PATIENT_RESULT/ORDER_OBSERVATION/ORC-4</td>
080         * <td style=" font-family: monospace;">
081         * MSH|^~\&amp;|||||||ORU^R01^ORU_R01||T|2.4<br>
082         * ORC||||</td>
083         * </tr>
084         * <tr>
085         * <td style="background: #E0E0E0;">PATIENT_RESULT/ORDER_OBSERVATION/ORC-4-2
086         * </td>
087         * <td style="background: #E0E0E0; font-family: monospace;">
088         * MSH|^~\&amp;|||||||ORU^R01^ORU_R01||T|2.4<br>
089         * ORC||||^</td>
090         * </tr>
091         * </table>
092         * <p>
093         * While empty segments do not generally have any meaning according to HL7,
094         * this may be useful when transmitting to systems which rely on segments
095         * being received even if they have no content.
096         * </p>
097         * <p>
098         * Note that this configuration item currently only applies to
099         * {@link PipeParser}
100         * </p>
101         *
102     * @param theForcedEncode path definition
103         * @since 2.0
104         */
105        public void addForcedEncode(String theForcedEncode) {
106                if (theForcedEncode == null) {
107                        throw new NullPointerException("forced encode may not be null");
108                }
109
110                int lastSlashIndex = theForcedEncode.lastIndexOf('/');
111                lastSlashIndex = Math.max(lastSlashIndex, 0);
112
113                if (lastSlashIndex == 0) {
114                        if (!theForcedEncode.matches("[A-Z0-9]{3}(-[0-9]+){0,2}$")) {
115                                throw new IllegalArgumentException("Definition must end with a segment name or field lookup, e.g. MSH or MSH-2");
116                        }
117                } else {
118                        if (lastSlashIndex == theForcedEncode.length() || !theForcedEncode.substring(lastSlashIndex + 1).matches("[A-Z0-9]{3}(-[0-9]+){0,2}$")) {
119                                throw new IllegalArgumentException("Definition must end with a segment name or field lookup, e.g. MSH or MSH-2");
120                        }
121                }
122                myForcedEncode.add(theForcedEncode);
123        }
124
125        boolean determineForcedEncodeIncludesTerserPath(String theTerserPath) {
126                for (String next : getForcedEncode()) {
127                        if (next.startsWith(theTerserPath)) {
128                                return true;
129                        }
130                }
131                return false;
132        }
133
134        int determineForcedFieldNumForTerserPath(String theCurrentTerserPath) {
135                int forceUpToFieldNum = 0;
136                for (String nextPath : getForcedEncode()) {
137                        if (nextPath.startsWith(theCurrentTerserPath) && nextPath.length() > theCurrentTerserPath.length()) {
138                                int endOfFieldDef = nextPath.indexOf('-', theCurrentTerserPath.length() + 1);
139                                if (endOfFieldDef == -1) {
140                                        endOfFieldDef = nextPath.length();
141                                }
142                                String fieldNumString = nextPath.substring(theCurrentTerserPath.length() + 1, endOfFieldDef);
143                                forceUpToFieldNum = Math.max(forceUpToFieldNum, Integer.parseInt(fieldNumString));
144                        }
145                }
146                return forceUpToFieldNum;
147        }
148
149        /**
150         * Returns the default datatype ("ST", "NM", etc) for an OBX segment with a
151         * missing OBX-2 value
152         * 
153         * @return Returns the default datatype ("ST", "NM", etc) for an OBX segment
154         *         with a missing OBX-2 value
155         * @see #setDefaultObx2Type(String)
156         */
157        public String getDefaultObx2Type() {
158                return myDefaultObx2Type;
159        }
160
161    /**
162     * Returns the default datatype ("ST", "NM", etc) for an MFE segment with a
163     * missing MFE-5 value
164     *
165     * @return Returns the default datatype ("ST", "NM", etc) for an OBX segment
166     *         with a missing MFE-5 value
167     * @see #setDefaultMfe5Type(String)
168     */
169    public String getDefaultMfe5Type() {
170        return myDefaultMfe5Type;
171    }
172
173        /**
174         * @return Returns the forced encode strings added by
175         *         {@link #addForcedEncode(String)}
176         * 
177         * @see #addForcedEncode(String)
178         * @since 1.3
179         */
180        public Set<String> getForcedEncode() {
181                return Collections.unmodifiableSet(myForcedEncode);
182        }
183
184        /**
185         * @return the ID Generator to be used for generating IDs for new messages
186         */
187        public IDGenerator getIdGenerator() {
188                return idGenerator;
189        }
190
191        /**
192         * Returns the value provides a default datatype ("ST", "NM", etc) for an
193         * OBX segment with an invalid OBX-2 value.
194         * 
195         * @return Returns the value provides a default datatype ("ST", "NM", etc)
196         *         for an OBX segment with an invalid OBX-2 value.
197         * @see #setInvalidObx2Type(String)
198         */
199        public String getInvalidObx2Type() {
200                return myInvalidObx2Type;
201        }
202
203    /**
204     * Returns the value provides a default datatype ("ST", "NM", etc) for an
205     * MFE segment with an invalid MFE-5 value.
206     *
207     * @return Returns the value provides a default datatype ("ST", "NM", etc)
208     *         for an MFE segment with an invalid MFE-5 value.
209     * @see #setInvalidMfe5Type(String)
210     */
211    public String getInvalidMfe5Type() {
212        return myInvalidMfe5Type;
213    }
214
215
216        /**
217         * Returns the behaviour to use when parsing a message and a nonstandard
218         * segment is found. Default is
219         * {@link #DEFAULT_UNEXPECTED_SEGMENT_BEHAVIOUR}
220     *
221     * @return the behaviour to use when a nonstandard egment is found
222         */
223        public UnexpectedSegmentBehaviourEnum getUnexpectedSegmentBehaviour() {
224                if (myUnexpectedSegmentBehaviour == null) {
225                        myUnexpectedSegmentBehaviour = DEFAULT_UNEXPECTED_SEGMENT_BEHAVIOUR;
226                }
227                return myUnexpectedSegmentBehaviour;
228        }
229
230        /**
231         * @see #setXmlDisableWhitespaceTrimmingOnNodeNames(Set)
232         */
233        public Set<String> getXmlDisableWhitespaceTrimmingOnNodeNames() {
234                return xmlDisableWhitespaceTrimmingOnNodeNames;
235        }
236
237        /**
238         * If set to <code>true</code> (default is <code>false</code>) the parser
239         * will allow messages to parse, even if they contain a version which is not
240         * known to the parser. When operating in this mode, if a message arrives
241         * with an unknown version string, the parser will attempt to parse it using
242         * a {@link GenericMessage Generic Message} class instead of a specific HAPI
243         * structure class. Default is <code>false</code>.
244     *
245     * @return true if parsing messages with unknown versions is allowed
246         */
247        public boolean isAllowUnknownVersions() {
248                return this.allowUnknownVersions;
249        }
250
251        /**
252     * Returns <code>true</code> if empty segments should still be encoded
253     * if they are mandatory within their message structure.  Default is <code>false</code>.
254         * @return <code>true</code> if empty segments should still be encoded
255     *
256         * @see #setEncodeEmptyMandatoryFirstSegments(boolean)
257         */
258        public boolean isEncodeEmptyMandatorySegments() {
259                return myEncodeEmptyMandatorySegments;
260        }
261
262        /**
263     * Returns code>true</code> if subcomponent delimiters in OBX-5 shall be
264     *         ignored. Default is <code>false</code>.
265         * @return <code>true</code> if subcomponent delimiters in OBX-5 shall be
266         *         ignored
267         */
268        public boolean isEscapeSubcomponentDelimiterInPrimitive() {
269                return escapeSubcomponentDelimiterInPrimitive;
270        }
271
272        /**
273         * Returns <code>true</code> if the parser should parse in non-greedy mode. Default
274         * is <code>false</code>
275         * 
276         * @see #setNonGreedyMode(boolean) for an explanation of non-greedy mode
277         */
278        public boolean isNonGreedyMode() {
279                return nonGreedyMode;
280        }
281
282        /**
283         * If set to <code>true</code> (which is the default), {@link XMLParser XML Parsers}
284         * will attempt to pretty-print the XML they generate. This means the messages will look
285         * nicer to humans, but may take up slightly more space/bandwidth.
286         */
287        public boolean isPrettyPrintWhenEncodingXml() {
288                return prettyPrintWhenEncodingXml;
289        }
290
291        /**
292     * Returns <code>true</code> if the parser validates using a configured
293     *         {@link ValidationContext}. Default is <code>true</code>.
294         * @return <code>true</code> if the parser validates using a configured
295         *         {@link ValidationContext}
296         */
297        public boolean isValidating() {
298                return validating;
299        }
300
301        /**
302         * @see #setXmlDisableWhitespaceTrimmingOnAllNodes(boolean)
303         */
304        public boolean isXmlDisableWhitespaceTrimmingOnAllNodes() {
305                return xmlDisableWhitespaceTrimmingOnAllNodes;
306        }
307
308        /**
309         * Removes a forced encode entry
310         *
311     * @param theForcedEncode path definition to be removed
312         * @see #addForcedEncode(String)
313         * @since 1.3
314         */
315        public void removeForcedEncode(String theForcedEncode) {
316                if (theForcedEncode == null) {
317                        throw new NullPointerException("forced encode may not be null");
318                }
319
320                myForcedEncode.remove(theForcedEncode);
321        }
322
323        /**
324         * If set to <code>true</code> (default is <code>false</code>) the parser
325         * will allow messages to parse, even if they contain a version which is not
326         * known to the parser. When operating in this mode, if a message arrives
327         * with an unknown version string, the parser will attempt to parse it using
328         * a {@link GenericMessage Generic Message} class instead of a specific HAPI
329         * structure class.
330     *
331     * @param theAllowUnknownVersions true if parsing unknown versions shall be allowed
332         */
333        public void setAllowUnknownVersions(boolean theAllowUnknownVersions) {
334                allowUnknownVersions = theAllowUnknownVersions;
335        }
336
337        /**
338         * <p>
339         * If this property is set, the value provides a default datatype ("ST",
340         * "NM", etc) for an OBX segment with a missing OBX-2 value. This is useful
341         * when parsing messages from systems which do not correctly populate OBX-2.
342         * </p>
343         * <p>
344         * For example, if this property is set to "ST", and the following OBX
345         * segment is encountered:
346         * 
347         * <pre>
348         * OBX|||||This is a value
349         * </pre>
350         * 
351         * It will be parsed as though it had read:
352         * 
353         * <pre>
354         * OBX||ST|||This is a value
355         * </pre>
356         * 
357         * </p>
358         * <p>
359         * Note that this configuration can also be set globally using the system
360         * property {@link FixFieldDataType#DEFAULT_OBX2_TYPE_PROP}, but any value provided to
361         * {@link ParserConfiguration} takes priority over the system property.
362         * </p>
363         * 
364         * @param theDefaultObx2Type
365         *            If this property is set, the value provides a default datatype
366         *            ("ST", "NM", etc) for an OBX segment with a missing OBX-2
367         *            value
368         * @see #setInvalidObx2Type(String)
369         * @see FixFieldDataType#INVALID_OBX2_TYPE_PROP
370         */
371        public void setDefaultObx2Type(String theDefaultObx2Type) {
372                myDefaultObx2Type = theDefaultObx2Type;
373        }
374
375    /**
376     * <p>
377     * If this property is set, the value provides a default datatype ("ST",
378     * "NM", etc) for an MFE segment with a missing MFE-5 value. This is useful
379     * when parsing messages from systems which do not correctly populate MFE-5.
380     * </p>
381     * <p>
382     * For example, if this property is set to "ST", and the following MFE
383     * segment is encountered:
384     *
385     * <pre>
386     * MFE||||This is a value
387     * </pre>
388     *
389     * It will be parsed as though it had read:
390     *
391     * <pre>
392     * MFE||||This is a value|ST
393     * </pre>
394     *
395     * </p>
396     * <p>
397     * Note that this configuration can also be set globally using the system
398     * property {@link FixFieldDataType#DEFAULT_MFE5_TYPE_PROP}, but any value provided to
399     * {@link ParserConfiguration} takes priority over the system property.
400     * </p>
401     *
402     * @param theDefaultMfe5Type
403     *            If this property is set, the value provides a default datatype
404     *            ("ST", "NM", etc) for an MFE segment with a missing MFE-5
405     *            value
406     * @see #setInvalidMfe5Type(String)
407     * @see FixFieldDataType#DEFAULT_MFE5_TYPE_PROP
408     */
409    public void setDefaultMfe5Type(String theDefaultMfe5Type) {
410        myDefaultMfe5Type = theDefaultMfe5Type;
411    }
412
413        /**
414         * <p>
415         * If set to <code>true</code> (default is <code>true</code>), when encoding
416         * a group using the PipeParser where the first segment is required, but no
417         * data has been populated in that segment, the empty segment is now still
418         * encoded if needed as a blank segment in order to give parsers a hint
419         * about which group subsequent segments are in. This helps to ensure that
420         * messages can be "round tripped", meaning that a message which is parsed,
421         * encoded, and then re-parsed should contain exactly the same structure
422         * from beginning to end.
423         * </p>
424         * <p>
425         * </p>
426         * For example, in an ORU^R01 message with a populated OBX segment, but no
427         * data in the mandatory OBR segment which begins the ORDER_OBSERVATION
428         * group the message would still contain an empty OBR segment when encoded:
429         * 
430         * <pre>
431         *      MSH|^~\&|REG|W|||201103230042||ORU^R01|32153168|P|2.5
432         *      OBR|
433         *      OBX||ST|||Value Data
434         * </pre>
435         * 
436         * Previously, the following encoding would have occurred, which would have
437         * incorrectly been parsed as having a custom OBX segment instead of having
438         * a normal ORDER_OBSERVATION group:
439         * 
440         * <pre>
441         *      MSH|^~\&|REG|W|||201103230042||ORU^R01|32153168|P|2.5
442         *      OBX||ST|||Value Data
443         * </pre>
444         * 
445         * @param theEncodeEmptyMandatorySegments
446         *            If set to <code>true</code> (default is <code>true</code>),
447         *            when encoding a group using the PipeParser where the first
448         *            segment is required, but no data has been populated in that
449         *            segment, the empty segment is now still encoded if needed as a
450         *            blank segment in order to give parsers a hint about which
451         *            group subsequent segments are in
452         */
453        public void setEncodeEmptyMandatoryFirstSegments(boolean theEncodeEmptyMandatorySegments) {
454                myEncodeEmptyMandatorySegments = theEncodeEmptyMandatorySegments;
455        }
456
457        /**
458         * Set to <code>true</code> if subcomponent delimiters in OBX-5 shall be
459         * ignored
460     * @param escapeSubcomponentDelimiterInPrimitive boolean flag to enable or disable this behavior
461         */
462        public void setEscapeSubcomponentDelimiterInPrimitive(boolean escapeSubcomponentDelimiterInPrimitive) {
463                this.escapeSubcomponentDelimiterInPrimitive = escapeSubcomponentDelimiterInPrimitive;
464        }
465
466        /**
467         * @param idGenerator
468         *            the {@link IDGenerator} to be used for generating IDs for new
469         *            messages, preferable initialized using the methods described
470         *            in IDGeneratorFactory.
471         * 
472         * @see IDGenerator
473         */
474        public void setIdGenerator(IDGenerator idGenerator) {
475                this.idGenerator = idGenerator;
476        }
477
478        /**
479         * <p>
480         * If this property is set, the value provides a default datatype ("ST",
481         * "NM", etc) for an OBX segment with an invalid OBX-2 value. This is useful
482         * when parsing messages from systems which do not correctly populate OBX-2.
483         * </p>
484         * <p>
485         * For example, if this property is set to "ST", and the following OBX
486         * segment is encountered:
487         * 
488         * <pre>
489         * OBX||INVALID|||This is a value
490         * </pre>
491         * 
492         * It will be parsed as though it had read:
493         * 
494         * <pre>
495         * OBX||ST|||This is a value
496         * </pre>
497         * 
498         * </p>
499         * <p>
500         * Note that this configuration can also be set globally using the system
501         * property {@link FixFieldDataType#INVALID_OBX2_TYPE_PROP}, but any value provided to
502         * {@link ParserConfiguration} takes priority over the system property.
503         * </p>
504         * 
505         * @param theInvalidObx2Type
506         *            If this property is set, the value provides a default datatype
507         *            ("ST", "NM", etc) for an OBX segment with an invalid OBX-2
508         *            value. This is useful when parsing messages from systems which
509         *            do not correctly populate OBX-2.
510         * @see ParserConfiguration#setDefaultObx2Type(String)
511         * @see FixFieldDataType#DEFAULT_OBX2_TYPE_PROP
512         */
513        public void setInvalidObx2Type(String theInvalidObx2Type) {
514                myInvalidObx2Type = theInvalidObx2Type;
515        }
516
517    /**
518     * <p>
519     * If this property is set, the value provides a default datatype ("ST",
520     * "NM", etc) for an MFE segment with an invalid MFE-5 value. This is useful
521     * when parsing messages from systems which do not correctly populate MFE-5.
522     * </p>
523     * <p>
524     * For example, if this property is set to "ST", and the following MFE
525     * segment is encountered:
526     *
527     * <pre>
528     * MFE||||This is a value|INVALID
529     * </pre>
530     *
531     * It will be parsed as though it had read:
532     *
533     * <pre>
534     * MFE||||This is a value|ST
535     * </pre>
536     *
537     * </p>
538     * <p>
539     * Note that this configuration can also be set globally using the system
540     * property {@link FixFieldDataType#INVALID_MFE5_TYPE_PROP}, but any value provided to
541     * {@link ParserConfiguration} takes priority over the system property.
542     * </p>
543     *
544     * @param theInvalidMfe5Type
545     *            If this property is set, the value provides a default datatype
546     *            ("ST", "NM", etc) for an MFE segment with an invalid MFE-5
547     *            value. This is useful when parsing messages from systems which
548     *            do not correctly populate MFE-5.
549     * @see ParserConfiguration#setDefaultMfe5Type(String)
550     * @see FixFieldDataType#INVALID_MFE5_TYPE_PROP
551     */
552    public void setInvalidMfe5Type(String theInvalidMfe5Type) {
553        myInvalidMfe5Type = theInvalidMfe5Type;
554    }
555
556        /**
557         * If set to <code>true</code> (default is <code>false</code>), pipe parser will be
558         * put in non-greedy mode. This setting applies only to {@link PipeParser Pipe Parsers} and
559         * will have no effect on {@link XMLParser XML Parsers}.
560         * 
561         * <p>
562         * In non-greedy mode, if the message structure being parsed has an ambiguous
563         * choice of where to put a segment because there is a segment matching the
564         * current segment name in both a later position in the message, and
565         * in an earlier position as a part of a repeating group, the earlier
566         * position will be chosen.
567         * </p>
568         * <p>
569         * This is perhaps best explained with an example. Consider the following structure:
570         * </p>
571         * <pre>
572         * MSH
573         * GROUP_1 (start)
574         * {
575         *    AAA
576         *    BBB
577         *    GROUP_2 (start)
578         *    {
579         *       AAA
580         *    }
581         *    GROUP_2 (end)
582         * }
583         * GROUP_1 (end)
584         * </pre>
585         * <p>
586         * </p>
587         * For the above example, consider a message containing the following segments:<br/>
588         * <code>MSH<br/>
589         * AAA<br/>
590         * BBB<br/>
591         * AAA</code>
592         * </p>
593         * <p>
594         * In this example, when the second AAA segment is encountered, there are two
595         * possible choices. It would be placed in GROUP_2, or it could be placed in 
596         * a second repetition of GROUP_1. By default it will be placed in GROUP_2, but
597         * in non-greedy mode it will be put in a new repetition of GROUP_1.
598         * </p>
599         * <p>
600         * This mode is useful for example when parsing OML^O21 messages containing
601         * multiple orders.
602         * </p>
603         */
604        public void setNonGreedyMode(boolean theNonGreedyMode) {
605                nonGreedyMode = theNonGreedyMode;
606        }
607
608        /**
609         * If set to <code>true</code> (which is the default), {@link XMLParser XML Parsers}
610         * will attempt to pretty-print the XML they generate. This means the messages will look
611         * nicer to humans, but may take up slightly more space/bandwidth.
612         */
613        public void setPrettyPrintWhenEncodingXml(boolean thePrettyPrintWhenEncodingXml) {
614                prettyPrintWhenEncodingXml = thePrettyPrintWhenEncodingXml;
615        }
616
617        /**
618         * Sets the behaviour to use when parsing a message and a nonstandard
619         * segment is found
620     *
621     * @param theUnexpectedSegmentBehaviour behaviour to use when a nonstandard segment is found
622     */
623        public void setUnexpectedSegmentBehaviour(UnexpectedSegmentBehaviourEnum theUnexpectedSegmentBehaviour) {
624                if (theUnexpectedSegmentBehaviour == null) {
625                        throw new NullPointerException("UnexpectedSegmentBehaviour can not be null");
626                }
627                myUnexpectedSegmentBehaviour = theUnexpectedSegmentBehaviour;
628        }
629
630        /**
631         * Determines whether the parser validates using a configured
632         * {@link ValidationContext} or not. This allows to disable message
633         * validation although a validation context is defined.
634         * 
635         * @param validating
636         *            <code>true</code> if parser shall validate, <code>false</code>
637         *            if not
638         */
639        public void setValidating(boolean validating) {
640                this.validating = validating;
641        }
642
643    public Escaping getEscaping() {
644        return escaping;
645    }
646
647    /**
648     * Sets an escaping strategy
649     * @param escaping escaping strategy instance
650     */
651    public void setEscaping(Escaping escaping) {
652        if (escaping == null) {
653            throw new NullPointerException("Escaping can not be null");
654        }
655        this.escaping = escaping;
656    }
657        /**
658         * Configures the XML Parser to treat all whitespace within text nodes as literal, meaning that
659         * line breaks, tabs, multiple spaces, etc. will be preserved. If set to <code>true</code>, any values
660         * passed to {@link #setXmlDisableWhitespaceTrimmingOnNodeNames(Set)} will be superceded since all
661         * whitespace will be treated as literal.
662         * <p>
663         * Default is <b>false</b>
664         * </p> 
665         */
666        public void setXmlDisableWhitespaceTrimmingOnAllNodes(boolean theXmlDisableWhitespaceTrimmingOnAllNodes) {
667                this.xmlDisableWhitespaceTrimmingOnAllNodes = theXmlDisableWhitespaceTrimmingOnAllNodes;
668        }
669
670        /**
671         * Configures the XML Parser to treat all whitespace within the given nodes as literal, meaning that
672         * line breaks, tabs, multiple spaces, etc. will be preserved. This method takes individual XML node names
673         * as arguments (e.g. "HD.2", or "TX.1").
674         * <p>
675         * Default is <b>none</b>
676         * </p> 
677         */
678        public void setXmlDisableWhitespaceTrimmingOnNodeNames(Set<String> theXmlDisableWhitespaceTrimmingOnNodeNames) {
679                if (theXmlDisableWhitespaceTrimmingOnNodeNames==null) {
680                        this.xmlDisableWhitespaceTrimmingOnNodeNames = Collections.emptySet();
681                } else {
682                        this.xmlDisableWhitespaceTrimmingOnNodeNames = theXmlDisableWhitespaceTrimmingOnNodeNames;
683                }
684        }
685
686        /**
687         * Configures the XML Parser to treat all whitespace within the given nodes as literal, meaning that
688         * line breaks, tabs, multiple spaces, etc. will be preserved. This method takes individual XML node names
689         * as arguments (e.g. "HD.2", or "TX.1").
690         * <p>
691         * Default is <b>none</b>
692         * </p> 
693         */
694        public void setXmlDisableWhitespaceTrimmingOnNodeNames(String... theKeepAsOriginalNodes) {
695                if (theKeepAsOriginalNodes==null) {
696                        setXmlDisableWhitespaceTrimmingOnNodeNames((Set<String>)null);
697                } else {
698                        setXmlDisableWhitespaceTrimmingOnNodeNames(new HashSet<>(Arrays.asList(theKeepAsOriginalNodes)));
699                }
700        }
701
702}