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