| /* | |
| * ProGuard -- shrinking, optimization, obfuscation, and preverification | |
| * of Java bytecode. | |
| * | |
| * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License as published by the Free | |
| * Software Foundation; either version 2 of the License, or (at your option) | |
| * any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| * more details. | |
| * | |
| * You should have received a copy of the GNU General Public License along | |
| * with this program; if not, write to the Free Software Foundation, Inc., | |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| package proguard; | |
| import proguard.classfile.*; | |
| import proguard.classfile.util.ClassUtil; | |
| import proguard.util.ListUtil; | |
| import java.io.*; | |
| import java.net.URL; | |
| import java.util.*; | |
| /** | |
| * This class parses ProGuard configurations. Configurations can be read from an | |
| * array of arguments or from a configuration file or URL. External references | |
| * in file names ('<...>') can be resolved against a given set of properties. | |
| * | |
| * @author Eric Lafortune | |
| */ | |
| public class ConfigurationParser | |
| { | |
| private final WordReader reader; | |
| private final Properties properties; | |
| private String nextWord; | |
| private String lastComments; | |
| /** | |
| * Creates a new ConfigurationParser for the given String arguments and | |
| * the given Properties. | |
| */ | |
| public ConfigurationParser(String[] args, | |
| Properties properties) throws IOException | |
| { | |
| this(args, null, properties); | |
| } | |
| /** | |
| * Creates a new ConfigurationParser for the given String arguments, | |
| * with the given base directory and the given Properties. | |
| */ | |
| public ConfigurationParser(String[] args, | |
| File baseDir, | |
| Properties properties) throws IOException | |
| { | |
| this(new ArgumentWordReader(args, baseDir), properties); | |
| } | |
| /** | |
| * Creates a new ConfigurationParser for the given lines, | |
| * with the given base directory and the given Properties. | |
| */ | |
| public ConfigurationParser(String lines, | |
| String description, | |
| File baseDir, | |
| Properties properties) throws IOException | |
| { | |
| this(new LineWordReader(new LineNumberReader(new StringReader(lines)), | |
| description, | |
| baseDir), | |
| properties); | |
| } | |
| /** | |
| * Creates a new ConfigurationParser for the given file, with the system | |
| * Properties. | |
| * @deprecated Temporary code for backward compatibility in Obclipse. | |
| */ | |
| public ConfigurationParser(File file) throws IOException | |
| { | |
| this(file, System.getProperties()); | |
| } | |
| /** | |
| * Creates a new ConfigurationParser for the given file and the given | |
| * Properties. | |
| */ | |
| public ConfigurationParser(File file, | |
| Properties properties) throws IOException | |
| { | |
| this(new FileWordReader(file), properties); | |
| } | |
| /** | |
| * Creates a new ConfigurationParser for the given URL and the given | |
| * Properties. | |
| */ | |
| public ConfigurationParser(URL url, | |
| Properties properties) throws IOException | |
| { | |
| this(new FileWordReader(url), properties); | |
| } | |
| /** | |
| * Creates a new ConfigurationParser for the given word reader and the | |
| * given Properties. | |
| */ | |
| public ConfigurationParser(WordReader reader, | |
| Properties properties) throws IOException | |
| { | |
| this.reader = reader; | |
| this.properties = properties; | |
| readNextWord(); | |
| } | |
| /** | |
| * Parses and returns the configuration. | |
| * @param configuration the configuration that is updated as a side-effect. | |
| * @throws ParseException if the any of the configuration settings contains | |
| * a syntax error. | |
| * @throws IOException if an IO error occurs while reading a configuration. | |
| */ | |
| public void parse(Configuration configuration) | |
| throws ParseException, IOException | |
| { | |
| while (nextWord != null) | |
| { | |
| lastComments = reader.lastComments(); | |
| // First include directives. | |
| if (ConfigurationConstants.AT_DIRECTIVE .startsWith(nextWord) || | |
| ConfigurationConstants.INCLUDE_DIRECTIVE .startsWith(nextWord)) configuration.lastModified = parseIncludeArgument(configuration.lastModified); | |
| else if (ConfigurationConstants.BASE_DIRECTORY_DIRECTIVE .startsWith(nextWord)) parseBaseDirectoryArgument(); | |
| // Then configuration options with or without arguments. | |
| else if (ConfigurationConstants.INJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, false); | |
| else if (ConfigurationConstants.OUTJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, true); | |
| else if (ConfigurationConstants.LIBRARYJARS_OPTION .startsWith(nextWord)) configuration.libraryJars = parseClassPathArgument(configuration.libraryJars, false); | |
| 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"); | |
| else if (ConfigurationConstants.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses = parseNoArgument(true); | |
| else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses = parseNoArgument(false); | |
| else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION.startsWith(nextWord)) configuration.skipNonPublicLibraryClassMembers = parseNoArgument(false); | |
| else if (ConfigurationConstants.TARGET_OPTION .startsWith(nextWord)) configuration.targetClassVersion = parseClassVersion(); | |
| else if (ConfigurationConstants.FORCE_PROCESSING_OPTION .startsWith(nextWord)) configuration.lastModified = parseNoArgument(Long.MAX_VALUE); | |
| else if (ConfigurationConstants.KEEP_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, false); | |
| else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, false); | |
| else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, false); | |
| else if (ConfigurationConstants.KEEP_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, true); | |
| else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, true); | |
| else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, true); | |
| else if (ConfigurationConstants.PRINT_SEEDS_OPTION .startsWith(nextWord)) configuration.printSeeds = parseOptionalFile(); | |
| // After '-keep'. | |
| else if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION .startsWith(nextWord)) configuration.keepDirectories = parseCommaSeparatedList("directory name", true, true, false, true, false, true, false, false, configuration.keepDirectories); | |
| else if (ConfigurationConstants.DONT_SHRINK_OPTION .startsWith(nextWord)) configuration.shrink = parseNoArgument(false); | |
| else if (ConfigurationConstants.PRINT_USAGE_OPTION .startsWith(nextWord)) configuration.printUsage = parseOptionalFile(); | |
| else if (ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION .startsWith(nextWord)) configuration.whyAreYouKeeping = parseClassSpecificationArguments(configuration.whyAreYouKeeping); | |
| else if (ConfigurationConstants.DONT_OPTIMIZE_OPTION .startsWith(nextWord)) configuration.optimize = parseNoArgument(false); | |
| else if (ConfigurationConstants.OPTIMIZATION_PASSES .startsWith(nextWord)) configuration.optimizationPasses = parseIntegerArgument(); | |
| else if (ConfigurationConstants.OPTIMIZATIONS .startsWith(nextWord)) configuration.optimizations = parseCommaSeparatedList("optimization name", true, false, false, false, false, false, false, false, configuration.optimizations); | |
| else if (ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION .startsWith(nextWord)) configuration.assumeNoSideEffects = parseClassSpecificationArguments(configuration.assumeNoSideEffects); | |
| else if (ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION .startsWith(nextWord)) configuration.allowAccessModification = parseNoArgument(true); | |
| else if (ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION .startsWith(nextWord)) configuration.mergeInterfacesAggressively = parseNoArgument(true); | |
| else if (ConfigurationConstants.DONT_OBFUSCATE_OPTION .startsWith(nextWord)) configuration.obfuscate = parseNoArgument(false); | |
| else if (ConfigurationConstants.PRINT_MAPPING_OPTION .startsWith(nextWord)) configuration.printMapping = parseOptionalFile(); | |
| else if (ConfigurationConstants.APPLY_MAPPING_OPTION .startsWith(nextWord)) configuration.applyMapping = parseFile(); | |
| else if (ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.obfuscationDictionary = parseFile(); | |
| else if (ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.classObfuscationDictionary = parseFile(); | |
| else if (ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.packageObfuscationDictionary = parseFile(); | |
| else if (ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION .startsWith(nextWord)) configuration.overloadAggressively = parseNoArgument(true); | |
| else if (ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.useUniqueClassMemberNames = parseNoArgument(true); | |
| else if (ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION .startsWith(nextWord)) configuration.useMixedCaseClassNames = parseNoArgument(false); | |
| else if (ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION .startsWith(nextWord)) configuration.keepPackageNames = parseCommaSeparatedList("package name", true, true, false, false, true, false, true, false, configuration.keepPackageNames); | |
| else if (ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION .startsWith(nextWord)) configuration.flattenPackageHierarchy = ClassUtil.internalClassName(parseOptionalArgument()); | |
| else if (ConfigurationConstants.REPACKAGE_CLASSES_OPTION .startsWith(nextWord)) configuration.repackageClasses = ClassUtil.internalClassName(parseOptionalArgument()); | |
| else if (ConfigurationConstants.DEFAULT_PACKAGE_OPTION .startsWith(nextWord)) configuration.repackageClasses = ClassUtil.internalClassName(parseOptionalArgument()); | |
| else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION .startsWith(nextWord)) configuration.keepAttributes = parseCommaSeparatedList("attribute name", true, true, false, false, true, false, false, false, configuration.keepAttributes); | |
| else if (ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION .startsWith(nextWord)) configuration.keepParameterNames = parseNoArgument(true); | |
| else if (ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION .startsWith(nextWord)) configuration.newSourceFileAttribute = parseOptionalArgument(); | |
| else if (ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION .startsWith(nextWord)) configuration.adaptClassStrings = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.adaptClassStrings); | |
| 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); | |
| 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); | |
| else if (ConfigurationConstants.DONT_PREVERIFY_OPTION .startsWith(nextWord)) configuration.preverify = parseNoArgument(false); | |
| else if (ConfigurationConstants.MICRO_EDITION_OPTION .startsWith(nextWord)) configuration.microEdition = parseNoArgument(true); | |
| else if (ConfigurationConstants.VERBOSE_OPTION .startsWith(nextWord)) configuration.verbose = parseNoArgument(true); | |
| else if (ConfigurationConstants.DONT_NOTE_OPTION .startsWith(nextWord)) configuration.note = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.note); | |
| else if (ConfigurationConstants.DONT_WARN_OPTION .startsWith(nextWord)) configuration.warn = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.warn); | |
| else if (ConfigurationConstants.IGNORE_WARNINGS_OPTION .startsWith(nextWord)) configuration.ignoreWarnings = parseNoArgument(true); | |
| else if (ConfigurationConstants.PRINT_CONFIGURATION_OPTION .startsWith(nextWord)) configuration.printConfiguration = parseOptionalFile(); | |
| else if (ConfigurationConstants.DUMP_OPTION .startsWith(nextWord)) configuration.dump = parseOptionalFile(); | |
| else | |
| { | |
| throw new ParseException("Unknown option " + reader.locationDescription()); | |
| } | |
| } | |
| } | |
| /** | |
| * Closes the configuration. | |
| * @throws IOException if an IO error occurs while closing the configuration. | |
| */ | |
| public void close() throws IOException | |
| { | |
| if (reader != null) | |
| { | |
| reader.close(); | |
| } | |
| } | |
| private long parseIncludeArgument(long lastModified) throws ParseException, IOException | |
| { | |
| // Read the configuration file name. | |
| readNextWord("configuration file name", true, false); | |
| File file = file(nextWord); | |
| reader.includeWordReader(new FileWordReader(file)); | |
| readNextWord(); | |
| return Math.max(lastModified, file.lastModified()); | |
| } | |
| private void parseBaseDirectoryArgument() throws ParseException, IOException | |
| { | |
| // Read the base directory name. | |
| readNextWord("base directory name", true, false); | |
| reader.setBaseDir(file(nextWord)); | |
| readNextWord(); | |
| } | |
| private ClassPath parseClassPathArgument(ClassPath classPath, | |
| boolean isOutput) | |
| throws ParseException, IOException | |
| { | |
| // Create a new List if necessary. | |
| if (classPath == null) | |
| { | |
| classPath = new ClassPath(); | |
| } | |
| while (true) | |
| { | |
| // Read the next jar name. | |
| readNextWord("jar or directory name", true, false); | |
| // Create a new class path entry. | |
| ClassPathEntry entry = new ClassPathEntry(file(nextWord), isOutput); | |
| // Read the opening parenthesis or the separator, if any. | |
| readNextWord(); | |
| // Read the optional filters. | |
| if (!configurationEnd() && | |
| ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) | |
| { | |
| // Read all filters in an array. | |
| List[] filters = new List[7]; | |
| int counter = 0; | |
| do | |
| { | |
| // Read the filter. | |
| filters[counter++] = | |
| parseCommaSeparatedList("filter", true, true, true, true, false, true, false, false, null); | |
| } | |
| while (counter < filters.length && | |
| ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)); | |
| // Make sure there is a closing parenthesis. | |
| if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) | |
| { | |
| throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + | |
| "' or '" + ConfigurationConstants.SEPARATOR_KEYWORD + | |
| "', or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + | |
| "' before " + reader.locationDescription()); | |
| } | |
| // Set all filters from the array on the entry. | |
| entry.setFilter(filters[--counter]); | |
| if (counter > 0) | |
| { | |
| entry.setJarFilter(filters[--counter]); | |
| if (counter > 0) | |
| { | |
| entry.setWarFilter(filters[--counter]); | |
| if (counter > 0) | |
| { | |
| entry.setEarFilter(filters[--counter]); | |
| if (counter > 0) | |
| { | |
| entry.setZipFilter(filters[--counter]); | |
| if (counter > 0) | |
| { | |
| // For backward compatibility, the apk | |
| // filter comes second in the list. | |
| entry.setApkFilter(filters[--counter]); | |
| if (counter > 0) | |
| { | |
| // For backward compatibility, the aar | |
| // filter comes first in the list. | |
| entry.setAarFilter(filters[--counter]); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Read the separator, if any. | |
| readNextWord(); | |
| } | |
| // Add the entry to the list. | |
| classPath.add(entry); | |
| if (configurationEnd()) | |
| { | |
| return classPath; | |
| } | |
| if (!nextWord.equals(ConfigurationConstants.JAR_SEPARATOR_KEYWORD)) | |
| { | |
| throw new ParseException("Expecting class path separator '" + ConfigurationConstants.JAR_SEPARATOR_KEYWORD + | |
| "' before " + reader.locationDescription()); | |
| } | |
| } | |
| } | |
| private int parseClassVersion() | |
| throws ParseException, IOException | |
| { | |
| // Read the obligatory target. | |
| readNextWord("java version"); | |
| int classVersion = ClassUtil.internalClassVersion(nextWord); | |
| if (classVersion == 0) | |
| { | |
| throw new ParseException("Unsupported java version " + reader.locationDescription()); | |
| } | |
| readNextWord(); | |
| return classVersion; | |
| } | |
| private int parseIntegerArgument() | |
| throws ParseException, IOException | |
| { | |
| try | |
| { | |
| // Read the obligatory integer. | |
| readNextWord("integer"); | |
| int integer = Integer.parseInt(nextWord); | |
| readNextWord(); | |
| return integer; | |
| } | |
| catch (NumberFormatException e) | |
| { | |
| throw new ParseException("Expecting integer argument instead of '" + nextWord + | |
| "' before " + reader.locationDescription()); | |
| } | |
| } | |
| private File parseFile() | |
| throws ParseException, IOException | |
| { | |
| // Read the obligatory file name. | |
| readNextWord("file name", true, false); | |
| // Make sure the file is properly resolved. | |
| File file = file(nextWord); | |
| readNextWord(); | |
| return file; | |
| } | |
| private File parseOptionalFile() | |
| throws ParseException, IOException | |
| { | |
| // Read the optional file name. | |
| readNextWord(true); | |
| // Didn't the user specify a file name? | |
| if (configurationEnd()) | |
| { | |
| return Configuration.STD_OUT; | |
| } | |
| // Make sure the file is properly resolved. | |
| File file = file(nextWord); | |
| readNextWord(); | |
| return file; | |
| } | |
| private String parseOptionalArgument() throws IOException | |
| { | |
| // Read the optional argument. | |
| readNextWord(); | |
| // Didn't the user specify an argument? | |
| if (configurationEnd()) | |
| { | |
| return ""; | |
| } | |
| String argument = nextWord; | |
| readNextWord(); | |
| return argument; | |
| } | |
| private boolean parseNoArgument(boolean value) throws IOException | |
| { | |
| readNextWord(); | |
| return value; | |
| } | |
| private long parseNoArgument(long value) throws IOException | |
| { | |
| readNextWord(); | |
| return value; | |
| } | |
| private List parseKeepClassSpecificationArguments(List keepClassSpecifications, | |
| boolean markClasses, | |
| boolean markConditionally, | |
| boolean allowShrinking) | |
| throws ParseException, IOException | |
| { | |
| // Create a new List if necessary. | |
| if (keepClassSpecifications == null) | |
| { | |
| keepClassSpecifications = new ArrayList(); | |
| } | |
| boolean markDescriptorClasses = false; | |
| //boolean allowShrinking = false; | |
| boolean allowOptimization = false; | |
| boolean allowObfuscation = false; | |
| // Read the keep modifiers. | |
| while (true) | |
| { | |
| readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + | |
| "', '" + JavaConstants.ACC_INTERFACE + | |
| "', or '" + JavaConstants.ACC_ENUM + "'", | |
| false, true); | |
| if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord)) | |
| { | |
| // Not a comma. Stop parsing the keep modifiers. | |
| break; | |
| } | |
| readNextWord("keyword '" + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + | |
| "', '" + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + | |
| "', or '" + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + "'"); | |
| if (ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION.startsWith(nextWord)) | |
| { | |
| markDescriptorClasses = true; | |
| } | |
| else if (ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION .startsWith(nextWord)) | |
| { | |
| allowShrinking = true; | |
| } | |
| else if (ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION .startsWith(nextWord)) | |
| { | |
| allowOptimization = true; | |
| } | |
| else if (ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION .startsWith(nextWord)) | |
| { | |
| allowObfuscation = true; | |
| } | |
| else | |
| { | |
| throw new ParseException("Expecting keyword '" + ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION + | |
| "', '" + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + | |
| "', '" + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + | |
| "', or '" + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + | |
| "' before " + reader.locationDescription()); | |
| } | |
| } | |
| // Read the class configuration. | |
| ClassSpecification classSpecification = | |
| parseClassSpecificationArguments(); | |
| // Create and add the keep configuration. | |
| keepClassSpecifications.add(new KeepClassSpecification(markClasses, | |
| markConditionally, | |
| markDescriptorClasses, | |
| allowShrinking, | |
| allowOptimization, | |
| allowObfuscation, | |
| classSpecification)); | |
| return keepClassSpecifications; | |
| } | |
| private List parseClassSpecificationArguments(List classSpecifications) | |
| throws ParseException, IOException | |
| { | |
| // Create a new List if necessary. | |
| if (classSpecifications == null) | |
| { | |
| classSpecifications = new ArrayList(); | |
| } | |
| // Read and add the class configuration. | |
| readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + | |
| "', '" + JavaConstants.ACC_INTERFACE + | |
| "', or '" + JavaConstants.ACC_ENUM + "'", | |
| false, true); | |
| classSpecifications.add(parseClassSpecificationArguments()); | |
| return classSpecifications; | |
| } | |
| /** | |
| * Parses and returns a class specification. | |
| * @throws ParseException if the class specification contains a syntax error. | |
| * @throws IOException if an IO error occurs while reading the class | |
| * specification. | |
| */ | |
| public ClassSpecification parseClassSpecificationArguments() | |
| throws ParseException, IOException | |
| { | |
| // Clear the annotation type. | |
| String annotationType = null; | |
| // Clear the class access modifiers. | |
| int requiredSetClassAccessFlags = 0; | |
| int requiredUnsetClassAccessFlags = 0; | |
| // Parse the class annotations and access modifiers until the class keyword. | |
| while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord)) | |
| { | |
| // Strip the negating sign, if any. | |
| boolean negated = | |
| nextWord.startsWith(ConfigurationConstants.NEGATOR_KEYWORD); | |
| String strippedWord = negated ? | |
| nextWord.substring(1) : | |
| nextWord; | |
| // Parse the class access modifiers. | |
| int accessFlag = | |
| strippedWord.equals(JavaConstants.ACC_PUBLIC) ? ClassConstants.ACC_PUBLIC : | |
| strippedWord.equals(JavaConstants.ACC_FINAL) ? ClassConstants.ACC_FINAL : | |
| strippedWord.equals(JavaConstants.ACC_INTERFACE) ? ClassConstants.ACC_INTERFACE : | |
| strippedWord.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : | |
| strippedWord.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : | |
| strippedWord.equals(JavaConstants.ACC_ANNOTATION) ? ClassConstants.ACC_ANNOTATTION : | |
| strippedWord.equals(JavaConstants.ACC_ENUM) ? ClassConstants.ACC_ENUM : | |
| unknownAccessFlag(); | |
| // Is it an annotation modifier? | |
| if (accessFlag == ClassConstants.ACC_ANNOTATTION) | |
| { | |
| // Already read the next word. | |
| readNextWord("annotation type or keyword '" + JavaConstants.ACC_INTERFACE + "'", | |
| false, false); | |
| // Is the next word actually an annotation type? | |
| if (!nextWord.equals(JavaConstants.ACC_INTERFACE) && | |
| !nextWord.equals(JavaConstants.ACC_ENUM) && | |
| !nextWord.equals(ConfigurationConstants.CLASS_KEYWORD)) | |
| { | |
| // Parse the annotation type. | |
| annotationType = | |
| ListUtil.commaSeparatedString( | |
| parseCommaSeparatedList("annotation type", | |
| false, false, false, false, true, false, false, true, null), false); | |
| // Continue parsing the access modifier that we just read | |
| // in the next cycle. | |
| continue; | |
| } | |
| // Otherwise just handle the annotation modifier. | |
| } | |
| if (!negated) | |
| { | |
| requiredSetClassAccessFlags |= accessFlag; | |
| } | |
| else | |
| { | |
| requiredUnsetClassAccessFlags |= accessFlag; | |
| } | |
| if ((requiredSetClassAccessFlags & | |
| requiredUnsetClassAccessFlags) != 0) | |
| { | |
| throw new ParseException("Conflicting class access modifiers for '" + strippedWord + | |
| "' before " + reader.locationDescription()); | |
| } | |
| if (strippedWord.equals(JavaConstants.ACC_INTERFACE) || | |
| strippedWord.equals(JavaConstants.ACC_ENUM) || | |
| strippedWord.equals(ConfigurationConstants.CLASS_KEYWORD)) | |
| { | |
| // The interface or enum keyword. Stop parsing the class flags. | |
| break; | |
| } | |
| // Should we read the next word? | |
| if (accessFlag != ClassConstants.ACC_ANNOTATTION) | |
| { | |
| readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + | |
| "', '" + JavaConstants.ACC_INTERFACE + | |
| "', or '" + JavaConstants.ACC_ENUM + "'", | |
| false, true); | |
| } | |
| } | |
| // Parse the class name part. | |
| String externalClassName = | |
| ListUtil.commaSeparatedString( | |
| parseCommaSeparatedList("class name or interface name", | |
| true, false, false, false, true, false, false, false, null), false); | |
| // For backward compatibility, allow a single "*" wildcard to match any | |
| // class. | |
| String className = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalClassName) ? | |
| null : | |
| ClassUtil.internalClassName(externalClassName); | |
| // Clear the annotation type and the class name of the extends part. | |
| String extendsAnnotationType = null; | |
| String extendsClassName = null; | |
| if (!configurationEnd()) | |
| { | |
| // Parse 'implements ...' or 'extends ...' part, if any. | |
| if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) || | |
| ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord)) | |
| { | |
| readNextWord("class name or interface name", false, true); | |
| // Parse the annotation type, if any. | |
| if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) | |
| { | |
| extendsAnnotationType = | |
| ListUtil.commaSeparatedString( | |
| parseCommaSeparatedList("annotation type", | |
| true, false, false, false, true, false, false, true, null), false); | |
| } | |
| String externalExtendsClassName = | |
| ListUtil.commaSeparatedString( | |
| parseCommaSeparatedList("class name or interface name", | |
| false, false, false, false, true, false, false, false, null), false); | |
| extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalExtendsClassName) ? | |
| null : | |
| ClassUtil.internalClassName(externalExtendsClassName); | |
| } | |
| } | |
| // Create the basic class specification. | |
| ClassSpecification classSpecification = | |
| new ClassSpecification(lastComments, | |
| requiredSetClassAccessFlags, | |
| requiredUnsetClassAccessFlags, | |
| annotationType, | |
| className, | |
| extendsAnnotationType, | |
| extendsClassName); | |
| // Now add any class members to this class specification. | |
| if (!configurationEnd()) | |
| { | |
| // Check the class member opening part. | |
| if (!ConfigurationConstants.OPEN_KEYWORD.equals(nextWord)) | |
| { | |
| throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_KEYWORD + | |
| "' at " + reader.locationDescription()); | |
| } | |
| // Parse all class members. | |
| while (true) | |
| { | |
| readNextWord("class member description" + | |
| " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'", | |
| false, true); | |
| if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD)) | |
| { | |
| // The closing brace. Stop parsing the class members. | |
| readNextWord(); | |
| break; | |
| } | |
| parseMemberSpecificationArguments(externalClassName, | |
| classSpecification); | |
| } | |
| } | |
| return classSpecification; | |
| } | |
| private void parseMemberSpecificationArguments(String externalClassName, | |
| ClassSpecification classSpecification) | |
| throws ParseException, IOException | |
| { | |
| // Clear the annotation name. | |
| String annotationType = null; | |
| // Parse the class member access modifiers, if any. | |
| int requiredSetMemberAccessFlags = 0; | |
| int requiredUnsetMemberAccessFlags = 0; | |
| while (!configurationEnd(true)) | |
| { | |
| // Parse the annotation type, if any. | |
| if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) | |
| { | |
| annotationType = | |
| ListUtil.commaSeparatedString( | |
| parseCommaSeparatedList("annotation type", | |
| true, false, false, false, true, false, false, true, null), false); | |
| continue; | |
| } | |
| String strippedWord = nextWord.startsWith("!") ? | |
| nextWord.substring(1) : | |
| nextWord; | |
| // Parse the class member access modifiers. | |
| int accessFlag = | |
| strippedWord.equals(JavaConstants.ACC_PUBLIC) ? ClassConstants.ACC_PUBLIC : | |
| strippedWord.equals(JavaConstants.ACC_PRIVATE) ? ClassConstants.ACC_PRIVATE : | |
| strippedWord.equals(JavaConstants.ACC_PROTECTED) ? ClassConstants.ACC_PROTECTED : | |
| strippedWord.equals(JavaConstants.ACC_STATIC) ? ClassConstants.ACC_STATIC : | |
| strippedWord.equals(JavaConstants.ACC_FINAL) ? ClassConstants.ACC_FINAL : | |
| strippedWord.equals(JavaConstants.ACC_SYNCHRONIZED) ? ClassConstants.ACC_SYNCHRONIZED : | |
| strippedWord.equals(JavaConstants.ACC_VOLATILE) ? ClassConstants.ACC_VOLATILE : | |
| strippedWord.equals(JavaConstants.ACC_TRANSIENT) ? ClassConstants.ACC_TRANSIENT : | |
| strippedWord.equals(JavaConstants.ACC_BRIDGE) ? ClassConstants.ACC_BRIDGE : | |
| strippedWord.equals(JavaConstants.ACC_VARARGS) ? ClassConstants.ACC_VARARGS : | |
| strippedWord.equals(JavaConstants.ACC_NATIVE) ? ClassConstants.ACC_NATIVE : | |
| strippedWord.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : | |
| strippedWord.equals(JavaConstants.ACC_STRICT) ? ClassConstants.ACC_STRICT : | |
| strippedWord.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : | |
| 0; | |
| if (accessFlag == 0) | |
| { | |
| // Not a class member access modifier. Stop parsing them. | |
| break; | |
| } | |
| if (strippedWord.equals(nextWord)) | |
| { | |
| requiredSetMemberAccessFlags |= accessFlag; | |
| } | |
| else | |
| { | |
| requiredUnsetMemberAccessFlags |= accessFlag; | |
| } | |
| // Make sure the user doesn't try to set and unset the same | |
| // access flags simultaneously. | |
| if ((requiredSetMemberAccessFlags & | |
| requiredUnsetMemberAccessFlags) != 0) | |
| { | |
| throw new ParseException("Conflicting class member access modifiers for " + | |
| reader.locationDescription()); | |
| } | |
| readNextWord("class member description"); | |
| } | |
| // Parse the class member type and name part. | |
| // Did we get a special wildcard? | |
| if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord) || | |
| ConfigurationConstants.ANY_FIELD_KEYWORD .equals(nextWord) || | |
| ConfigurationConstants.ANY_METHOD_KEYWORD .equals(nextWord)) | |
| { | |
| // Act according to the type of wildcard.. | |
| if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord)) | |
| { | |
| checkFieldAccessFlags(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags); | |
| checkMethodAccessFlags(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags); | |
| classSpecification.addField( | |
| new MemberSpecification(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags, | |
| annotationType, | |
| null, | |
| null)); | |
| classSpecification.addMethod( | |
| new MemberSpecification(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags, | |
| annotationType, | |
| null, | |
| null)); | |
| } | |
| else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord)) | |
| { | |
| checkFieldAccessFlags(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags); | |
| classSpecification.addField( | |
| new MemberSpecification(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags, | |
| annotationType, | |
| null, | |
| null)); | |
| } | |
| else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord)) | |
| { | |
| checkMethodAccessFlags(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags); | |
| classSpecification.addMethod( | |
| new MemberSpecification(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags, | |
| annotationType, | |
| null, | |
| null)); | |
| } | |
| // We still have to read the closing separator. | |
| readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); | |
| if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) | |
| { | |
| throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + | |
| "' before " + reader.locationDescription()); | |
| } | |
| } | |
| else | |
| { | |
| // Make sure we have a proper type. | |
| checkJavaIdentifier("java type"); | |
| String type = nextWord; | |
| readNextWord("class member name"); | |
| String name = nextWord; | |
| // Did we get just one word before the opening parenthesis? | |
| if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name)) | |
| { | |
| // This must be a constructor then. | |
| // Make sure the type is a proper constructor name. | |
| if (!(type.equals(ClassConstants.METHOD_NAME_INIT) || | |
| type.equals(externalClassName) || | |
| type.equals(ClassUtil.externalShortClassName(externalClassName)))) | |
| { | |
| throw new ParseException("Expecting type and name " + | |
| "instead of just '" + type + | |
| "' before " + reader.locationDescription()); | |
| } | |
| // Assign the fixed constructor type and name. | |
| type = JavaConstants.TYPE_VOID; | |
| name = ClassConstants.METHOD_NAME_INIT; | |
| } | |
| else | |
| { | |
| // It's not a constructor. | |
| // Make sure we have a proper name. | |
| checkJavaIdentifier("class member name"); | |
| // Read the opening parenthesis or the separating | |
| // semi-colon. | |
| readNextWord("opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + | |
| "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); | |
| } | |
| // Are we looking at a field, a method, or something else? | |
| if (ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) | |
| { | |
| // It's a field. | |
| checkFieldAccessFlags(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags); | |
| // We already have a field descriptor. | |
| String descriptor = ClassUtil.internalType(type); | |
| // Add the field. | |
| classSpecification.addField( | |
| new MemberSpecification(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags, | |
| annotationType, | |
| name, | |
| descriptor)); | |
| } | |
| else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) | |
| { | |
| // It's a method. | |
| checkMethodAccessFlags(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags); | |
| // Parse the method arguments. | |
| String descriptor = | |
| ClassUtil.internalMethodDescriptor(type, | |
| parseCommaSeparatedList("argument", true, true, true, false, true, false, false, false, null)); | |
| if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) | |
| { | |
| throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + | |
| "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + | |
| "' before " + reader.locationDescription()); | |
| } | |
| // Read the separator after the closing parenthesis. | |
| readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); | |
| if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) | |
| { | |
| throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + | |
| "' before " + reader.locationDescription()); | |
| } | |
| // Add the method. | |
| classSpecification.addMethod( | |
| new MemberSpecification(requiredSetMemberAccessFlags, | |
| requiredUnsetMemberAccessFlags, | |
| annotationType, | |
| name, | |
| descriptor)); | |
| } | |
| else | |
| { | |
| // It doesn't look like a field or a method. | |
| throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + | |
| "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + | |
| "' before " + reader.locationDescription()); | |
| } | |
| } | |
| } | |
| /** | |
| * Reads a comma-separated list of java identifiers or of file names. | |
| * Examples of invocation arguments: | |
| * ("directory name", true, true, false, true, false, true, false, false, ...) | |
| * ("optimization", true, false, false, false, false, false, false, false, ...) | |
| * ("package name", true, true, false, false, true, false, true, false, ...) | |
| * ("attribute name", true, true, false, false, true, false, false, false, ...) | |
| * ("class name", true, true, false, false, true, false, true, false, ...) | |
| * ("resource file", true, true, false, true, false, false, false, false, ...) | |
| * ("resource file", true, true, false, true, false, false, false, false, ...) | |
| * ("class name", true, true, false, false, true, false, true, false, ...) | |
| * ("class name", true, true, false, false, true, false, true, false, ...) | |
| * ("filter", true, true, true, true, false, true, false, false, ...) | |
| * ("annotation ", false, false, false, false, true, false, false, true, ...) | |
| * ("class name ", true, false, false, false, true, false, false, false, ...) | |
| * ("annotation ", true, false, false, false, true, false, false, true, ...) | |
| * ("class name ", false, false, false, false, true, false, false, false, ...) | |
| * ("annotation ", true, false, false, false, true, false, false, true, ...) | |
| * ("argument", true, true, true, false, true, false, false, false, ...) | |
| */ | |
| private List parseCommaSeparatedList(String expectedDescription, | |
| boolean readFirstWord, | |
| boolean allowEmptyList, | |
| boolean expectClosingParenthesis, | |
| boolean isFileName, | |
| boolean checkJavaIdentifiers, | |
| boolean replaceSystemProperties, | |
| boolean replaceExternalClassNames, | |
| boolean replaceExternalTypes, | |
| List list) | |
| throws ParseException, IOException | |
| { | |
| if (list == null) | |
| { | |
| list = new ArrayList(); | |
| } | |
| if (readFirstWord) | |
| { | |
| if (!allowEmptyList) | |
| { | |
| // Read the first list entry. | |
| readNextWord(expectedDescription, isFileName, false); | |
| } | |
| else if (expectClosingParenthesis) | |
| { | |
| // Read the first list entry. | |
| readNextWord(expectedDescription, isFileName, false); | |
| // Return if the entry is actually empty (an empty file name or | |
| // a closing parenthesis). | |
| if (nextWord.length() == 0) | |
| { | |
| // Read the closing parenthesis | |
| readNextWord("closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + | |
| "'"); | |
| return list; | |
| } | |
| else if (nextWord.equals(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD)) | |
| { | |
| return list; | |
| } | |
| } | |
| else | |
| { | |
| // Read the first list entry, if there is any. | |
| readNextWord(isFileName); | |
| // Check if the list is empty. | |
| if (configurationEnd()) | |
| { | |
| return list; | |
| } | |
| } | |
| } | |
| while (true) | |
| { | |
| if (checkJavaIdentifiers) | |
| { | |
| checkJavaIdentifier("java type"); | |
| } | |
| if (replaceSystemProperties) | |
| { | |
| nextWord = replaceSystemProperties(nextWord); | |
| } | |
| if (replaceExternalClassNames) | |
| { | |
| nextWord = ClassUtil.internalClassName(nextWord); | |
| } | |
| if (replaceExternalTypes) | |
| { | |
| nextWord = ClassUtil.internalType(nextWord); | |
| } | |
| list.add(nextWord); | |
| if (expectClosingParenthesis) | |
| { | |
| // Read a comma (or a closing parenthesis, or a different word). | |
| readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + | |
| "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + | |
| "'"); | |
| } | |
| else | |
| { | |
| // Read a comma (or a different word). | |
| readNextWord(); | |
| } | |
| if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord)) | |
| { | |
| return list; | |
| } | |
| // Read the next list entry. | |
| readNextWord(expectedDescription, isFileName, false); | |
| } | |
| } | |
| /** | |
| * Throws a ParseException for an unexpected keyword. | |
| */ | |
| private int unknownAccessFlag() throws ParseException | |
| { | |
| throw new ParseException("Unexpected keyword " + reader.locationDescription()); | |
| } | |
| /** | |
| * Creates a properly resolved File, based on the given word. | |
| */ | |
| private File file(String word) throws ParseException | |
| { | |
| String fileName = replaceSystemProperties(word); | |
| File file = new File(fileName); | |
| // Try to get an absolute file. | |
| if (!file.isAbsolute()) | |
| { | |
| file = new File(reader.getBaseDir(), fileName); | |
| } | |
| return file; | |
| } | |
| /** | |
| * Replaces any properties in the given word by their values. | |
| * For instance, the substring "<java.home>" is replaced by its value. | |
| */ | |
| private String replaceSystemProperties(String word) throws ParseException | |
| { | |
| int fromIndex = 0; | |
| while (true) | |
| { | |
| fromIndex = word.indexOf(ConfigurationConstants.OPEN_SYSTEM_PROPERTY, fromIndex); | |
| if (fromIndex < 0) | |
| { | |
| break; | |
| } | |
| int toIndex = word.indexOf(ConfigurationConstants.CLOSE_SYSTEM_PROPERTY, fromIndex+1); | |
| if (toIndex < 0) | |
| { | |
| break; | |
| } | |
| String propertyName = word.substring(fromIndex+1, toIndex); | |
| String propertyValue = properties.getProperty(propertyName); | |
| if (propertyValue == null) | |
| { | |
| throw new ParseException("Value of system property '" + propertyName + | |
| "' is undefined in " + reader.locationDescription()); | |
| } | |
| word = word.substring(0, fromIndex) + propertyValue + word.substring(toIndex+1); | |
| fromIndex += propertyValue.length(); | |
| } | |
| return word; | |
| } | |
| /** | |
| * Reads the next word of the configuration in the 'nextWord' field, | |
| * throwing an exception if there is no next word. | |
| */ | |
| private void readNextWord(String expectedDescription) | |
| throws ParseException, IOException | |
| { | |
| readNextWord(expectedDescription, false, false); | |
| } | |
| /** | |
| * Reads the next word of the configuration in the 'nextWord' field, | |
| * throwing an exception if there is no next word. | |
| */ | |
| private void readNextWord(String expectedDescription, | |
| boolean isFileName, | |
| boolean expectingAtCharacter) | |
| throws ParseException, IOException | |
| { | |
| readNextWord(isFileName); | |
| if (configurationEnd(expectingAtCharacter)) | |
| { | |
| throw new ParseException("Expecting " + expectedDescription + | |
| " before " + reader.locationDescription()); | |
| } | |
| } | |
| /** | |
| * Reads the next word of the configuration in the 'nextWord' field. | |
| */ | |
| private void readNextWord() throws IOException | |
| { | |
| readNextWord(false); | |
| } | |
| /** | |
| * Reads the next word of the configuration in the 'nextWord' field. | |
| */ | |
| private void readNextWord(boolean isFileName) throws IOException | |
| { | |
| nextWord = reader.nextWord(isFileName); | |
| } | |
| /** | |
| * Returns whether the end of the configuration has been reached. | |
| */ | |
| private boolean configurationEnd() | |
| { | |
| return configurationEnd(false); | |
| } | |
| /** | |
| * Returns whether the end of the configuration has been reached. | |
| */ | |
| private boolean configurationEnd(boolean expectingAtCharacter) | |
| { | |
| return nextWord == null || | |
| nextWord.startsWith(ConfigurationConstants.OPTION_PREFIX) || | |
| (!expectingAtCharacter && | |
| nextWord.equals(ConfigurationConstants.AT_DIRECTIVE)); | |
| } | |
| /** | |
| * Checks whether the given word is a valid Java identifier and throws | |
| * a ParseException if it isn't. Wildcard characters are accepted. | |
| */ | |
| private void checkJavaIdentifier(String expectedDescription) | |
| throws ParseException | |
| { | |
| if (!isJavaIdentifier(nextWord)) | |
| { | |
| throw new ParseException("Expecting " + expectedDescription + | |
| " before " + reader.locationDescription()); | |
| } | |
| } | |
| /** | |
| * Returns whether the given word is a valid Java identifier. | |
| * Wildcard characters are accepted. | |
| */ | |
| private boolean isJavaIdentifier(String aWord) | |
| { | |
| if (aWord.length() == 0) | |
| { | |
| return false; | |
| } | |
| for (int index = 0; index < aWord.length(); index++) | |
| { | |
| char c = aWord.charAt(index); | |
| if (!(Character.isJavaIdentifierPart(c) || | |
| c == '.' || | |
| c == '[' || | |
| c == ']' || | |
| c == '<' || | |
| c == '>' || | |
| c == '-' || | |
| c == '!' || | |
| c == '*' || | |
| c == '?' || | |
| c == '%')) | |
| { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Checks whether the given access flags are valid field access flags, | |
| * throwing a ParseException if they aren't. | |
| */ | |
| private void checkFieldAccessFlags(int requiredSetMemberAccessFlags, | |
| int requiredUnsetMemberAccessFlags) | |
| throws ParseException | |
| { | |
| if (((requiredSetMemberAccessFlags | | |
| requiredUnsetMemberAccessFlags) & | |
| ~ClassConstants.VALID_ACC_FIELD) != 0) | |
| { | |
| throw new ParseException("Invalid method access modifier for field before " + | |
| reader.locationDescription()); | |
| } | |
| } | |
| /** | |
| * Checks whether the given access flags are valid method access flags, | |
| * throwing a ParseException if they aren't. | |
| */ | |
| private void checkMethodAccessFlags(int requiredSetMemberAccessFlags, | |
| int requiredUnsetMemberAccessFlags) | |
| throws ParseException | |
| { | |
| if (((requiredSetMemberAccessFlags | | |
| requiredUnsetMemberAccessFlags) & | |
| ~ClassConstants.VALID_ACC_METHOD) != 0) | |
| { | |
| throw new ParseException("Invalid field access modifier for method before " + | |
| reader.locationDescription()); | |
| } | |
| } | |
| /** | |
| * A main method for testing configuration parsing. | |
| */ | |
| public static void main(String[] args) | |
| { | |
| try | |
| { | |
| ConfigurationParser parser = | |
| new ConfigurationParser(args, System.getProperties()); | |
| try | |
| { | |
| parser.parse(new Configuration()); | |
| } | |
| catch (ParseException ex) | |
| { | |
| ex.printStackTrace(); | |
| } | |
| finally | |
| { | |
| parser.close(); | |
| } | |
| } | |
| catch (IOException ex) | |
| { | |
| ex.printStackTrace(); | |
| } | |
| } | |
| } |