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