View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URI;
25  import java.util.ArrayDeque;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Deque;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  import java.util.Optional;
35  
36  import javax.xml.parsers.ParserConfigurationException;
37  
38  import org.xml.sax.Attributes;
39  import org.xml.sax.InputSource;
40  import org.xml.sax.SAXException;
41  import org.xml.sax.SAXParseException;
42  
43  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
44  import com.puppycrawl.tools.checkstyle.api.Configuration;
45  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
46  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
47  
48  /**
49   * Loads a configuration from a standard configuration XML file.
50   *
51   * @author Oliver Burn
52   */
53  public final class ConfigurationLoader {
54  
55      /**
56       * Enum to specify behaviour regarding ignored modules.
57       */
58      public enum IgnoredModulesOptions {
59          /**
60           * Omit ignored modules.
61           */
62          OMIT,
63  
64          /**
65           * Execute ignored modules.
66           */
67          EXECUTE
68      }
69  
70      /** Format of message for sax parse exception. */
71      private static final String SAX_PARSE_EXCEPTION_FORMAT = "%s - %s:%s:%s";
72  
73      /** The public ID for version 1_0 of the configuration dtd. */
74      private static final String DTD_PUBLIC_ID_1_0 =
75          "-//Puppy Crawl//DTD Check Configuration 1.0//EN";
76  
77      /** The resource for version 1_0 of the configuration dtd. */
78      private static final String DTD_CONFIGURATION_NAME_1_0 =
79          "com/puppycrawl/tools/checkstyle/configuration_1_0.dtd";
80  
81      /** The public ID for version 1_1 of the configuration dtd. */
82      private static final String DTD_PUBLIC_ID_1_1 =
83          "-//Puppy Crawl//DTD Check Configuration 1.1//EN";
84  
85      /** The resource for version 1_1 of the configuration dtd. */
86      private static final String DTD_CONFIGURATION_NAME_1_1 =
87          "com/puppycrawl/tools/checkstyle/configuration_1_1.dtd";
88  
89      /** The public ID for version 1_2 of the configuration dtd. */
90      private static final String DTD_PUBLIC_ID_1_2 =
91          "-//Puppy Crawl//DTD Check Configuration 1.2//EN";
92  
93      /** The resource for version 1_2 of the configuration dtd. */
94      private static final String DTD_CONFIGURATION_NAME_1_2 =
95          "com/puppycrawl/tools/checkstyle/configuration_1_2.dtd";
96  
97      /** The public ID for version 1_3 of the configuration dtd. */
98      private static final String DTD_PUBLIC_ID_1_3 =
99          "-//Puppy Crawl//DTD Check Configuration 1.3//EN";
100 
101     /** The resource for version 1_3 of the configuration dtd. */
102     private static final String DTD_CONFIGURATION_NAME_1_3 =
103         "com/puppycrawl/tools/checkstyle/configuration_1_3.dtd";
104 
105     /** Prefix for the exception when unable to parse resource. */
106     private static final String UNABLE_TO_PARSE_EXCEPTION_PREFIX = "unable to parse"
107             + " configuration stream";
108 
109     /** Dollar sign literal. */
110     private static final char DOLLAR_SIGN = '$';
111 
112     /** The SAX document handler. */
113     private final InternalLoader saxHandler;
114 
115     /** Property resolver. **/
116     private final PropertyResolver overridePropsResolver;
117     /** The loaded configurations. **/
118     private final Deque<DefaultConfiguration> configStack = new ArrayDeque<>();
119 
120     /** Flags if modules with the severity 'ignore' should be omitted. */
121     private final boolean omitIgnoredModules;
122 
123     /** The thread mode configuration. */
124     private final ThreadModeSettings threadModeSettings;
125 
126     /** The Configuration that is being built. */
127     private Configuration configuration;
128 
129     /**
130      * Creates a new {@code ConfigurationLoader} instance.
131      * @param overrideProps resolver for overriding properties
132      * @param omitIgnoredModules {@code true} if ignored modules should be
133      *         omitted
134      * @throws ParserConfigurationException if an error occurs
135      * @throws SAXException if an error occurs
136      */
137     private ConfigurationLoader(final PropertyResolver overrideProps,
138                                 final boolean omitIgnoredModules)
139             throws ParserConfigurationException, SAXException {
140         this(overrideProps, omitIgnoredModules, ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE);
141     }
142 
143     /**
144      * Creates a new {@code ConfigurationLoader} instance.
145      * @param overrideProps resolver for overriding properties
146      * @param omitIgnoredModules {@code true} if ignored modules should be
147      *         omitted
148      * @param threadModeSettings the thread mode configuration
149      * @throws ParserConfigurationException if an error occurs
150      * @throws SAXException if an error occurs
151      */
152     private ConfigurationLoader(final PropertyResolver overrideProps,
153                                 final boolean omitIgnoredModules,
154                                 final ThreadModeSettings threadModeSettings)
155             throws ParserConfigurationException, SAXException {
156         saxHandler = new InternalLoader();
157         overridePropsResolver = overrideProps;
158         this.omitIgnoredModules = omitIgnoredModules;
159         this.threadModeSettings = threadModeSettings;
160     }
161 
162     /**
163      * Creates mapping between local resources and dtd ids.
164      * @return map between local resources and dtd ids.
165      */
166     private static Map<String, String> createIdToResourceNameMap() {
167         final Map<String, String> map = new HashMap<>();
168         map.put(DTD_PUBLIC_ID_1_0, DTD_CONFIGURATION_NAME_1_0);
169         map.put(DTD_PUBLIC_ID_1_1, DTD_CONFIGURATION_NAME_1_1);
170         map.put(DTD_PUBLIC_ID_1_2, DTD_CONFIGURATION_NAME_1_2);
171         map.put(DTD_PUBLIC_ID_1_3, DTD_CONFIGURATION_NAME_1_3);
172         return map;
173     }
174 
175     /**
176      * Parses the specified input source loading the configuration information.
177      * The stream wrapped inside the source, if any, is NOT
178      * explicitly closed after parsing, it is the responsibility of
179      * the caller to close the stream.
180      *
181      * @param source the source that contains the configuration data
182      * @throws IOException if an error occurs
183      * @throws SAXException if an error occurs
184      */
185     private void parseInputSource(InputSource source)
186             throws IOException, SAXException {
187         saxHandler.parseInputSource(source);
188     }
189 
190     /**
191      * Returns the module configurations in a specified file.
192      * @param config location of config file, can be either a URL or a filename
193      * @param overridePropsResolver overriding properties
194      * @return the check configurations
195      * @throws CheckstyleException if an error occurs
196      */
197     public static Configuration loadConfiguration(String config,
198             PropertyResolver overridePropsResolver) throws CheckstyleException {
199         return loadConfiguration(config, overridePropsResolver, IgnoredModulesOptions.EXECUTE);
200     }
201 
202     /**
203      * Returns the module configurations in a specified file.
204      * @param config location of config file, can be either a URL or a filename
205      * @param overridePropsResolver overriding properties
206      * @param threadModeSettings the thread mode configuration
207      * @return the check configurations
208      * @throws CheckstyleException if an error occurs
209      */
210     public static Configuration loadConfiguration(String config,
211             PropertyResolver overridePropsResolver, ThreadModeSettings threadModeSettings)
212             throws CheckstyleException {
213         return loadConfiguration(config, overridePropsResolver,
214                 IgnoredModulesOptions.EXECUTE, threadModeSettings);
215     }
216 
217     /**
218      * Returns the module configurations in a specified file.
219      *
220      * @param config location of config file, can be either a URL or a filename
221      * @param overridePropsResolver overriding properties
222      * @param omitIgnoredModules {@code true} if modules with severity
223      *            'ignore' should be omitted, {@code false} otherwise
224      * @return the check configurations
225      * @throws CheckstyleException if an error occurs
226      * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
227      * @noinspection BooleanParameter
228      */
229     @Deprecated
230     public static Configuration loadConfiguration(String config,
231         PropertyResolver overridePropsResolver, boolean omitIgnoredModules)
232             throws CheckstyleException {
233         return loadConfiguration(config, overridePropsResolver, omitIgnoredModules,
234                 ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE);
235     }
236 
237     /**
238      * Returns the module configurations in a specified file.
239      *
240      * @param config location of config file, can be either a URL or a filename
241      * @param overridePropsResolver overriding properties
242      * @param omitIgnoredModules {@code true} if modules with severity
243      *            'ignore' should be omitted, {@code false} otherwise
244      * @param threadModeSettings the thread mode configuration
245      * @return the check configurations
246      * @throws CheckstyleException if an error occurs
247      * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
248      * @noinspection BooleanParameter, WeakerAccess
249      */
250     @Deprecated
251     public static Configuration loadConfiguration(String config,
252             PropertyResolver overridePropsResolver,
253             boolean omitIgnoredModules, ThreadModeSettings threadModeSettings)
254             throws CheckstyleException {
255         // figure out if this is a File or a URL
256         final URI uri = CommonUtils.getUriByFilename(config);
257         final InputSource source = new InputSource(uri.toString());
258         return loadConfiguration(source, overridePropsResolver,
259                 omitIgnoredModules, threadModeSettings);
260     }
261 
262     /**
263      * Returns the module configurations from a specified input stream.
264      * Note that clients are required to close the given stream by themselves
265      *
266      * @param configStream the input stream to the Checkstyle configuration
267      * @param overridePropsResolver overriding properties
268      * @param omitIgnoredModules {@code true} if modules with severity
269      *            'ignore' should be omitted, {@code false} otherwise
270      * @return the check configurations
271      * @throws CheckstyleException if an error occurs
272      *
273      * @deprecated As this method does not provide a valid system ID,
274      *     preventing resolution of external entities, a
275      *     {@link #loadConfiguration(InputSource,PropertyResolver,boolean)
276      *          version using an InputSource}
277      *     should be used instead
278      * @noinspection BooleanParameter
279      */
280     @Deprecated
281     public static Configuration loadConfiguration(InputStream configStream,
282         PropertyResolver overridePropsResolver, boolean omitIgnoredModules)
283             throws CheckstyleException {
284         return loadConfiguration(new InputSource(configStream),
285                                  overridePropsResolver, omitIgnoredModules);
286     }
287 
288     /**
289      * Returns the module configurations from a specified input source.
290      * Note that if the source does wrap an open byte or character
291      * stream, clients are required to close that stream by themselves
292      *
293      * @param configSource the input stream to the Checkstyle configuration
294      * @param overridePropsResolver overriding properties
295      * @param omitIgnoredModules {@code true} if modules with severity
296      *            'ignore' should be omitted, {@code false} otherwise
297      * @return the check configurations
298      * @throws CheckstyleException if an error occurs
299      * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
300      * @noinspection BooleanParameter
301      */
302     @Deprecated
303     public static Configuration loadConfiguration(InputSource configSource,
304             PropertyResolver overridePropsResolver, boolean omitIgnoredModules)
305             throws CheckstyleException {
306         return loadConfiguration(configSource, overridePropsResolver,
307                 omitIgnoredModules, ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE);
308     }
309 
310     /**
311      * Returns the module configurations from a specified input source.
312      * Note that if the source does wrap an open byte or character
313      * stream, clients are required to close that stream by themselves
314      *
315      * @param configSource the input stream to the Checkstyle configuration
316      * @param overridePropsResolver overriding properties
317      * @param omitIgnoredModules {@code true} if modules with severity
318      *            'ignore' should be omitted, {@code false} otherwise
319      * @param threadModeSettings the thread mode configuration
320      * @return the check configurations
321      * @throws CheckstyleException if an error occurs
322      * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
323      * @noinspection BooleanParameter, WeakerAccess
324      */
325     @Deprecated
326     public static Configuration loadConfiguration(InputSource configSource,
327         PropertyResolver overridePropsResolver,
328         boolean omitIgnoredModules, ThreadModeSettings threadModeSettings)
329             throws CheckstyleException {
330         try {
331             final ConfigurationLoader loader =
332                 new ConfigurationLoader(overridePropsResolver,
333                                         omitIgnoredModules, threadModeSettings);
334             loader.parseInputSource(configSource);
335             return loader.configuration;
336         }
337         catch (final SAXParseException ex) {
338             final String message = String.format(Locale.ROOT, SAX_PARSE_EXCEPTION_FORMAT,
339                     UNABLE_TO_PARSE_EXCEPTION_PREFIX,
340                     ex.getMessage(), ex.getLineNumber(), ex.getColumnNumber());
341             throw new CheckstyleException(message, ex);
342         }
343         catch (final ParserConfigurationException | IOException | SAXException ex) {
344             throw new CheckstyleException(UNABLE_TO_PARSE_EXCEPTION_PREFIX, ex);
345         }
346     }
347 
348     /**
349      * Returns the module configurations in a specified file.
350      *
351      * @param config location of config file, can be either a URL or a filename
352      * @param overridePropsResolver overriding properties
353      * @param ignoredModulesOptions {@code OMIT} if modules with severity
354      *            'ignore' should be omitted, {@code EXECUTE} otherwise
355      * @return the check configurations
356      * @throws CheckstyleException if an error occurs
357      */
358     public static Configuration loadConfiguration(String config,
359                                                   PropertyResolver overridePropsResolver,
360                                                   IgnoredModulesOptions ignoredModulesOptions)
361             throws CheckstyleException {
362         return loadConfiguration(config, overridePropsResolver, ignoredModulesOptions,
363                 ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE);
364     }
365 
366     /**
367      * Returns the module configurations in a specified file.
368      *
369      * @param config location of config file, can be either a URL or a filename
370      * @param overridePropsResolver overriding properties
371      * @param ignoredModulesOptions {@code OMIT} if modules with severity
372      *            'ignore' should be omitted, {@code EXECUTE} otherwise
373      * @param threadModeSettings the thread mode configuration
374      * @return the check configurations
375      * @throws CheckstyleException if an error occurs
376      */
377     public static Configuration loadConfiguration(String config,
378                                                   PropertyResolver overridePropsResolver,
379                                                   IgnoredModulesOptions ignoredModulesOptions,
380                                                   ThreadModeSettings threadModeSettings)
381             throws CheckstyleException {
382         // figure out if this is a File or a URL
383         final URI uri = CommonUtils.getUriByFilename(config);
384         final InputSource source = new InputSource(uri.toString());
385         return loadConfiguration(source, overridePropsResolver,
386                 ignoredModulesOptions, threadModeSettings);
387     }
388 
389     /**
390      * Returns the module configurations from a specified input source.
391      * Note that if the source does wrap an open byte or character
392      * stream, clients are required to close that stream by themselves
393      *
394      * @param configSource the input stream to the Checkstyle configuration
395      * @param overridePropsResolver overriding properties
396      * @param ignoredModulesOptions {@code OMIT} if modules with severity
397      *            'ignore' should be omitted, {@code EXECUTE} otherwise
398      * @return the check configurations
399      * @throws CheckstyleException if an error occurs
400      */
401     public static Configuration loadConfiguration(InputSource configSource,
402                                                   PropertyResolver overridePropsResolver,
403                                                   IgnoredModulesOptions ignoredModulesOptions)
404             throws CheckstyleException {
405         return loadConfiguration(configSource, overridePropsResolver,
406                 ignoredModulesOptions, ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE);
407     }
408 
409     /**
410      * Returns the module configurations from a specified input source.
411      * Note that if the source does wrap an open byte or character
412      * stream, clients are required to close that stream by themselves
413      *
414      * @param configSource the input stream to the Checkstyle configuration
415      * @param overridePropsResolver overriding properties
416      * @param ignoredModulesOptions {@code OMIT} if modules with severity
417      *            'ignore' should be omitted, {@code EXECUTE} otherwise
418      * @param threadModeSettings the thread mode configuration
419      * @return the check configurations
420      * @throws CheckstyleException if an error occurs
421      * @noinspection WeakerAccess
422      */
423     public static Configuration loadConfiguration(InputSource configSource,
424                                                   PropertyResolver overridePropsResolver,
425                                                   IgnoredModulesOptions ignoredModulesOptions,
426                                                   ThreadModeSettings threadModeSettings)
427             throws CheckstyleException {
428         try {
429             final boolean omitIgnoreModules = ignoredModulesOptions == IgnoredModulesOptions.OMIT;
430             final ConfigurationLoader loader =
431                     new ConfigurationLoader(overridePropsResolver,
432                             omitIgnoreModules, threadModeSettings);
433             loader.parseInputSource(configSource);
434             return loader.configuration;
435         }
436         catch (final SAXParseException ex) {
437             final String message = String.format(Locale.ROOT, SAX_PARSE_EXCEPTION_FORMAT,
438                     UNABLE_TO_PARSE_EXCEPTION_PREFIX,
439                     ex.getMessage(), ex.getLineNumber(), ex.getColumnNumber());
440             throw new CheckstyleException(message, ex);
441         }
442         catch (final ParserConfigurationException | IOException | SAXException ex) {
443             throw new CheckstyleException(UNABLE_TO_PARSE_EXCEPTION_PREFIX, ex);
444         }
445     }
446 
447     /**
448      * Replaces {@code ${xxx}} style constructions in the given value
449      * with the string value of the corresponding data types.
450      *
451      * <p>Code copied from ant -
452      * http://cvs.apache.org/viewcvs/jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java
453      *
454      * @param value The string to be scanned for property references.
455      *              May be {@code null}, in which case this
456      *              method returns immediately with no effect.
457      * @param props Mapping (String to String) of property names to their
458      *              values. Must not be {@code null}.
459      * @param defaultValue default to use if one of the properties in value
460      *              cannot be resolved from props.
461      *
462      * @return the original string with the properties replaced, or
463      *         {@code null} if the original string is {@code null}.
464      * @throws CheckstyleException if the string contains an opening
465      *                           {@code ${} without a closing
466      *                           {@code }}
467      * @noinspection MethodWithMultipleReturnPoints
468      */
469     private static String replaceProperties(
470             String value, PropertyResolver props, String defaultValue)
471             throws CheckstyleException {
472         if (value == null) {
473             return null;
474         }
475 
476         final List<String> fragments = new ArrayList<>();
477         final List<String> propertyRefs = new ArrayList<>();
478         parsePropertyString(value, fragments, propertyRefs);
479 
480         final StringBuilder sb = new StringBuilder(256);
481         final Iterator<String> fragmentsIterator = fragments.iterator();
482         final Iterator<String> propertyRefsIterator = propertyRefs.iterator();
483         while (fragmentsIterator.hasNext()) {
484             String fragment = fragmentsIterator.next();
485             if (fragment == null) {
486                 final String propertyName = propertyRefsIterator.next();
487                 fragment = props.resolve(propertyName);
488                 if (fragment == null) {
489                     if (defaultValue != null) {
490                         sb.replace(0, sb.length(), defaultValue);
491                         break;
492                     }
493                     throw new CheckstyleException(
494                         "Property ${" + propertyName + "} has not been set");
495                 }
496             }
497             sb.append(fragment);
498         }
499 
500         return sb.toString();
501     }
502 
503     /**
504      * Parses a string containing {@code ${xxx}} style property
505      * references into two lists. The first list is a collection
506      * of text fragments, while the other is a set of string property names.
507      * {@code null} entries in the first list indicate a property
508      * reference from the second list.
509      *
510      * <p>Code copied from ant -
511      * http://cvs.apache.org/viewcvs/jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java
512      *
513      * @param value     Text to parse. Must not be {@code null}.
514      * @param fragments List to add text fragments to.
515      *                  Must not be {@code null}.
516      * @param propertyRefs List to add property names to.
517      *                     Must not be {@code null}.
518      *
519      * @throws CheckstyleException if the string contains an opening
520      *                           {@code ${} without a closing
521      *                           {@code }}
522      */
523     private static void parsePropertyString(String value,
524                                            List<String> fragments,
525                                            List<String> propertyRefs)
526             throws CheckstyleException {
527         int prev = 0;
528         //search for the next instance of $ from the 'prev' position
529         int pos = value.indexOf(DOLLAR_SIGN, prev);
530         while (pos >= 0) {
531 
532             //if there was any text before this, add it as a fragment
533             if (pos > 0) {
534                 fragments.add(value.substring(prev, pos));
535             }
536             //if we are at the end of the string, we tack on a $
537             //then move past it
538             if (pos == value.length() - 1) {
539                 fragments.add(String.valueOf(DOLLAR_SIGN));
540                 prev = pos + 1;
541             }
542             else if (value.charAt(pos + 1) == '{') {
543                 //property found, extract its name or bail on a typo
544                 final int endName = value.indexOf('}', pos);
545                 if (endName == -1) {
546                     throw new CheckstyleException("Syntax error in property: "
547                                                     + value);
548                 }
549                 final String propertyName = value.substring(pos + 2, endName);
550                 fragments.add(null);
551                 propertyRefs.add(propertyName);
552                 prev = endName + 1;
553             }
554             else {
555                 if (value.charAt(pos + 1) == DOLLAR_SIGN) {
556                     //backwards compatibility two $ map to one mode
557                     fragments.add(String.valueOf(DOLLAR_SIGN));
558                     prev = pos + 2;
559                 }
560                 else {
561                     //new behaviour: $X maps to $X for all values of X!='$'
562                     fragments.add(value.substring(pos, pos + 2));
563                     prev = pos + 2;
564                 }
565             }
566 
567             //search for the next instance of $ from the 'prev' position
568             pos = value.indexOf(DOLLAR_SIGN, prev);
569         }
570         //no more $ signs found
571         //if there is any tail to the file, append it
572         if (prev < value.length()) {
573             fragments.add(value.substring(prev));
574         }
575     }
576 
577     /**
578      * Implements the SAX document handler interfaces, so they do not
579      * appear in the public API of the ConfigurationLoader.
580      */
581     private final class InternalLoader
582         extends XmlLoader {
583         /** Module elements. */
584         private static final String MODULE = "module";
585         /** Name attribute. */
586         private static final String NAME = "name";
587         /** Property element. */
588         private static final String PROPERTY = "property";
589         /** Value attribute. */
590         private static final String VALUE = "value";
591         /** Default attribute. */
592         private static final String DEFAULT = "default";
593         /** Name of the severity property. */
594         private static final String SEVERITY = "severity";
595         /** Name of the message element. */
596         private static final String MESSAGE = "message";
597         /** Name of the message element. */
598         private static final String METADATA = "metadata";
599         /** Name of the key attribute. */
600         private static final String KEY = "key";
601 
602         /**
603          * Creates a new InternalLoader.
604          * @throws SAXException if an error occurs
605          * @throws ParserConfigurationException if an error occurs
606          */
607         InternalLoader()
608                 throws SAXException, ParserConfigurationException {
609             super(createIdToResourceNameMap());
610         }
611 
612         @Override
613         public void startElement(String uri,
614                                  String localName,
615                                  String qName,
616                                  Attributes attributes)
617                 throws SAXException {
618             if (qName.equals(MODULE)) {
619                 //create configuration
620                 final String originalName = attributes.getValue(NAME);
621                 final String name = threadModeSettings.resolveName(originalName);
622                 final DefaultConfiguration conf =
623                     new DefaultConfiguration(name, threadModeSettings);
624 
625                 if (configuration == null) {
626                     configuration = conf;
627                 }
628 
629                 //add configuration to it's parent
630                 if (!configStack.isEmpty()) {
631                     final DefaultConfiguration top =
632                         configStack.peek();
633                     top.addChild(conf);
634                 }
635 
636                 configStack.push(conf);
637             }
638             else if (qName.equals(PROPERTY)) {
639                 //extract value and name
640                 final String value;
641                 try {
642                     value = replaceProperties(attributes.getValue(VALUE),
643                         overridePropsResolver, attributes.getValue(DEFAULT));
644                 }
645                 catch (final CheckstyleException ex) {
646                     // -@cs[IllegalInstantiation] SAXException is in the overridden method signature
647                     throw new SAXException(ex);
648                 }
649                 final String name = attributes.getValue(NAME);
650 
651                 //add to attributes of configuration
652                 final DefaultConfiguration top =
653                     configStack.peek();
654                 top.addAttribute(name, value);
655             }
656             else if (qName.equals(MESSAGE)) {
657                 //extract key and value
658                 final String key = attributes.getValue(KEY);
659                 final String value = attributes.getValue(VALUE);
660 
661                 //add to messages of configuration
662                 final DefaultConfiguration top = configStack.peek();
663                 top.addMessage(key, value);
664             }
665             else {
666                 if (!qName.equals(METADATA)) {
667                     throw new IllegalStateException("Unknown name:" + qName + ".");
668                 }
669             }
670         }
671 
672         @Override
673         public void endElement(String uri,
674                                String localName,
675                                String qName) throws SAXException {
676             if (qName.equals(MODULE)) {
677 
678                 final Configuration recentModule =
679                     configStack.pop();
680 
681                 // get severity attribute if it exists
682                 SeverityLevel level = null;
683                 if (containsAttribute(recentModule, SEVERITY)) {
684                     try {
685                         final String severity = recentModule.getAttribute(SEVERITY);
686                         level = SeverityLevel.getInstance(severity);
687                     }
688                     catch (final CheckstyleException ex) {
689                         // -@cs[IllegalInstantiation] SAXException is in the overridden
690                         // method signature
691                         throw new SAXException(
692                                 "Problem during accessing '" + SEVERITY + "' attribute for "
693                                         + recentModule.getName(), ex);
694                     }
695                 }
696 
697                 // omit this module if these should be omitted and the module
698                 // has the severity 'ignore'
699                 final boolean omitModule = omitIgnoredModules
700                     && level == SeverityLevel.IGNORE;
701 
702                 if (omitModule && !configStack.isEmpty()) {
703                     final DefaultConfiguration parentModule =
704                         configStack.peek();
705                     parentModule.removeChild(recentModule);
706                 }
707             }
708         }
709 
710         /**
711          * Util method to recheck attribute in module.
712          * @param module module to check
713          * @param attributeName name of attribute in module to find
714          * @return true if attribute is present in module
715          */
716         private boolean containsAttribute(Configuration module, String attributeName) {
717             final String[] names = module.getAttributeNames();
718             final Optional<String> result = Arrays.stream(names)
719                     .filter(name -> name.equals(attributeName)).findFirst();
720             return result.isPresent();
721         }
722     }
723 }