001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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.AbstractLoader; 044import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 045import com.puppycrawl.tools.checkstyle.api.Configuration; 046import com.puppycrawl.tools.checkstyle.api.SeverityLevel; 047import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 048 049/** 050 * Loads a configuration from a standard configuration XML file. 051 * 052 * @author Oliver Burn 053 */ 054public final class ConfigurationLoader { 055 056 /** 057 * Enum to specify behaviour regarding ignored modules. 058 */ 059 public enum IgnoredModulesOptions { 060 /** 061 * Omit ignored modules. 062 */ 063 OMIT, 064 065 /** 066 * Execute ignored modules. 067 */ 068 EXECUTE 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 resource for version 1_0 of the configuration dtd. */ 079 private static final String DTD_RESOURCE_NAME_1_0 = 080 "com/puppycrawl/tools/checkstyle/configuration_1_0.dtd"; 081 082 /** The public ID for version 1_1 of the configuration dtd. */ 083 private static final String DTD_PUBLIC_ID_1_1 = 084 "-//Puppy Crawl//DTD Check Configuration 1.1//EN"; 085 086 /** The resource for version 1_1 of the configuration dtd. */ 087 private static final String DTD_RESOURCE_NAME_1_1 = 088 "com/puppycrawl/tools/checkstyle/configuration_1_1.dtd"; 089 090 /** The public ID for version 1_2 of the configuration dtd. */ 091 private static final String DTD_PUBLIC_ID_1_2 = 092 "-//Puppy Crawl//DTD Check Configuration 1.2//EN"; 093 094 /** The resource for version 1_2 of the configuration dtd. */ 095 private static final String DTD_RESOURCE_NAME_1_2 = 096 "com/puppycrawl/tools/checkstyle/configuration_1_2.dtd"; 097 098 /** The public ID for version 1_3 of the configuration dtd. */ 099 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 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 this(overrideProps, omitIgnoredModules, ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE); 142 } 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 throws ParserConfigurationException, SAXException { 157 saxHandler = new InternalLoader(); 158 overridePropsResolver = overrideProps; 159 this.omitIgnoredModules = omitIgnoredModules; 160 this.threadModeSettings = threadModeSettings; 161 } 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 final Map<String, String> map = new HashMap<>(); 169 map.put(DTD_PUBLIC_ID_1_0, DTD_RESOURCE_NAME_1_0); 170 map.put(DTD_PUBLIC_ID_1_1, DTD_RESOURCE_NAME_1_1); 171 map.put(DTD_PUBLIC_ID_1_2, DTD_RESOURCE_NAME_1_2); 172 map.put(DTD_PUBLIC_ID_1_3, DTD_RESOURCE_NAME_1_3); 173 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 saxHandler.parseInputSource(source); 189 } 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 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 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 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 final URI uri = CommonUtils.getUriByFilename(config); 258 final InputSource source = new InputSource(uri.toString()); 259 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 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 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 final ConfigurationLoader loader = 333 new ConfigurationLoader(overridePropsResolver, 334 omitIgnoredModules, threadModeSettings); 335 loader.parseInputSource(configSource); 336 return loader.configuration; 337 } 338 catch (final SAXParseException ex) { 339 final String message = String.format(Locale.ROOT, SAX_PARSE_EXCEPTION_FORMAT, 340 UNABLE_TO_PARSE_EXCEPTION_PREFIX, 341 ex.getMessage(), ex.getLineNumber(), ex.getColumnNumber()); 342 throw new CheckstyleException(message, ex); 343 } 344 catch (final ParserConfigurationException | IOException | SAXException ex) { 345 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 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 final URI uri = CommonUtils.getUriByFilename(config); 385 final InputSource source = new InputSource(uri.toString()); 386 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 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 final boolean omitIgnoreModules = ignoredModulesOptions == IgnoredModulesOptions.OMIT; 431 final ConfigurationLoader loader = 432 new ConfigurationLoader(overridePropsResolver, 433 omitIgnoreModules, threadModeSettings); 434 loader.parseInputSource(configSource); 435 return loader.configuration; 436 } 437 catch (final SAXParseException ex) { 438 final String message = String.format(Locale.ROOT, SAX_PARSE_EXCEPTION_FORMAT, 439 UNABLE_TO_PARSE_EXCEPTION_PREFIX, 440 ex.getMessage(), ex.getLineNumber(), ex.getColumnNumber()); 441 throw new CheckstyleException(message, ex); 442 } 443 catch (final ParserConfigurationException | IOException | SAXException ex) { 444 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 if (value == null) { 474 return null; 475 } 476 477 final List<String> fragments = new ArrayList<>(); 478 final List<String> propertyRefs = new ArrayList<>(); 479 parsePropertyString(value, fragments, propertyRefs); 480 481 final StringBuilder sb = new StringBuilder(256); 482 final Iterator<String> fragmentsIterator = fragments.iterator(); 483 final Iterator<String> propertyRefsIterator = propertyRefs.iterator(); 484 while (fragmentsIterator.hasNext()) { 485 String fragment = fragmentsIterator.next(); 486 if (fragment == null) { 487 final String propertyName = propertyRefsIterator.next(); 488 fragment = props.resolve(propertyName); 489 if (fragment == null) { 490 if (defaultValue != null) { 491 sb.replace(0, sb.length(), defaultValue); 492 break; 493 } 494 throw new CheckstyleException( 495 "Property ${" + propertyName + "} has not been set"); 496 } 497 } 498 sb.append(fragment); 499 } 500 501 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 int prev = 0; 529 //search for the next instance of $ from the 'prev' position 530 int pos = value.indexOf(DOLLAR_SIGN, prev); 531 while (pos >= 0) { 532 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 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 throws SAXException, ParserConfigurationException { 610 super(createIdToResourceNameMap()); 611 } 612 613 @Override 614 public void startElement(String uri, 615 String localName, 616 String qName, 617 Attributes attributes) 618 throws SAXException { 619 if (qName.equals(MODULE)) { 620 //create configuration 621 final String originalName = attributes.getValue(NAME); 622 final String name = threadModeSettings.resolveName(originalName); 623 final DefaultConfiguration conf = 624 new DefaultConfiguration(name, threadModeSettings); 625 626 if (configuration == null) { 627 configuration = conf; 628 } 629 630 //add configuration to it's parent 631 if (!configStack.isEmpty()) { 632 final DefaultConfiguration top = 633 configStack.peek(); 634 top.addChild(conf); 635 } 636 637 configStack.push(conf); 638 } 639 else if (qName.equals(PROPERTY)) { 640 //extract value and name 641 final String value; 642 try { 643 value = replaceProperties(attributes.getValue(VALUE), 644 overridePropsResolver, attributes.getValue(DEFAULT)); 645 } 646 catch (final CheckstyleException ex) { 647 // -@cs[IllegalInstantiation] SAXException is in the overridden method signature 648 throw new SAXException(ex); 649 } 650 final String name = attributes.getValue(NAME); 651 652 //add to attributes of configuration 653 final DefaultConfiguration top = 654 configStack.peek(); 655 top.addAttribute(name, value); 656 } 657 else if (qName.equals(MESSAGE)) { 658 //extract key and value 659 final String key = attributes.getValue(KEY); 660 final String value = attributes.getValue(VALUE); 661 662 //add to messages of configuration 663 final DefaultConfiguration top = configStack.peek(); 664 top.addMessage(key, value); 665 } 666 else { 667 if (!qName.equals(METADATA)) { 668 throw new IllegalStateException("Unknown name:" + qName + "."); 669 } 670 } 671 } 672 673 @Override 674 public void endElement(String uri, 675 String localName, 676 String qName) throws SAXException { 677 if (qName.equals(MODULE)) { 678 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}