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