1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard; 22 23import proguard.classfile.*; 24import proguard.classfile.util.ClassUtil; 25import proguard.util.ListUtil; 26 27import java.io.*; 28import java.net.URL; 29import java.util.*; 30 31 32/** 33 * This class parses ProGuard configurations. Configurations can be read from an 34 * array of arguments or from a configuration file or URL. External references 35 * in file names ('<...>') can be resolved against a given set of properties. 36 * 37 * @author Eric Lafortune 38 */ 39public class ConfigurationParser 40{ 41 private final WordReader reader; 42 private final Properties properties; 43 44 private String nextWord; 45 private String lastComments; 46 47 48 /** 49 * Creates a new ConfigurationParser for the given String arguments and 50 * the given Properties. 51 */ 52 public ConfigurationParser(String[] args, 53 Properties properties) throws IOException 54 { 55 this(args, null, properties); 56 } 57 58 59 /** 60 * Creates a new ConfigurationParser for the given String arguments, 61 * with the given base directory and the given Properties. 62 */ 63 public ConfigurationParser(String[] args, 64 File baseDir, 65 Properties properties) throws IOException 66 { 67 this(new ArgumentWordReader(args, baseDir), properties); 68 } 69 70 71 /** 72 * Creates a new ConfigurationParser for the given lines, 73 * with the given base directory and the given Properties. 74 */ 75 public ConfigurationParser(String lines, 76 String description, 77 File baseDir, 78 Properties properties) throws IOException 79 { 80 this(new LineWordReader(new LineNumberReader(new StringReader(lines)), 81 description, 82 baseDir), 83 properties); 84 } 85 86 87 /** 88 * Creates a new ConfigurationParser for the given file, with the system 89 * Properties. 90 * @deprecated Temporary code for backward compatibility in Obclipse. 91 */ 92 public ConfigurationParser(File file) throws IOException 93 { 94 this(file, System.getProperties()); 95 } 96 97 98 /** 99 * Creates a new ConfigurationParser for the given file and the given 100 * Properties. 101 */ 102 public ConfigurationParser(File file, 103 Properties properties) throws IOException 104 { 105 this(new FileWordReader(file), properties); 106 } 107 108 109 /** 110 * Creates a new ConfigurationParser for the given URL and the given 111 * Properties. 112 */ 113 public ConfigurationParser(URL url, 114 Properties properties) throws IOException 115 { 116 this(new FileWordReader(url), properties); 117 } 118 119 120 /** 121 * Creates a new ConfigurationParser for the given word reader and the 122 * given Properties. 123 */ 124 public ConfigurationParser(WordReader reader, 125 Properties properties) throws IOException 126 { 127 this.reader = reader; 128 this.properties = properties; 129 130 readNextWord(); 131 } 132 133 134 /** 135 * Parses and returns the configuration. 136 * @param configuration the configuration that is updated as a side-effect. 137 * @throws ParseException if the any of the configuration settings contains 138 * a syntax error. 139 * @throws IOException if an IO error occurs while reading a configuration. 140 */ 141 public void parse(Configuration configuration) 142 throws ParseException, IOException 143 { 144 while (nextWord != null) 145 { 146 lastComments = reader.lastComments(); 147 148 // First include directives. 149 if (ConfigurationConstants.AT_DIRECTIVE .startsWith(nextWord) || 150 ConfigurationConstants.INCLUDE_DIRECTIVE .startsWith(nextWord)) configuration.lastModified = parseIncludeArgument(configuration.lastModified); 151 else if (ConfigurationConstants.BASE_DIRECTORY_DIRECTIVE .startsWith(nextWord)) parseBaseDirectoryArgument(); 152 153 // Then configuration options with or without arguments. 154 else if (ConfigurationConstants.INJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, false); 155 else if (ConfigurationConstants.OUTJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, true); 156 else if (ConfigurationConstants.LIBRARYJARS_OPTION .startsWith(nextWord)) configuration.libraryJars = parseClassPathArgument(configuration.libraryJars, false); 157 // Android-added: Parse -systemjars option. 158 else if (ConfigurationConstants.SYSTEMJARS_OPTION .startsWith(nextWord)) configuration.systemJars = parseClassPathArgument(configuration.systemJars, false); 159 else if (ConfigurationConstants.RESOURCEJARS_OPTION .startsWith(nextWord)) throw new ParseException("The '-resourcejars' option is no longer supported. Please use the '-injars' option for all input"); 160 else if (ConfigurationConstants.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses = parseNoArgument(true); 161 else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses = parseNoArgument(false); 162 else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION.startsWith(nextWord)) configuration.skipNonPublicLibraryClassMembers = parseNoArgument(false); 163 else if (ConfigurationConstants.TARGET_OPTION .startsWith(nextWord)) configuration.targetClassVersion = parseClassVersion(); 164 else if (ConfigurationConstants.FORCE_PROCESSING_OPTION .startsWith(nextWord)) configuration.lastModified = parseNoArgument(Long.MAX_VALUE); 165 166 else if (ConfigurationConstants.KEEP_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, false); 167 else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, false); 168 else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, false); 169 else if (ConfigurationConstants.KEEP_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, true); 170 else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, true); 171 else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, true); 172 else if (ConfigurationConstants.PRINT_SEEDS_OPTION .startsWith(nextWord)) configuration.printSeeds = parseOptionalFile(); 173 174 // After '-keep'. 175 else if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION .startsWith(nextWord)) configuration.keepDirectories = parseCommaSeparatedList("directory name", true, true, false, true, false, true, false, false, configuration.keepDirectories); 176 177 else if (ConfigurationConstants.DONT_SHRINK_OPTION .startsWith(nextWord)) configuration.shrink = parseNoArgument(false); 178 else if (ConfigurationConstants.PRINT_USAGE_OPTION .startsWith(nextWord)) configuration.printUsage = parseOptionalFile(); 179 else if (ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION .startsWith(nextWord)) configuration.whyAreYouKeeping = parseClassSpecificationArguments(configuration.whyAreYouKeeping); 180 181 else if (ConfigurationConstants.DONT_OPTIMIZE_OPTION .startsWith(nextWord)) configuration.optimize = parseNoArgument(false); 182 else if (ConfigurationConstants.OPTIMIZATION_PASSES .startsWith(nextWord)) configuration.optimizationPasses = parseIntegerArgument(); 183 else if (ConfigurationConstants.OPTIMIZATIONS .startsWith(nextWord)) configuration.optimizations = parseCommaSeparatedList("optimization name", true, false, false, false, false, false, false, false, configuration.optimizations); 184 else if (ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION .startsWith(nextWord)) configuration.assumeNoSideEffects = parseClassSpecificationArguments(configuration.assumeNoSideEffects); 185 else if (ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION .startsWith(nextWord)) configuration.allowAccessModification = parseNoArgument(true); 186 else if (ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION .startsWith(nextWord)) configuration.mergeInterfacesAggressively = parseNoArgument(true); 187 188 else if (ConfigurationConstants.DONT_OBFUSCATE_OPTION .startsWith(nextWord)) configuration.obfuscate = parseNoArgument(false); 189 else if (ConfigurationConstants.PRINT_MAPPING_OPTION .startsWith(nextWord)) configuration.printMapping = parseOptionalFile(); 190 else if (ConfigurationConstants.APPLY_MAPPING_OPTION .startsWith(nextWord)) configuration.applyMapping = parseFile(); 191 else if (ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.obfuscationDictionary = parseFile(); 192 else if (ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.classObfuscationDictionary = parseFile(); 193 else if (ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.packageObfuscationDictionary = parseFile(); 194 else if (ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION .startsWith(nextWord)) configuration.overloadAggressively = parseNoArgument(true); 195 else if (ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.useUniqueClassMemberNames = parseNoArgument(true); 196 else if (ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION .startsWith(nextWord)) configuration.useMixedCaseClassNames = parseNoArgument(false); 197 else if (ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION .startsWith(nextWord)) configuration.keepPackageNames = parseCommaSeparatedList("package name", true, true, false, false, true, false, true, false, configuration.keepPackageNames); 198 else if (ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION .startsWith(nextWord)) configuration.flattenPackageHierarchy = ClassUtil.internalClassName(parseOptionalArgument()); 199 else if (ConfigurationConstants.REPACKAGE_CLASSES_OPTION .startsWith(nextWord)) configuration.repackageClasses = ClassUtil.internalClassName(parseOptionalArgument()); 200 else if (ConfigurationConstants.DEFAULT_PACKAGE_OPTION .startsWith(nextWord)) configuration.repackageClasses = ClassUtil.internalClassName(parseOptionalArgument()); 201 else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION .startsWith(nextWord)) configuration.keepAttributes = parseCommaSeparatedList("attribute name", true, true, false, false, true, false, false, false, configuration.keepAttributes); 202 else if (ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION .startsWith(nextWord)) configuration.keepParameterNames = parseNoArgument(true); 203 else if (ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION .startsWith(nextWord)) configuration.newSourceFileAttribute = parseOptionalArgument(); 204 else if (ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION .startsWith(nextWord)) configuration.adaptClassStrings = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.adaptClassStrings); 205 else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION .startsWith(nextWord)) configuration.adaptResourceFileNames = parseCommaSeparatedList("resource file name", true, true, false, true, false, false, false, false, configuration.adaptResourceFileNames); 206 else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION .startsWith(nextWord)) configuration.adaptResourceFileContents = parseCommaSeparatedList("resource file name", true, true, false, true, false, false, false, false, configuration.adaptResourceFileContents); 207 208 else if (ConfigurationConstants.DONT_PREVERIFY_OPTION .startsWith(nextWord)) configuration.preverify = parseNoArgument(false); 209 else if (ConfigurationConstants.MICRO_EDITION_OPTION .startsWith(nextWord)) configuration.microEdition = parseNoArgument(true); 210 211 else if (ConfigurationConstants.VERBOSE_OPTION .startsWith(nextWord)) configuration.verbose = parseNoArgument(true); 212 else if (ConfigurationConstants.DONT_NOTE_OPTION .startsWith(nextWord)) configuration.note = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.note); 213 else if (ConfigurationConstants.DONT_WARN_OPTION .startsWith(nextWord)) configuration.warn = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.warn); 214 else if (ConfigurationConstants.IGNORE_WARNINGS_OPTION .startsWith(nextWord)) configuration.ignoreWarnings = parseNoArgument(true); 215 else if (ConfigurationConstants.PRINT_CONFIGURATION_OPTION .startsWith(nextWord)) configuration.printConfiguration = parseOptionalFile(); 216 else if (ConfigurationConstants.DUMP_OPTION .startsWith(nextWord)) configuration.dump = parseOptionalFile(); 217 else 218 { 219 throw new ParseException("Unknown option " + reader.locationDescription()); 220 } 221 } 222 } 223 224 225 226 /** 227 * Closes the configuration. 228 * @throws IOException if an IO error occurs while closing the configuration. 229 */ 230 public void close() throws IOException 231 { 232 if (reader != null) 233 { 234 reader.close(); 235 } 236 } 237 238 239 private long parseIncludeArgument(long lastModified) throws ParseException, IOException 240 { 241 // Read the configuration file name. 242 readNextWord("configuration file name", true, false); 243 244 File file = file(nextWord); 245 reader.includeWordReader(new FileWordReader(file)); 246 247 readNextWord(); 248 249 return Math.max(lastModified, file.lastModified()); 250 } 251 252 253 private void parseBaseDirectoryArgument() throws ParseException, IOException 254 { 255 // Read the base directory name. 256 readNextWord("base directory name", true, false); 257 258 reader.setBaseDir(file(nextWord)); 259 260 readNextWord(); 261 } 262 263 264 private ClassPath parseClassPathArgument(ClassPath classPath, 265 boolean isOutput) 266 throws ParseException, IOException 267 { 268 // Create a new List if necessary. 269 if (classPath == null) 270 { 271 classPath = new ClassPath(); 272 } 273 274 while (true) 275 { 276 // Read the next jar name. 277 readNextWord("jar or directory name", true, false); 278 279 // Create a new class path entry. 280 ClassPathEntry entry = new ClassPathEntry(file(nextWord), isOutput); 281 282 // Read the opening parenthesis or the separator, if any. 283 readNextWord(); 284 285 // Read the optional filters. 286 if (!configurationEnd() && 287 ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) 288 { 289 // Read all filters in an array. 290 List[] filters = new List[7]; 291 292 int counter = 0; 293 do 294 { 295 // Read the filter. 296 filters[counter++] = 297 parseCommaSeparatedList("filter", true, true, true, true, false, true, false, false, null); 298 } 299 while (counter < filters.length && 300 ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)); 301 302 // Make sure there is a closing parenthesis. 303 if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) 304 { 305 throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 306 "' or '" + ConfigurationConstants.SEPARATOR_KEYWORD + 307 "', or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + 308 "' before " + reader.locationDescription()); 309 } 310 311 // Set all filters from the array on the entry. 312 entry.setFilter(filters[--counter]); 313 if (counter > 0) 314 { 315 entry.setJarFilter(filters[--counter]); 316 if (counter > 0) 317 { 318 entry.setWarFilter(filters[--counter]); 319 if (counter > 0) 320 { 321 entry.setEarFilter(filters[--counter]); 322 if (counter > 0) 323 { 324 entry.setZipFilter(filters[--counter]); 325 if (counter > 0) 326 { 327 // For backward compatibility, the apk 328 // filter comes second in the list. 329 entry.setApkFilter(filters[--counter]); 330 if (counter > 0) 331 { 332 // For backward compatibility, the aar 333 // filter comes first in the list. 334 entry.setAarFilter(filters[--counter]); 335 } 336 } 337 } 338 } 339 } 340 } 341 342 // Read the separator, if any. 343 readNextWord(); 344 } 345 346 // Add the entry to the list. 347 classPath.add(entry); 348 349 if (configurationEnd()) 350 { 351 return classPath; 352 } 353 354 if (!nextWord.equals(ConfigurationConstants.JAR_SEPARATOR_KEYWORD)) 355 { 356 throw new ParseException("Expecting class path separator '" + ConfigurationConstants.JAR_SEPARATOR_KEYWORD + 357 "' before " + reader.locationDescription()); 358 } 359 } 360 } 361 362 363 private int parseClassVersion() 364 throws ParseException, IOException 365 { 366 // Read the obligatory target. 367 readNextWord("java version"); 368 369 int classVersion = ClassUtil.internalClassVersion(nextWord); 370 if (classVersion == 0) 371 { 372 throw new ParseException("Unsupported java version " + reader.locationDescription()); 373 } 374 375 readNextWord(); 376 377 return classVersion; 378 } 379 380 381 private int parseIntegerArgument() 382 throws ParseException, IOException 383 { 384 try 385 { 386 // Read the obligatory integer. 387 readNextWord("integer"); 388 389 int integer = Integer.parseInt(nextWord); 390 391 readNextWord(); 392 393 return integer; 394 } 395 catch (NumberFormatException e) 396 { 397 throw new ParseException("Expecting integer argument instead of '" + nextWord + 398 "' before " + reader.locationDescription()); 399 } 400 } 401 402 403 private File parseFile() 404 throws ParseException, IOException 405 { 406 // Read the obligatory file name. 407 readNextWord("file name", true, false); 408 409 // Make sure the file is properly resolved. 410 File file = file(nextWord); 411 412 readNextWord(); 413 414 return file; 415 } 416 417 418 private File parseOptionalFile() 419 throws ParseException, IOException 420 { 421 // Read the optional file name. 422 readNextWord(true); 423 424 // Didn't the user specify a file name? 425 if (configurationEnd()) 426 { 427 return Configuration.STD_OUT; 428 } 429 430 // Make sure the file is properly resolved. 431 File file = file(nextWord); 432 433 readNextWord(); 434 435 return file; 436 } 437 438 439 private String parseOptionalArgument() throws IOException 440 { 441 // Read the optional argument. 442 readNextWord(); 443 444 // Didn't the user specify an argument? 445 if (configurationEnd()) 446 { 447 return ""; 448 } 449 450 String argument = nextWord; 451 452 readNextWord(); 453 454 return argument; 455 } 456 457 458 private boolean parseNoArgument(boolean value) throws IOException 459 { 460 readNextWord(); 461 462 return value; 463 } 464 465 466 private long parseNoArgument(long value) throws IOException 467 { 468 readNextWord(); 469 470 return value; 471 } 472 473 474 private List parseKeepClassSpecificationArguments(List keepClassSpecifications, 475 boolean markClasses, 476 boolean markConditionally, 477 boolean allowShrinking) 478 throws ParseException, IOException 479 { 480 // Create a new List if necessary. 481 if (keepClassSpecifications == null) 482 { 483 keepClassSpecifications = new ArrayList(); 484 } 485 486 boolean markDescriptorClasses = false; 487 //boolean allowShrinking = false; 488 boolean allowOptimization = false; 489 boolean allowObfuscation = false; 490 491 // Read the keep modifiers. 492 while (true) 493 { 494 readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + 495 "', '" + JavaConstants.ACC_INTERFACE + 496 "', or '" + JavaConstants.ACC_ENUM + "'", 497 false, true); 498 499 if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord)) 500 { 501 // Not a comma. Stop parsing the keep modifiers. 502 break; 503 } 504 505 readNextWord("keyword '" + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + 506 "', '" + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + 507 "', or '" + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + "'"); 508 509 if (ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION.startsWith(nextWord)) 510 { 511 markDescriptorClasses = true; 512 } 513 else if (ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION .startsWith(nextWord)) 514 { 515 allowShrinking = true; 516 } 517 else if (ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION .startsWith(nextWord)) 518 { 519 allowOptimization = true; 520 } 521 else if (ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION .startsWith(nextWord)) 522 { 523 allowObfuscation = true; 524 } 525 else 526 { 527 throw new ParseException("Expecting keyword '" + ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION + 528 "', '" + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + 529 "', '" + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + 530 "', or '" + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + 531 "' before " + reader.locationDescription()); 532 } 533 } 534 535 // Read the class configuration. 536 ClassSpecification classSpecification = 537 parseClassSpecificationArguments(); 538 539 // Create and add the keep configuration. 540 keepClassSpecifications.add(new KeepClassSpecification(markClasses, 541 markConditionally, 542 markDescriptorClasses, 543 allowShrinking, 544 allowOptimization, 545 allowObfuscation, 546 classSpecification)); 547 return keepClassSpecifications; 548 } 549 550 551 private List parseClassSpecificationArguments(List classSpecifications) 552 throws ParseException, IOException 553 { 554 // Create a new List if necessary. 555 if (classSpecifications == null) 556 { 557 classSpecifications = new ArrayList(); 558 } 559 560 // Read and add the class configuration. 561 readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + 562 "', '" + JavaConstants.ACC_INTERFACE + 563 "', or '" + JavaConstants.ACC_ENUM + "'", 564 false, true); 565 566 classSpecifications.add(parseClassSpecificationArguments()); 567 568 return classSpecifications; 569 } 570 571 572 /** 573 * Parses and returns a class specification. 574 * @throws ParseException if the class specification contains a syntax error. 575 * @throws IOException if an IO error occurs while reading the class 576 * specification. 577 */ 578 public ClassSpecification parseClassSpecificationArguments() 579 throws ParseException, IOException 580 { 581 // Clear the annotation type. 582 String annotationType = null; 583 584 // Clear the class access modifiers. 585 int requiredSetClassAccessFlags = 0; 586 int requiredUnsetClassAccessFlags = 0; 587 588 // Parse the class annotations and access modifiers until the class keyword. 589 while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord)) 590 { 591 // Strip the negating sign, if any. 592 boolean negated = 593 nextWord.startsWith(ConfigurationConstants.NEGATOR_KEYWORD); 594 595 String strippedWord = negated ? 596 nextWord.substring(1) : 597 nextWord; 598 599 // Parse the class access modifiers. 600 int accessFlag = 601 strippedWord.equals(JavaConstants.ACC_PUBLIC) ? ClassConstants.ACC_PUBLIC : 602 strippedWord.equals(JavaConstants.ACC_FINAL) ? ClassConstants.ACC_FINAL : 603 strippedWord.equals(JavaConstants.ACC_INTERFACE) ? ClassConstants.ACC_INTERFACE : 604 strippedWord.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : 605 strippedWord.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : 606 strippedWord.equals(JavaConstants.ACC_ANNOTATION) ? ClassConstants.ACC_ANNOTATTION : 607 strippedWord.equals(JavaConstants.ACC_ENUM) ? ClassConstants.ACC_ENUM : 608 unknownAccessFlag(); 609 610 // Is it an annotation modifier? 611 if (accessFlag == ClassConstants.ACC_ANNOTATTION) 612 { 613 // Already read the next word. 614 readNextWord("annotation type or keyword '" + JavaConstants.ACC_INTERFACE + "'", 615 false, false); 616 617 // Is the next word actually an annotation type? 618 if (!nextWord.equals(JavaConstants.ACC_INTERFACE) && 619 !nextWord.equals(JavaConstants.ACC_ENUM) && 620 !nextWord.equals(ConfigurationConstants.CLASS_KEYWORD)) 621 { 622 // Parse the annotation type. 623 annotationType = 624 ListUtil.commaSeparatedString( 625 parseCommaSeparatedList("annotation type", 626 false, false, false, false, true, false, false, true, null), false); 627 628 // Continue parsing the access modifier that we just read 629 // in the next cycle. 630 continue; 631 } 632 633 // Otherwise just handle the annotation modifier. 634 } 635 636 if (!negated) 637 { 638 requiredSetClassAccessFlags |= accessFlag; 639 } 640 else 641 { 642 requiredUnsetClassAccessFlags |= accessFlag; 643 } 644 645 if ((requiredSetClassAccessFlags & 646 requiredUnsetClassAccessFlags) != 0) 647 { 648 throw new ParseException("Conflicting class access modifiers for '" + strippedWord + 649 "' before " + reader.locationDescription()); 650 } 651 652 if (strippedWord.equals(JavaConstants.ACC_INTERFACE) || 653 strippedWord.equals(JavaConstants.ACC_ENUM) || 654 strippedWord.equals(ConfigurationConstants.CLASS_KEYWORD)) 655 { 656 // The interface or enum keyword. Stop parsing the class flags. 657 break; 658 } 659 660 // Should we read the next word? 661 if (accessFlag != ClassConstants.ACC_ANNOTATTION) 662 { 663 readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + 664 "', '" + JavaConstants.ACC_INTERFACE + 665 "', or '" + JavaConstants.ACC_ENUM + "'", 666 false, true); 667 } 668 } 669 670 // Parse the class name part. 671 String externalClassName = 672 ListUtil.commaSeparatedString( 673 parseCommaSeparatedList("class name or interface name", 674 true, false, false, false, true, false, false, false, null), false); 675 676 // For backward compatibility, allow a single "*" wildcard to match any 677 // class. 678 String className = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalClassName) ? 679 null : 680 ClassUtil.internalClassName(externalClassName); 681 682 // Clear the annotation type and the class name of the extends part. 683 String extendsAnnotationType = null; 684 String extendsClassName = null; 685 686 if (!configurationEnd()) 687 { 688 // Parse 'implements ...' or 'extends ...' part, if any. 689 if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) || 690 ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord)) 691 { 692 readNextWord("class name or interface name", false, true); 693 694 // Parse the annotation type, if any. 695 if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) 696 { 697 extendsAnnotationType = 698 ListUtil.commaSeparatedString( 699 parseCommaSeparatedList("annotation type", 700 true, false, false, false, true, false, false, true, null), false); 701 } 702 703 String externalExtendsClassName = 704 ListUtil.commaSeparatedString( 705 parseCommaSeparatedList("class name or interface name", 706 false, false, false, false, true, false, false, false, null), false); 707 708 extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalExtendsClassName) ? 709 null : 710 ClassUtil.internalClassName(externalExtendsClassName); 711 } 712 } 713 714 // Create the basic class specification. 715 ClassSpecification classSpecification = 716 new ClassSpecification(lastComments, 717 requiredSetClassAccessFlags, 718 requiredUnsetClassAccessFlags, 719 annotationType, 720 className, 721 extendsAnnotationType, 722 extendsClassName); 723 724 725 // Now add any class members to this class specification. 726 if (!configurationEnd()) 727 { 728 // Check the class member opening part. 729 if (!ConfigurationConstants.OPEN_KEYWORD.equals(nextWord)) 730 { 731 throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_KEYWORD + 732 "' at " + reader.locationDescription()); 733 } 734 735 // Parse all class members. 736 while (true) 737 { 738 readNextWord("class member description" + 739 " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'", 740 false, true); 741 742 if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD)) 743 { 744 // The closing brace. Stop parsing the class members. 745 readNextWord(); 746 747 break; 748 } 749 750 parseMemberSpecificationArguments(externalClassName, 751 classSpecification); 752 } 753 } 754 755 return classSpecification; 756 } 757 758 759 private void parseMemberSpecificationArguments(String externalClassName, 760 ClassSpecification classSpecification) 761 throws ParseException, IOException 762 { 763 // Clear the annotation name. 764 String annotationType = null; 765 766 // Parse the class member access modifiers, if any. 767 int requiredSetMemberAccessFlags = 0; 768 int requiredUnsetMemberAccessFlags = 0; 769 770 while (!configurationEnd(true)) 771 { 772 // Parse the annotation type, if any. 773 if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) 774 { 775 annotationType = 776 ListUtil.commaSeparatedString( 777 parseCommaSeparatedList("annotation type", 778 true, false, false, false, true, false, false, true, null), false); 779 continue; 780 } 781 782 String strippedWord = nextWord.startsWith("!") ? 783 nextWord.substring(1) : 784 nextWord; 785 786 // Parse the class member access modifiers. 787 int accessFlag = 788 strippedWord.equals(JavaConstants.ACC_PUBLIC) ? ClassConstants.ACC_PUBLIC : 789 strippedWord.equals(JavaConstants.ACC_PRIVATE) ? ClassConstants.ACC_PRIVATE : 790 strippedWord.equals(JavaConstants.ACC_PROTECTED) ? ClassConstants.ACC_PROTECTED : 791 strippedWord.equals(JavaConstants.ACC_STATIC) ? ClassConstants.ACC_STATIC : 792 strippedWord.equals(JavaConstants.ACC_FINAL) ? ClassConstants.ACC_FINAL : 793 strippedWord.equals(JavaConstants.ACC_SYNCHRONIZED) ? ClassConstants.ACC_SYNCHRONIZED : 794 strippedWord.equals(JavaConstants.ACC_VOLATILE) ? ClassConstants.ACC_VOLATILE : 795 strippedWord.equals(JavaConstants.ACC_TRANSIENT) ? ClassConstants.ACC_TRANSIENT : 796 strippedWord.equals(JavaConstants.ACC_BRIDGE) ? ClassConstants.ACC_BRIDGE : 797 strippedWord.equals(JavaConstants.ACC_VARARGS) ? ClassConstants.ACC_VARARGS : 798 strippedWord.equals(JavaConstants.ACC_NATIVE) ? ClassConstants.ACC_NATIVE : 799 strippedWord.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : 800 strippedWord.equals(JavaConstants.ACC_STRICT) ? ClassConstants.ACC_STRICT : 801 strippedWord.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : 802 0; 803 if (accessFlag == 0) 804 { 805 // Not a class member access modifier. Stop parsing them. 806 break; 807 } 808 809 if (strippedWord.equals(nextWord)) 810 { 811 requiredSetMemberAccessFlags |= accessFlag; 812 } 813 else 814 { 815 requiredUnsetMemberAccessFlags |= accessFlag; 816 } 817 818 // Make sure the user doesn't try to set and unset the same 819 // access flags simultaneously. 820 if ((requiredSetMemberAccessFlags & 821 requiredUnsetMemberAccessFlags) != 0) 822 { 823 throw new ParseException("Conflicting class member access modifiers for " + 824 reader.locationDescription()); 825 } 826 827 readNextWord("class member description"); 828 } 829 830 // Parse the class member type and name part. 831 832 // Did we get a special wildcard? 833 if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord) || 834 ConfigurationConstants.ANY_FIELD_KEYWORD .equals(nextWord) || 835 ConfigurationConstants.ANY_METHOD_KEYWORD .equals(nextWord)) 836 { 837 // Act according to the type of wildcard.. 838 if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord)) 839 { 840 checkFieldAccessFlags(requiredSetMemberAccessFlags, 841 requiredUnsetMemberAccessFlags); 842 checkMethodAccessFlags(requiredSetMemberAccessFlags, 843 requiredUnsetMemberAccessFlags); 844 845 classSpecification.addField( 846 new MemberSpecification(requiredSetMemberAccessFlags, 847 requiredUnsetMemberAccessFlags, 848 annotationType, 849 null, 850 null)); 851 classSpecification.addMethod( 852 new MemberSpecification(requiredSetMemberAccessFlags, 853 requiredUnsetMemberAccessFlags, 854 annotationType, 855 null, 856 null)); 857 } 858 else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord)) 859 { 860 checkFieldAccessFlags(requiredSetMemberAccessFlags, 861 requiredUnsetMemberAccessFlags); 862 863 classSpecification.addField( 864 new MemberSpecification(requiredSetMemberAccessFlags, 865 requiredUnsetMemberAccessFlags, 866 annotationType, 867 null, 868 null)); 869 } 870 else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord)) 871 { 872 checkMethodAccessFlags(requiredSetMemberAccessFlags, 873 requiredUnsetMemberAccessFlags); 874 875 classSpecification.addMethod( 876 new MemberSpecification(requiredSetMemberAccessFlags, 877 requiredUnsetMemberAccessFlags, 878 annotationType, 879 null, 880 null)); 881 } 882 883 // We still have to read the closing separator. 884 readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); 885 886 if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) 887 { 888 throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + 889 "' before " + reader.locationDescription()); 890 } 891 } 892 else 893 { 894 // Make sure we have a proper type. 895 checkJavaIdentifier("java type"); 896 String type = nextWord; 897 898 readNextWord("class member name"); 899 String name = nextWord; 900 901 // Did we get just one word before the opening parenthesis? 902 if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name)) 903 { 904 // This must be a constructor then. 905 // Make sure the type is a proper constructor name. 906 if (!(type.equals(ClassConstants.METHOD_NAME_INIT) || 907 type.equals(externalClassName) || 908 type.equals(ClassUtil.externalShortClassName(externalClassName)))) 909 { 910 throw new ParseException("Expecting type and name " + 911 "instead of just '" + type + 912 "' before " + reader.locationDescription()); 913 } 914 915 // Assign the fixed constructor type and name. 916 type = JavaConstants.TYPE_VOID; 917 name = ClassConstants.METHOD_NAME_INIT; 918 } 919 else 920 { 921 // It's not a constructor. 922 // Make sure we have a proper name. 923 checkJavaIdentifier("class member name"); 924 925 // Read the opening parenthesis or the separating 926 // semi-colon. 927 readNextWord("opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + 928 "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); 929 } 930 931 // Are we looking at a field, a method, or something else? 932 if (ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) 933 { 934 // It's a field. 935 checkFieldAccessFlags(requiredSetMemberAccessFlags, 936 requiredUnsetMemberAccessFlags); 937 938 // We already have a field descriptor. 939 String descriptor = ClassUtil.internalType(type); 940 941 // Add the field. 942 classSpecification.addField( 943 new MemberSpecification(requiredSetMemberAccessFlags, 944 requiredUnsetMemberAccessFlags, 945 annotationType, 946 name, 947 descriptor)); 948 } 949 else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) 950 { 951 // It's a method. 952 checkMethodAccessFlags(requiredSetMemberAccessFlags, 953 requiredUnsetMemberAccessFlags); 954 955 // Parse the method arguments. 956 String descriptor = 957 ClassUtil.internalMethodDescriptor(type, 958 parseCommaSeparatedList("argument", true, true, true, false, true, false, false, false, null)); 959 960 if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) 961 { 962 throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 963 "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + 964 "' before " + reader.locationDescription()); 965 } 966 967 // Read the separator after the closing parenthesis. 968 readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); 969 970 if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) 971 { 972 throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + 973 "' before " + reader.locationDescription()); 974 } 975 976 // Add the method. 977 classSpecification.addMethod( 978 new MemberSpecification(requiredSetMemberAccessFlags, 979 requiredUnsetMemberAccessFlags, 980 annotationType, 981 name, 982 descriptor)); 983 } 984 else 985 { 986 // It doesn't look like a field or a method. 987 throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + 988 "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + 989 "' before " + reader.locationDescription()); 990 } 991 } 992 } 993 994 995 /** 996 * Reads a comma-separated list of java identifiers or of file names. 997 * Examples of invocation arguments: 998 * ("directory name", true, true, false, true, false, true, false, false, ...) 999 * ("optimization", true, false, false, false, false, false, false, false, ...) 1000 * ("package name", true, true, false, false, true, false, true, false, ...) 1001 * ("attribute name", true, true, false, false, true, false, false, false, ...) 1002 * ("class name", true, true, false, false, true, false, true, false, ...) 1003 * ("resource file", true, true, false, true, false, false, false, false, ...) 1004 * ("resource file", true, true, false, true, false, false, false, false, ...) 1005 * ("class name", true, true, false, false, true, false, true, false, ...) 1006 * ("class name", true, true, false, false, true, false, true, false, ...) 1007 * ("filter", true, true, true, true, false, true, false, false, ...) 1008 * ("annotation ", false, false, false, false, true, false, false, true, ...) 1009 * ("class name ", true, false, false, false, true, false, false, false, ...) 1010 * ("annotation ", true, false, false, false, true, false, false, true, ...) 1011 * ("class name ", false, false, false, false, true, false, false, false, ...) 1012 * ("annotation ", true, false, false, false, true, false, false, true, ...) 1013 * ("argument", true, true, true, false, true, false, false, false, ...) 1014 */ 1015 private List parseCommaSeparatedList(String expectedDescription, 1016 boolean readFirstWord, 1017 boolean allowEmptyList, 1018 boolean expectClosingParenthesis, 1019 boolean isFileName, 1020 boolean checkJavaIdentifiers, 1021 boolean replaceSystemProperties, 1022 boolean replaceExternalClassNames, 1023 boolean replaceExternalTypes, 1024 List list) 1025 throws ParseException, IOException 1026 { 1027 if (list == null) 1028 { 1029 list = new ArrayList(); 1030 } 1031 1032 if (readFirstWord) 1033 { 1034 if (!allowEmptyList) 1035 { 1036 // Read the first list entry. 1037 readNextWord(expectedDescription, isFileName, false); 1038 } 1039 else if (expectClosingParenthesis) 1040 { 1041 // Read the first list entry. 1042 readNextWord(expectedDescription, isFileName, false); 1043 1044 // Return if the entry is actually empty (an empty file name or 1045 // a closing parenthesis). 1046 if (nextWord.length() == 0) 1047 { 1048 // Read the closing parenthesis 1049 readNextWord("closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + 1050 "'"); 1051 1052 return list; 1053 } 1054 else if (nextWord.equals(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD)) 1055 { 1056 return list; 1057 } 1058 } 1059 else 1060 { 1061 // Read the first list entry, if there is any. 1062 readNextWord(isFileName); 1063 1064 // Check if the list is empty. 1065 if (configurationEnd()) 1066 { 1067 return list; 1068 } 1069 } 1070 } 1071 1072 while (true) 1073 { 1074 if (checkJavaIdentifiers) 1075 { 1076 checkJavaIdentifier("java type"); 1077 } 1078 1079 if (replaceSystemProperties) 1080 { 1081 nextWord = replaceSystemProperties(nextWord); 1082 } 1083 1084 if (replaceExternalClassNames) 1085 { 1086 nextWord = ClassUtil.internalClassName(nextWord); 1087 } 1088 1089 if (replaceExternalTypes) 1090 { 1091 nextWord = ClassUtil.internalType(nextWord); 1092 } 1093 1094 list.add(nextWord); 1095 1096 if (expectClosingParenthesis) 1097 { 1098 // Read a comma (or a closing parenthesis, or a different word). 1099 readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 1100 "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + 1101 "'"); 1102 } 1103 else 1104 { 1105 // Read a comma (or a different word). 1106 readNextWord(); 1107 } 1108 1109 if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord)) 1110 { 1111 return list; 1112 } 1113 1114 // Read the next list entry. 1115 readNextWord(expectedDescription, isFileName, false); 1116 } 1117 } 1118 1119 1120 /** 1121 * Throws a ParseException for an unexpected keyword. 1122 */ 1123 private int unknownAccessFlag() throws ParseException 1124 { 1125 throw new ParseException("Unexpected keyword " + reader.locationDescription()); 1126 } 1127 1128 1129 /** 1130 * Creates a properly resolved File, based on the given word. 1131 */ 1132 private File file(String word) throws ParseException 1133 { 1134 String fileName = replaceSystemProperties(word); 1135 File file = new File(fileName); 1136 1137 // Try to get an absolute file. 1138 if (!file.isAbsolute()) 1139 { 1140 file = new File(reader.getBaseDir(), fileName); 1141 } 1142 1143 return file; 1144 } 1145 1146 1147 /** 1148 * Replaces any properties in the given word by their values. 1149 * For instance, the substring "<java.home>" is replaced by its value. 1150 */ 1151 private String replaceSystemProperties(String word) throws ParseException 1152 { 1153 int fromIndex = 0; 1154 while (true) 1155 { 1156 fromIndex = word.indexOf(ConfigurationConstants.OPEN_SYSTEM_PROPERTY, fromIndex); 1157 if (fromIndex < 0) 1158 { 1159 break; 1160 } 1161 1162 int toIndex = word.indexOf(ConfigurationConstants.CLOSE_SYSTEM_PROPERTY, fromIndex+1); 1163 if (toIndex < 0) 1164 { 1165 break; 1166 } 1167 1168 String propertyName = word.substring(fromIndex+1, toIndex); 1169 String propertyValue = properties.getProperty(propertyName); 1170 if (propertyValue == null) 1171 { 1172 throw new ParseException("Value of system property '" + propertyName + 1173 "' is undefined in " + reader.locationDescription()); 1174 } 1175 1176 word = word.substring(0, fromIndex) + propertyValue + word.substring(toIndex+1); 1177 1178 fromIndex += propertyValue.length(); 1179 } 1180 1181 return word; 1182 } 1183 1184 1185 /** 1186 * Reads the next word of the configuration in the 'nextWord' field, 1187 * throwing an exception if there is no next word. 1188 */ 1189 private void readNextWord(String expectedDescription) 1190 throws ParseException, IOException 1191 { 1192 readNextWord(expectedDescription, false, false); 1193 } 1194 1195 1196 /** 1197 * Reads the next word of the configuration in the 'nextWord' field, 1198 * throwing an exception if there is no next word. 1199 */ 1200 private void readNextWord(String expectedDescription, 1201 boolean isFileName, 1202 boolean expectingAtCharacter) 1203 throws ParseException, IOException 1204 { 1205 readNextWord(isFileName); 1206 if (configurationEnd(expectingAtCharacter)) 1207 { 1208 throw new ParseException("Expecting " + expectedDescription + 1209 " before " + reader.locationDescription()); 1210 } 1211 } 1212 1213 1214 /** 1215 * Reads the next word of the configuration in the 'nextWord' field. 1216 */ 1217 private void readNextWord() throws IOException 1218 { 1219 readNextWord(false); 1220 } 1221 1222 1223 /** 1224 * Reads the next word of the configuration in the 'nextWord' field. 1225 */ 1226 private void readNextWord(boolean isFileName) throws IOException 1227 { 1228 nextWord = reader.nextWord(isFileName); 1229 } 1230 1231 1232 /** 1233 * Returns whether the end of the configuration has been reached. 1234 */ 1235 private boolean configurationEnd() 1236 { 1237 return configurationEnd(false); 1238 } 1239 1240 1241 /** 1242 * Returns whether the end of the configuration has been reached. 1243 */ 1244 private boolean configurationEnd(boolean expectingAtCharacter) 1245 { 1246 return nextWord == null || 1247 nextWord.startsWith(ConfigurationConstants.OPTION_PREFIX) || 1248 (!expectingAtCharacter && 1249 nextWord.equals(ConfigurationConstants.AT_DIRECTIVE)); 1250 } 1251 1252 1253 /** 1254 * Checks whether the given word is a valid Java identifier and throws 1255 * a ParseException if it isn't. Wildcard characters are accepted. 1256 */ 1257 private void checkJavaIdentifier(String expectedDescription) 1258 throws ParseException 1259 { 1260 if (!isJavaIdentifier(nextWord)) 1261 { 1262 throw new ParseException("Expecting " + expectedDescription + 1263 " before " + reader.locationDescription()); 1264 } 1265 } 1266 1267 1268 /** 1269 * Returns whether the given word is a valid Java identifier. 1270 * Wildcard characters are accepted. 1271 */ 1272 private boolean isJavaIdentifier(String aWord) 1273 { 1274 if (aWord.length() == 0) 1275 { 1276 return false; 1277 } 1278 1279 for (int index = 0; index < aWord.length(); index++) 1280 { 1281 char c = aWord.charAt(index); 1282 if (!(Character.isJavaIdentifierPart(c) || 1283 c == '.' || 1284 c == '[' || 1285 c == ']' || 1286 c == '<' || 1287 c == '>' || 1288 c == '-' || 1289 c == '!' || 1290 c == '*' || 1291 c == '?' || 1292 c == '%')) 1293 { 1294 return false; 1295 } 1296 } 1297 1298 return true; 1299 } 1300 1301 1302 /** 1303 * Checks whether the given access flags are valid field access flags, 1304 * throwing a ParseException if they aren't. 1305 */ 1306 private void checkFieldAccessFlags(int requiredSetMemberAccessFlags, 1307 int requiredUnsetMemberAccessFlags) 1308 throws ParseException 1309 { 1310 if (((requiredSetMemberAccessFlags | 1311 requiredUnsetMemberAccessFlags) & 1312 ~ClassConstants.VALID_ACC_FIELD) != 0) 1313 { 1314 throw new ParseException("Invalid method access modifier for field before " + 1315 reader.locationDescription()); 1316 } 1317 } 1318 1319 1320 /** 1321 * Checks whether the given access flags are valid method access flags, 1322 * throwing a ParseException if they aren't. 1323 */ 1324 private void checkMethodAccessFlags(int requiredSetMemberAccessFlags, 1325 int requiredUnsetMemberAccessFlags) 1326 throws ParseException 1327 { 1328 if (((requiredSetMemberAccessFlags | 1329 requiredUnsetMemberAccessFlags) & 1330 ~ClassConstants.VALID_ACC_METHOD) != 0) 1331 { 1332 throw new ParseException("Invalid field access modifier for method before " + 1333 reader.locationDescription()); 1334 } 1335 } 1336 1337 1338 /** 1339 * A main method for testing configuration parsing. 1340 */ 1341 public static void main(String[] args) 1342 { 1343 try 1344 { 1345 ConfigurationParser parser = 1346 new ConfigurationParser(args, System.getProperties()); 1347 1348 try 1349 { 1350 parser.parse(new Configuration()); 1351 } 1352 catch (ParseException ex) 1353 { 1354 ex.printStackTrace(); 1355 } 1356 finally 1357 { 1358 parser.close(); 1359 } 1360 } 1361 catch (IOException ex) 1362 { 1363 ex.printStackTrace(); 1364 } 1365 } 1366} 1367