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