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