/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package flex2.compiler.config; import java.lang.reflect.Method; import java.lang.reflect.Field; import flex2.compiler.util.CompilerMessage; /** * The base class used for get*Info() methods. It allows * configuration objects to provide information about a configuration * option. For example, whether it's hidden, deprecated, advanced, * etc. Subclasses should override ConfigurationInfo's methods to * change the defaults. * * @author Roger Gonzalez */ public class ConfigurationInfo { /** * This ctor is used when everything can be introspected off the setter method, or else * when the names/types are provided by method overrides rather than ctor arguments */ public ConfigurationInfo() { this.argcount = -2; this.argnames = null; } /** * Simple ctor for restricting the number of arguments. * @param argcount number of args, -1 for an infinite list */ public ConfigurationInfo( int argcount ) { this.argcount = argcount; this.argnames = null; } /** * Simple ctor for naming the arguments. * @param argnames list of argnames, argcount will default to # of elements */ public ConfigurationInfo( String argnames[] ) { this.argcount = argnames.length; this.argnames = argnames; } /** * Use this ctor when you want to set a single list of some number of identically named args * @param argcount number of arguments (-1 for infinite) * @param argname name of each argument */ public ConfigurationInfo( int argcount, String argname ) { this.argcount = argcount; this.argnames = new String[]{ argname }; } /** * More unusual ctor, this would let you have the first few args named one thing, the rest named something else. * It is far more likely that you want a constrained list of names or else an arbitrary list of identical names. * @param argcount number of arguments * @param argnames array of argument names */ public ConfigurationInfo( int argcount, String argnames[] ) { this.argcount = argcount; this.argnames = argnames; } public final int getArgCount() { return argcount; } private int argcount = -2; private static String classToArgName( Class c ) { // we only support builtin classnames! String className = c.getName(); if (className.startsWith( "java.lang." )) className = className.substring( "java.lang.".length() ); return className.toLowerCase(); } /** * Return the name of each parameter. The default implementation is usually * sufficient for simple cases, but one could do wacky things here like support * an infinite list of alternating arg names. * @param argnum * @return name of argument */ public String getArgName( int argnum ) { if ((argnames == null) || (argnames.length == 0)) { return classToArgName( getArgType( argnum ) ); } else if (argnum >= argnames.length) { return argnames[argnames.length-1]; } else { return argnames[argnum]; } } /** * Return the type of each parameter. This is computed based on your setter, * and cannot be overridden * @param argnum */ public final Class getArgType( int argnum ) { if (argnum >= argtypes.length) { return argtypes[argtypes.length-1]; } else { return argtypes[argnum]; } } private String[] argnames; private Class[] argtypes; /** * Return variable names that should be set before this one. * The buffer is always set such that it tries to set all variables * at a given level before setting child values, but you could override * by using this. Its probably a bad idea to depend on children, though. * It is unnecessary to set parent vars as prerequisites, since they are * implicitly set first */ public String[] getPrerequisites() { return null; } /** * Prerequisites which should be set before this one if they exist */ public String[] getSoftPrerequisites() { return null; } /** * Variables are generally only allowed to be set once in a given file/cmdline. * It is sometimes useful to allow the same set multiple times in order to * aggregate values. * @return true if the setter can be called multiple times */ public boolean allowMultiple() { return false; } /** * Return an array of other names for this variable. */ public String[] getAliases() { return null; } /** * Override to make a variable hidden by default (i.e. you need -advanced on the cmdline) */ public boolean isAdvanced() { return false; } /** * Override to make a variable completely hidden */ public boolean isHidden() { return false; } /** * Override to prevent printing when dumping configuration */ public boolean isDisplayed() { return true; } /** * If a variable -must- be set, override this */ public boolean isRequired() { return false; } /** * Magic used by the command line configurator only at the moment to decide whether this * variable should eat all subsequent arguments. Useful for -help... */ public boolean isGreedy() { return false; } public boolean isPath() { return false; } public boolean doChecksum() { return true; } public CompilerMessage.CompilerWarning getDeprecatedMessage() { return null; } public boolean isDeprecated() { return false; } public String getDeprecatedReplacement() { return null; } public String getDeprecatedSince() { return null; } protected final void setSetterMethod( Method setter ) { Class[] pt = setter.getParameterTypes(); assert ( pt.length >= 2 ) : ( "coding error: config setter must take at least 2 args!" ); this.setter = setter; if (pt.length == 2) { Class c = pt[1]; if (ConfigurationBuffer.isSupportedListType( c )) { if (argcount == -2) argcount = -1; // infinite list argtypes = new Class[] {String.class}; return; } else if (ConfigurationBuffer.isSupportedValueType( c )) { assert ( argcount == -2 ) : ( "coding error: value object setter cannot override argcount" ); assert ( argnames == null) : ( "coding error: value object setter cannot override argnames" ); Field[] fields = c.getFields(); argcount = fields.length; assert ( argcount > 0 ) : ( "coding error: " + setter + " value object " + c.getName() + " must contain at least one public field" ); argnames = new String[fields.length]; argtypes = new Class[fields.length]; for (int f = 0; f < fields.length; ++f) { argnames[f] = ConfigurationBuffer.c2h( fields[f].getName() ); argtypes[f] = fields[f].getType(); } return; } } assert ( (argcount == -2) || (argcount == pt.length - 1) ) : ( "coding error: the argument count must match the number of setter arguments" ); // We've taken care of lists and value objects, from here on out, it must match the parameter list. argcount = pt.length - 1; argtypes = new Class[pt.length - 1]; for (int i = 1; i < pt.length; ++i) { assert ( ConfigurationBuffer.isSupportedSimpleType( pt[i] ) ) : ( "coding error: " + setter.getClass().getName() + "." + setter.getName() + " parameter " + i + " is not a supported type!" ); argtypes[i-1] = pt[i]; } } protected final Method getSetterMethod() { return setter; } private Method setter; private Method getter; protected final void setGetterMethod( Method getter ) { this.getter = getter; } protected final Method getGetterMethod() { return getter; } }