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}