flex-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cd...@apache.org
Subject [27/51] [partial] - Migrated the directory structure to a more Maven style structure. - Started migrating the Parser from Antlr2+3 and JFlex to Antlr4.
Date Tue, 22 Jul 2014 13:35:53 GMT
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationBuffer.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationBuffer.java b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationBuffer.java
new file mode 100644
index 0000000..b6484fd
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationBuffer.java
@@ -0,0 +1,1346 @@
+/*
+ *
+ *  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 org.apache.flex.compiler.config;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.flex.compiler.exceptions.ConfigurationException;
+import org.apache.flex.compiler.internal.config.IConfigurationFilter;
+import org.apache.flex.compiler.internal.config.annotations.ArgumentNameGenerator;
+import org.apache.flex.compiler.internal.config.annotations.Arguments;
+import org.apache.flex.compiler.internal.config.annotations.Config;
+import org.apache.flex.compiler.internal.config.annotations.FlexOnly;
+import org.apache.flex.compiler.internal.config.annotations.InfiniteArguments;
+import org.apache.flex.compiler.internal.config.annotations.Mapping;
+import org.apache.flex.compiler.internal.config.annotations.SoftPrerequisites;
+import org.apache.flex.compiler.problems.ConfigurationProblem;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.utils.Trace;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * The basic idea here is to let you keep all your configuration knowledge in
+ * your configuration object, and to automate as much as possible. Reflection is
+ * used to convert public fields and setters on your configuration object into
+ * settable vars. There are a few key concepts:
+ * <p>
+ * - You should be able to configure absolutely any object.<br>
+ * - Child configuration variables in your config become a dotted hierarchy of
+ * varnames<br>
+ * - All sources of configuration data are buffered and merged (as string
+ * var/vals) before committing to the final configuration. This class acts as
+ * the buffer.<br>
+ * - Hyphenated variables (i.e. "some-var") are automatically configured by
+ * calling your matching setter (i.e. setSomeVar)<br>
+ * - Implementing an getSomeVarInfo() method on your class lets you set up more
+ * complicated config objects<br>
+ * - You can make variables depend on other variables having been set first.
+ * This lets you set a root directory in one var and then use its value in
+ * another.<br>
+ * - Per-variable validation can be performed in setters. Overall validation
+ * should take place as a post-process step.<br>
+ * - You can keep ConfigurationBuffers around and merge multiple buffers
+ * together before committing. Most recent definitions always win.<br>
+ * <p>
+ * The contract with your configuration class:
+ * <p>
+ * - You must provide a method with the signature
+ * "void setYourVar(ConfigurationValue val)" to set your config var. Your setter
+ * method should accept either a single arg of type List or String[], or else an
+ * arglist of simple types. For example
+ * "void myvar(int a, boolean b, String c")".<br>
+ * - You can implement a function with the signature "int yourvar_argcount()" to
+ * require a different number of arguments. This limit will be enforced by
+ * configurators (command line, file, etc.)<br>
+ * - If you provide a setter and explicit parameters (i.e. not List or String[])
+ * the number of arguments will be automatically determined.<br>
+ * - Each argument to your configuration variable is assumed to have a
+ * (potentially non-unique) name. The default is the simple type of the argument
+ * (boolean, int, string). If the var takes an undetermined number of args via
+ * List or String[], the argname defaults to string.<br>
+ * - You can implement a function with the signature
+ * "String yourvar_argnames(int)" to provide names for each of the parameters.
+ * The integer passed in is the argument number. Return the same name (i.e.
+ * "item") for infinite lists.<br>
+ * - You can implement a function with the signature "String[] yourvar_deps()"
+ * to provide a list of other prerequisites for this var. You will be guaranteed
+ * that the deps are committed before your var, or else a configurationexception
+ * will be thrown if a prerequsite was unset. (Note that infinite cycles are not
+ * checked, so be careful.)<br>
+ */
+public final class ConfigurationBuffer
+{
+    public ConfigurationBuffer(Class<? extends Configuration> configClass)
+    {
+        this(configClass, new HashMap<String, String>());
+    }
+
+    public ConfigurationBuffer(Class<? extends Configuration> configClass, Map<String, String> aliases)
+    {
+        this(configClass, aliases, null);
+    }
+
+    /**
+     * Create a configuration buffer with an optional filter. The filter can be
+     * used to remove unwanted options from a super class.
+     * 
+     * @param filter if null there is no filter, otherwise the set of
+     * configuration options is filtered.
+     */
+    public ConfigurationBuffer(Class<? extends Configuration> configClass, Map<String, String> aliases, IConfigurationFilter filter)
+    {
+        this.configClass = configClass;
+        this.varMap = new HashMap<String, List<ConfigurationValue>>();
+        this.committed = new HashSet<String>();
+
+        loadCache(configClass, filter);
+        assert (varCache.size() > 0) : "coding error: nothing was configurable in the provided object!";
+        for (Map.Entry<String, String> e : aliases.entrySet())
+        {
+            addAlias(e.getKey(), e.getValue());
+        }
+    }
+
+    public ConfigurationBuffer(ConfigurationBuffer copyFrom, boolean copyCommitted)
+    {
+        this.configClass = copyFrom.configClass;
+        this.varMap = new HashMap<String, List<ConfigurationValue>>(copyFrom.varMap);
+        this.committed = copyCommitted ? new HashSet<String>(copyFrom.committed) : new HashSet<String>();
+        this.varCache = copyFrom.varCache; // doesn't change after creation
+        this.varList = copyFrom.varList; // doesn't change after creation
+        this.tokens = new HashMap<String, String>(copyFrom.tokens);
+    }
+
+    public final List<String> dump()
+    {
+        final List<String> dump = new ArrayList<String>(varCache.size());
+        for (final Map.Entry<String, ConfigurationInfo> entry : varCache.entrySet())
+        {
+            dump.add(entry.getKey() + "," + entry.getValue().toString());
+        }
+        Collections.sort(dump);
+        return dump;
+    }
+
+    public void setVar(String var, String val, String source, int line) throws ConfigurationException
+    {
+        List<String> list = new LinkedList<String>();
+        list.add(val);
+        setVar(var, list, source, line, null, false);
+    }
+
+    public void setVar(String var, List<String> vals, String source, int line) throws ConfigurationException
+    {
+        setVar(var, vals, source, line, null, false);
+    }
+
+    public void setVar(String avar, List<String> vals, String source, int line, String contextPath, boolean append) throws ConfigurationException
+    {
+        String var = unalias(avar);
+        if (!isValidVar(var))
+            throw new ConfigurationException.UnknownVariable(var, source, line);
+
+        int argCount = getVarArgCount(var);
+
+        // -1 means unspecified length, its up to the receiving setter to validate.
+        if (argCount != -1)
+        {
+            addAnyDefaultArgValues(var, argCount, vals);
+
+            if (vals.size() != argCount)
+            {
+                throw new ConfigurationException.IncorrectArgumentCount(argCount, // expected
+                        vals.size(), //passed
+                var, source, line);
+            }
+        }
+
+        ConfigurationValue val = new ConfigurationValue(this, var,
+                                                         vals, //processValues( var, vals, source, line ),
+                                                         source, line, contextPath);
+        storeValue(var, val, append);
+        committed.remove(var);
+    }
+
+    public void clearVar(String avar, String source, int line) throws ConfigurationException
+    {
+        String var = unalias(avar);
+        if (!isValidVar(var))
+            throw new ConfigurationException.UnknownVariable(var, source, line);
+        varMap.remove(var);
+        committed.remove(var);
+    }
+
+    /**
+     * Remove the configuration values came from the given source.
+     * 
+     * @param source source name
+     * @see CommandLineConfigurator#SOURCE_COMMAND_LINE
+     */
+    public void clearSourceVars(String source)
+    {
+        List<String> remove = new LinkedList<String>();
+        for (Map.Entry<String, List<ConfigurationValue>> e : varMap.entrySet())
+        {
+            String var = e.getKey();
+            List<ConfigurationValue> vals = e.getValue();
+
+            List<ConfigurationValue> newvals = new LinkedList<ConfigurationValue>();
+            for (ConfigurationValue val : vals)
+            {
+                if (!val.getSource().equals(source))
+                {
+                    newvals.add(val);
+                }
+            }
+            if (newvals.size() > 0)
+                varMap.put(var, newvals);
+            else
+                remove.add(var);
+        }
+        for (Iterator<String> it = remove.iterator(); it.hasNext();)
+        {
+            varMap.remove(it.next());
+        }
+    }
+
+    public List<String> processValues(String var, List<String> args, String source, int line) throws ConfigurationException
+    {
+        List<String> newArgs = new LinkedList<String>();
+        for (Iterator<String> it = args.iterator(); it.hasNext();)
+        {
+            String arg = it.next();
+
+            int depth = 100;
+            while (depth-- > 0)
+            {
+                int o = arg.indexOf("${");
+                if (o == -1)
+                    break;
+
+                int c = arg.indexOf("}", o);
+
+                if (c == -1)
+                {
+                    throw new ConfigurationException.Token(ConfigurationException.Token.MISSING_DELIMITER,
+                                                           null, var, source, line);
+                }
+                String token = arg.substring(o + 2, c);
+                String value = getToken(token);
+
+                if (value == null)
+                {
+                    if (value == null)
+
+                    {
+                        throw new ConfigurationException.Token(ConfigurationException.Token.UNKNOWN_TOKEN,
+                                                                token, var, source, line);
+                    }
+
+                }
+                arg = arg.substring(0, o) + value + arg.substring(c + 1);
+
+            }
+            if (depth == 0)
+            {
+                throw new ConfigurationException.Token(ConfigurationException.Token.RECURSION_LIMIT,
+                                                        null, var, source, line);
+            }
+
+            newArgs.add(arg);
+        }
+        return newArgs;
+    }
+
+    public void setToken(String token, String value)
+    {
+        tokens.put(token, value);
+    }
+
+    public String getToken(String token)
+    {
+        if (tokens.containsKey(token))
+            return tokens.get(token);
+        else
+        {
+            try
+            {
+                return System.getProperty(token);
+            }
+            catch (SecurityException se)
+            {
+                return null;
+            }
+        }
+    }
+
+    private void storeValue(String avar, ConfigurationValue val, boolean append) throws ConfigurationException
+    {
+        String var = unalias(avar);
+        ConfigurationInfo info = getInfo(var);
+
+        List<ConfigurationValue> vals;
+        if (varMap.containsKey(var))
+        {
+            vals = varMap.get(var);
+            assert (vals.size() > 0);
+            ConfigurationValue first = vals.get(0);
+            if (!append && !first.getSource().equals(val.getSource()))
+                vals.clear();
+            else if (!info.allowMultiple())
+                throw new ConfigurationException.IllegalMultipleSet(
+                                                  var,
+                                                  val.getSource(), val.getLine());
+        }
+        else
+        {
+            vals = new LinkedList<ConfigurationValue>();
+            varMap.put(var, vals);
+        }
+        vals.add(val);
+    }
+
+    public List<ConfigurationValue> getVar(String avar)
+    {
+        String var = unalias(avar);
+        return varMap.get(var);
+    }
+
+    public Set<String> getVars()
+    {
+        return varCache.keySet();
+    }
+
+    public void merge(ConfigurationBuffer other)
+    {
+        assert (configClass == other.configClass);
+        varMap.putAll(other.varMap);
+        committed.addAll(other.committed);
+    }
+
+    private final Map<String, List<ConfigurationValue>> varMap; // list of vars that have been set
+    private final Set<String> committed; // set of vars committed to backing config
+    private final Class<? extends Configuration> configClass; // configuration class
+    private Map<String, ConfigurationInfo> varCache // info cache
+    = new HashMap<String, ConfigurationInfo>();
+    private List<String> requiredList = new LinkedList<String>(); // required vars
+    private List<String> varList = new LinkedList<String>(); // list of vars in order they should be set
+    private Map<String, String> aliases = new HashMap<String, String>(); // variable name aliases
+    private Map<String, String> tokens = new HashMap<String, String>(); // tokens for replacement
+    private List<Object[]> positions = new ArrayList<Object[]>();
+
+    private static final String SET_PREFIX = "cfg";
+    private static final String GET_PREFIX = "get";
+    private static final String INFO_SUFFIX = "Info";
+
+    //-----------------------------------------------
+    //
+
+    /**
+     * WORKAROUND FOR BUG CMP-396
+     * 
+     * <p>
+     * {@link #c2h(String)} generates option names based on cfgXXX names in
+     * {@code Configuration}. Since we collapsed all the sub-configurations into
+     * one class, there's no longer a "base name" like "compiler.*" or
+     * "compiler.fonts.*". In order to preserve the dotted naming convention, we
+     * need to know which "-" separated names are actually dotted names. The
+     * {@link #CONVERT_FROM} and {@link #CONVERT_TO} is an <b>ordered</b> lookup
+     * table for option group base names. It's order makes sure that the longest
+     * possible replacement is done.
+     */
+    private static final ImmutableList<String> CONVERT_FROM =
+            ImmutableList.of(
+                    "compiler-fonts-languages-",
+                    "compiler-fonts-",
+                    "compiler-namespaces-",
+                    "compiler-mxml-",
+                    "compiler-",
+                    "metadata-",
+                    "licenses-",
+                    "frames-",
+                    "runtime-shared-library-settings-");
+
+    private static final ImmutableList<String> CONVERT_TO =
+            ImmutableList.of(
+                    "compiler.fonts.languages.",
+                    "compiler.fonts.",
+                    "compiler.namespaces.",
+                    "compiler.mxml.",
+                    "compiler.",
+                    "metadata.",
+                    "licenses.",
+                    "frames.",
+                    "runtime-shared-library-settings.");
+
+    /**
+     * convert StudlyCaps or camelCase to hyphenated
+     * 
+     * @param camel someVar or SomeVar
+     * @return hyphen some-var
+     */
+    protected static String c2h(String camel)
+    {
+        StringBuilder b = new StringBuilder(camel.length() + 5);
+        for (int i = 0; i < camel.length(); ++i)
+        {
+            char c = camel.charAt(i);
+            if (Character.isUpperCase(c))
+            {
+                if (i != 0)
+                    b.append('-');
+                b.append(Character.toLowerCase(c));
+            }
+            else
+            {
+                b.append(camel.charAt(i));
+            }
+        }
+        final String combined = b.toString();
+
+        for (int i = 0; i < CONVERT_FROM.size(); i++)
+        {
+            if (combined.startsWith(CONVERT_FROM.get(i)))
+            {
+                return combined.replaceFirst(CONVERT_FROM.get(i), CONVERT_TO.get(i));
+            }
+        }
+        return combined;
+    }
+
+    /**
+     * convert hyphenated to StudlyCaps or camelCase
+     * 
+     * @param hyphenated some-var
+     * @return result
+     */
+    protected static String h2c(String hyphenated, boolean studly)
+    {
+        StringBuilder b = new StringBuilder(hyphenated.length());
+        boolean capNext = studly;
+        for (int i = 0; i < hyphenated.length(); ++i)
+        {
+            char c = hyphenated.charAt(i);
+            if (c == '-')
+                capNext = true;
+            else
+            {
+                b.append(capNext ? Character.toUpperCase(c) : c);
+                capNext = false;
+            }
+        }
+        return b.toString();
+    }
+
+    public static String varname(String membername, String basename)
+    {
+        return ((basename == null) ? membername : (basename + "." + membername));
+    }
+
+    private static ConfigurationInfo createInfo(Method setterMethod)
+    {
+        ConfigurationInfo info = null;
+
+        String infoMethodName = GET_PREFIX + setterMethod.getName().substring(SET_PREFIX.length()) + INFO_SUFFIX;
+        String getterMethodName = GET_PREFIX + setterMethod.getName().substring(SET_PREFIX.length());
+        @SuppressWarnings("unchecked")
+        Class<? extends Configuration> cfgClass = (Class<? extends Configuration>)setterMethod.getDeclaringClass();
+
+        Method infoMethod = null, getterMethod = null;
+        if (!setterMethod.isAnnotationPresent(Config.class))
+        {
+            try
+            {
+                infoMethod = cfgClass.getMethod(infoMethodName);
+    
+                if (!Modifier.isStatic(infoMethod.getModifiers()))
+                {
+                    assert false : ("coding error: " + cfgClass.getName() + "." + infoMethodName + " needs to be static!");
+                    infoMethod = null;
+                }
+    
+                info = (ConfigurationInfo)infoMethod.invoke(null, (Object[])null);
+    
+            }
+            catch (SecurityException e)
+            {
+                e.printStackTrace();
+            }
+            catch (NoSuchMethodException e)
+            {
+            }
+            catch (IllegalArgumentException e)
+            {
+                e.printStackTrace();
+            }
+            catch (IllegalAccessException e)
+            {
+                e.printStackTrace();
+            }
+            catch (InvocationTargetException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        if (info == null)
+            info = new ConfigurationInfo();
+
+        try
+        {
+            getterMethod = cfgClass.getMethod(getterMethodName, (Class[])null);
+        }
+        catch (SecurityException e)
+        {
+            e.printStackTrace();
+        }
+        catch (NoSuchMethodException e)
+        {
+        }
+        info.setSetterMethod(setterMethod);
+        info.setGetterMethod(getterMethod);
+
+        return info;
+    }
+
+    /**
+     * load - prefetch all the interesting names into a dictionary so that we
+     * can find them again more easily. At the end of this call, we will have a
+     * list of every variable and their associated method.
+     * 
+     * @param filter if null there is no filter, otherwise the set of
+     * configuration options is filtered.
+     */
+    private boolean loadCache(Class<? extends Configuration> cfg, IConfigurationFilter filter)
+    {
+        int count = 0;
+
+        // First, find all vars at this level.
+        for (final Method method : cfg.getMethods())
+        {
+            if (method.getName().startsWith(SET_PREFIX) ||
+                method.isAnnotationPresent(Config.class))
+            {
+                String configName = null;
+
+                final Class<?>[] pt = method.getParameterTypes();
+                assert pt.length > 1 : "Expected at least one parameters on setter.";
+
+                // Collect configuration info from getXXXInfo() static methods.
+                final ConfigurationInfo info = createInfo(method);
+
+                // Collect configuration info from annotations.
+                final Config config = method.getAnnotation(Config.class);
+                if (config != null)
+                {
+                    info.isAdvanced = config.advanced();
+                    info.isHidden = config.hidden();
+                    info.isRemoved = config.removed();
+                    info.allowMultiple = config.allowMultiple();
+                    info.isPath = config.isPath();
+                    info.isDisplayed = config.displayed();
+                    info.isCompcOnly = config.compcOnly();
+                    info.isRequired = config.isRequired();
+                    
+                    // Argument name generator class
+                    final ArgumentNameGenerator argumentNameGeneratorClass = 
+                        method.getAnnotation(ArgumentNameGenerator.class);
+                    if (argumentNameGeneratorClass != null)
+                    {
+                        info.argNameGeneratorClass = argumentNameGeneratorClass.value();
+                    }
+                    else
+                    {
+                        // Argument names
+                        final Arguments arguments = method.getAnnotation(Arguments.class);
+                        if (arguments != null)
+                            info.argnames = arguments.value();
+                    }
+                    
+                    // Argument count
+                    final InfiniteArguments infinite = method.getAnnotation(InfiniteArguments.class);
+                    if (infinite != null)
+                        info.argcount = ConfigurationInfo.INFINITE_ARGS;
+
+                    // Soft Prerequisites
+                    final SoftPrerequisites softPre = method.getAnnotation(SoftPrerequisites.class);
+                    if (softPre != null)
+                        info.softPrerequisites = softPre.value();
+                    
+                    // XML element name for configuration
+                    final Mapping mapping = method.getAnnotation(Mapping.class);
+                    if (mapping != null)
+                        configName = Joiner.on(".").skipNulls().join(mapping.value());
+                    
+                    // Is this a Flex only option?
+                    final FlexOnly flexOnly = method.getAnnotation(FlexOnly.class);
+                    if (flexOnly != null)
+                        info.isFlexOnly = true;
+                }
+
+                // Fall back to naming convention for configuration names.
+                if (configName == null)
+                    configName = c2h(method.getName().substring(SET_PREFIX.length()));
+
+                if( filter == null || filter.select(configName) )
+                {
+                    varCache.put(configName, info);
+                    varList.add(configName);
+                    if (info.isRequired())
+                    {
+                        requiredList.add(configName);
+                    }
+                    ++count;
+                }
+            }
+        }
+
+        assert (count > 0 || filter != null) : "coding error: config class " + cfg.getName() + " did not define any setters or child configs";
+        return (count > 0);
+    }
+
+    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();
+    }
+
+    public ConfigurationInfo getInfo(String avar)
+    {
+        String var = unalias(avar);
+        return varCache.get(var);
+    }
+
+    public String getVarArgName(String avar, int argnum)
+    {
+        String var = unalias(avar);
+        ConfigurationInfo info = getInfo(var);
+
+        if (info == null)
+        {
+            assert false : ("must call isValid to check vars!");
+        }
+
+        return info.getArgName(argnum);
+    }
+
+    public boolean isValidVar(String avar)
+    {
+        String var = unalias(avar);
+        ConfigurationInfo info = getInfo(var);
+        return (info != null);
+    }
+
+    public int getVarArgCount(String avar)
+    {
+        ConfigurationInfo info = getInfo(avar);
+        assert (info != null);
+        return info.getArgCount();
+    }
+
+    /**
+     * Add any default values to an argument, if the user did not specify them
+     * on the command line.
+     * 
+     * @param avar the argument variable
+     * @param argCount the number of argument values specified
+     * @param vals Values to add any default values to
+     */
+    private void addAnyDefaultArgValues(String avar, int argCount, List<String> vals)
+    {
+        ConfigurationInfo info = getInfo(avar);
+        final int missingArgsCount = argCount - vals.size();
+        if (missingArgsCount == 0 || info.getDefaultArgValues() == null)
+            return;
+
+        final String[] defaultArgValues = info.getDefaultArgValues();
+        final int defaultArgsCount = defaultArgValues.length;
+        final int defaultArgsStart = defaultArgsCount - missingArgsCount;
+        for (int i = defaultArgsStart; i < defaultArgsCount; i++)
+        {
+            vals.add(defaultArgValues[i]);
+        }
+    }
+
+    /**
+     * commit - bake the resolved map to the configuration
+     * 
+     * @param config The configuration to set the buffer variables into.
+     * @param problems A collection where configuration problems are reported.
+     * 
+     * @return true if successful, false otherwise.
+     */
+    public boolean commit(Object config, Collection<ICompilerProblem> problems)
+    {
+        assert (config.getClass() == configClass) : 
+            ("coding error: configuration " + config.getClass() + " != template " + configClass);
+        Set<String> done = new HashSet<String>();
+        boolean success = true;
+        
+        for (Iterator<String> vars = varList.iterator(); vars.hasNext();)
+        {
+            String var = vars.next();
+            if (varMap.containsKey(var))
+            {
+                try
+                {
+                    commitVariable(config, var, done);
+                }
+                catch (ConfigurationException e)
+                {
+                    problems.add(new ConfigurationProblem(e));
+                    success = false;
+                }
+            }
+        }
+
+        for (Iterator<String> reqs = requiredList.iterator(); reqs.hasNext();)
+        {
+            String req = reqs.next();
+
+            if (!committed.contains(req))
+            {
+                ConfigurationException e = new ConfigurationException.MissingRequirement(req, null, null, -1);
+                problems.add(new ConfigurationProblem(
+                        null,
+                        -1,
+                        -1,
+                        -1,
+                        -1,
+                        e.getMessage()));
+                success = false;
+            }
+        }
+        
+        return success;
+    }
+
+    /**
+     * commitVariable - copy a variable out of a state into the final config.
+     * This should only be called on variables that are known to exist in the
+     * state!
+     * 
+     * @param var variable name to lookup
+     * @param done set of variable names that have been completed so far (for
+     * recursion)
+     */
+    private void commitVariable(Object config, String var, Set<String> done) throws ConfigurationException
+    {
+        ConfigurationInfo info = getInfo(var);
+
+        setPrerequisites(info.getPrerequisites(), var, done, config, true);
+        setPrerequisites(info.getSoftPrerequisites(), var, done, config, false);
+
+        if (committed.contains(var))
+            return;
+
+        committed.add(var);
+        done.add(var);
+
+        assert (varMap.containsKey(var));
+        List<ConfigurationValue> vals = varMap.get(var);
+
+        if (vals.size() > 1)
+        {
+            assert (info.allowMultiple()); // assumed to have been previously checked
+        }
+        for (ConfigurationValue val : vals)
+        {
+            try
+            {
+                Object[] args = buildArgList(info, val);
+
+                info.getSetterMethod().invoke(config, args);
+            }
+            catch (Exception e)
+            {
+                Throwable t = e;
+
+                if (e instanceof InvocationTargetException)
+                {
+                    t = ((InvocationTargetException)e).getTargetException();
+                }
+
+                if (Trace.error)
+                    t.printStackTrace();
+
+                if (t instanceof ConfigurationException)
+                {
+                    throw (ConfigurationException)t;
+                }
+                else
+                {
+                    throw new ConfigurationException.OtherThrowable(t, var, val.getSource(), val.getLine());
+                }
+            }
+        }
+
+    }
+
+    private void setPrerequisites(String[] prerequisites, String var, Set<String> done, Object config, boolean required)
+            throws ConfigurationException
+    {
+        if (prerequisites != null)
+        {
+            for (int p = 0; p < prerequisites.length; ++p)
+            {
+                String depvar = prerequisites[p];
+
+                // Dependencies can only go downward.
+                int dot = var.lastIndexOf('.');
+
+                if (dot >= 0)
+                {
+                    String car = var.substring(0, dot);
+                    //String cdr = var.substring( dot + 1 );
+
+                    String newDepvar = car + "." + depvar;
+
+                    // Since in falcon we have collapsed sub-configurations into one
+                    // configuration, some options that were in sub-configurations now
+                    // have prerequisites on options in the same configuration. We
+                    // need to keep the old configuration mappings so old configurations
+                    // options will still work. So a simple thing we can do is if the 
+                    // dependency variable is invalid (presumably because the 
+                    // dependency is really on a parent configuration option), 
+                    // then use the dependency as is depvar) and see if it is 
+                    // valid. If depvar ends up not being valid then set depvar
+                    // to newDepvar so error reporting isn't changed by the new
+                    // fall-back behavior.
+                    if (isValidVar(newDepvar) || !isValidVar(depvar))
+                        depvar = newDepvar;
+
+                }
+
+                if (!done.contains(depvar))
+                {
+                    if (!isValidVar(depvar))
+                    {
+                        assert false : ("invalid " + var + " dependency " + depvar);
+                        continue;
+                    }
+                    if (varMap.containsKey(depvar))
+                    {
+                        commitVariable(config, depvar, done);
+                    }
+                    else if (required && !committed.contains(depvar))
+                    {
+                        // TODO - can we get source/line for this?
+                        throw new ConfigurationException.MissingRequirement(depvar, var, null, -1);
+                    }
+                }
+            }
+        }
+    }
+
+    private String[] constructStringArray(List<String> args)
+    {
+        String[] sa = new String[args.size()];
+
+        int i = 0;
+        for (Iterator<String> it = args.iterator(); it.hasNext();)
+            sa[i++] = it.next();
+
+        return sa;
+    }
+
+    private Object constructValueObject(ConfigurationInfo info, ConfigurationValue cv) throws ConfigurationException
+    {
+        try
+        {
+            Class<?>[] pt = info.getSetterMethod().getParameterTypes();
+            assert (pt.length == 2); // assumed to be checked upstream
+
+            Object o = pt[1].newInstance();
+
+            Field[] fields = pt[1].getFields();
+
+            assert (fields.length == cv.getArgs().size()); // assumed to be checked upstream
+
+            Iterator<String> argsit = cv.getArgs().iterator();
+            for (int f = 0; f < fields.length; ++f)
+            {
+                String val = (String)argsit.next();
+                Object valobj = null;
+                Class<?> fc = fields[f].getType();
+
+                assert (info.getArgType(f) == fc);
+                assert (info.getArgName(f).equals(ConfigurationBuffer.c2h(fields[f].getName())));
+
+                if (fc == String.class)
+                {
+                    valobj = val;
+                }
+                else if ((fc == Boolean.class) || (fc == boolean.class))
+                {
+                    // TODO - Boolean.valueOf is pretty lax.  Maybe we should restrict to true/false?
+                    valobj = Boolean.valueOf(val);
+                }
+                else if ((fc == Integer.class) || (fc == int.class))
+                {
+                    valobj = Integer.decode(val);
+                }
+                else if ((fc == Long.class) || (fc == long.class))
+                {
+                    valobj = Long.decode(val);
+                }
+                else
+                {
+                    assert false; // should have checked any other condition upstream!
+                }
+                fields[f].set(o, valobj);
+            }
+
+            return o;
+        }
+        catch (InstantiationException e)
+        {
+            assert false : ("coding error: unable to instantiate value object when trying to set var " + cv.getVar());
+            throw new ConfigurationException.OtherThrowable(e, cv.getVar(), cv.getSource(), cv.getLine());
+
+        }
+        catch (IllegalAccessException e)
+        {
+            assert false : ("coding error: " + e + " when trying to set var " + cv.getVar());
+            throw new ConfigurationException.OtherThrowable(e, cv.getVar(), cv.getSource(), cv.getLine());
+        }
+    }
+
+    protected static boolean isSupportedSimpleType(Class<?> c)
+    {
+        return ((c == String.class)
+                || (c == Integer.class) || (c == int.class)
+                || (c == Long.class) || (c == long.class)
+                || (c == Boolean.class) || (c == boolean.class));
+    }
+
+    protected static boolean isSupportedListType(Class<?> c)
+    {
+        return ((c == List.class) || (c == String[].class));
+    }
+
+    protected static boolean isSupportedValueType(Class<?> c)
+    {
+        if (isSupportedSimpleType(c))
+            return false;
+
+        Field[] fields = c.getFields();
+
+        for (int f = 0; f < fields.length; ++f)
+        {
+            if (!isSupportedSimpleType(fields[f].getType()))
+                return false;
+        }
+        return true;
+    }
+
+    private Object[] buildArgList(ConfigurationInfo info, ConfigurationValue val) throws ConfigurationException
+    {
+        Method setter = info.getSetterMethod();
+
+        Class<?>[] pt = setter.getParameterTypes();
+
+        List<String> args = processValues(val.getVar(), val.getArgs(), val.getSource(), val.getLine());
+
+        if (info.getArgCount() == -1)
+        {
+            if (pt.length != 2)
+            {
+                assert false : ("coding error: unlimited length setter " + val.getVar() + " must take a single argument of type List or String[]");
+                return null;
+            }
+            else if (List.class.isAssignableFrom(pt[1]))
+            {
+                return new Object[] {val, args};
+            }
+            else if (String[].class.isAssignableFrom(pt[1]))
+            {
+                return new Object[] {val, constructStringArray(args)};
+            }
+            else
+            {
+                assert false : ("coding error: unlimited length setter " + val.getVar() + " must take a single argument of type List or String[]");
+                return null;
+            }
+        }
+        else
+        {
+            assert (pt.length > 1) : ("coding error: config setter " + val.getVar() + " must accept at least one argument");
+            // ok, we first check to see if the signature of their setter accepts a list.
+
+            if (pt.length == 2)
+            {
+                // a variety of specialty setters here...
+
+                if (List.class.isAssignableFrom(pt[1]))
+                {
+                    return new Object[] {val, args};
+                }
+                else if (String[].class == pt[1])
+                {
+                    return new Object[] {val, constructStringArray(args)};
+                }
+                else if (isSupportedValueType(pt[1]))
+                {
+                    return new Object[] {val, constructValueObject(info, val)};
+                }
+            }
+
+            // otherwise, they must have a matching size parm list as the number of args passed in.
+
+            assert (pt.length == (args.size() + 1)) : ("coding error: config setter " + val.getVar() + " does not have " + args.size() + " parameters!");
+
+            Object[] pa = new Object[pt.length];
+
+            pa[0] = val;
+
+            for (int p = 1; p < pt.length; ++p)
+            {
+                String arg = args.get(p - 1);
+                if (pt[p].isAssignableFrom(String.class))
+                {
+                    pa[p] = arg;
+                }
+                else if ((pt[p] == int.class) || (pt[p] == Integer.class))
+                {
+                    try
+                    {
+                        pa[p] = Integer.decode(arg);
+
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ConfigurationException.TypeMismatch(ConfigurationException.TypeMismatch.INTEGER,
+                                                                       arg, val.getVar(), val.getSource(), val.getLine());
+                    }
+                }
+                else if ((pt[p] == long.class) || (pt[p] == Long.class))
+                {
+                    try
+                    {
+                        pa[p] = Long.decode(arg);
+
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ConfigurationException.TypeMismatch(
+                                ConfigurationException.TypeMismatch.LONG,
+                                arg, val.getVar(), val.getSource(), val.getLine());
+                    }
+                }
+                else if ((pt[p] == boolean.class) || (pt[p] == Boolean.class))
+                {
+                    try
+                    {
+                        arg = arg.trim().toLowerCase();
+                        if (arg.equals("true") || arg.equals("false"))
+                        {
+                            pa[p] = Boolean.valueOf(arg);
+                        }
+                        else
+                        {
+                            throw new ConfigurationException.TypeMismatch(
+                                    ConfigurationException.TypeMismatch.BOOLEAN, arg, val.getVar(), val.getSource(), val.getLine());
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ConfigurationException.TypeMismatch(
+                                ConfigurationException.TypeMismatch.BOOLEAN, arg, val.getVar(), val.getSource(), val.getLine());
+                    }
+                }
+                else
+                {
+                    assert false : ("coding error: " + val.getVar() + " setter argument " + p + " is not a supported type");
+                }
+            }
+
+            return pa;
+        }
+    }
+
+    public void addAlias(String alias, String var)
+    {
+        if (!isValidVar(var))
+        {
+            assert false : ("coding error: can't bind alias " + alias + " to nonexistent var " + var);
+            return;
+        }
+        if (aliases.containsKey(alias))
+        {
+            assert false : ("coding error: alias " + alias + " already defined as " + aliases.get(alias));
+            return;
+        }
+        if (varCache.containsKey(alias))
+        {
+            assert false : ("coding error: can't define alias " + alias + ", it already exists as a var");
+            return;
+        }
+
+        aliases.put(alias, var);
+    }
+
+    public Map<String, String> getAliases()
+    {
+        return aliases;
+    }
+
+    public String unalias(String var)
+    {
+        String realvar = aliases.get(var);
+        return (realvar == null) ? var : realvar;
+    }
+
+    public String peekSimpleConfigurationVar(String avar) throws ConfigurationException
+    {
+        String val = null;
+        List<ConfigurationValue> valList = getVar(avar);
+        if (valList != null)
+        {
+            ConfigurationValue cv = (ConfigurationValue)valList.get(0);
+            List<String> args = processValues(avar, cv.getArgs(), cv.getSource(), cv.getLine());
+            val = args.get(0);
+        }
+        return val;
+    }
+
+    public List<ConfigurationValue> peekConfigurationVar(String avar) throws ConfigurationException
+    {
+        List<ConfigurationValue> srcList = getVar(avar);
+        if (srcList == null)
+            return null;
+
+        List<ConfigurationValue> dstList = new LinkedList<ConfigurationValue>();
+        for (ConfigurationValue srcVal : srcList)
+        {
+            List<String> args = processValues(avar, srcVal.getArgs(), srcVal.getSource(), srcVal.getLine());
+
+            ConfigurationValue dstVal = new ConfigurationValue(srcVal.getBuffer(), avar, args, srcVal.getSource(), srcVal.getLine(), srcVal.getContext());
+            dstList.add(dstVal);
+        }
+        return dstList;
+    }
+
+    public void addPosition(String var, int iStart, int iEnd)
+    {
+        positions.add(new Object[] {var, new Integer(iStart), new Integer(iEnd)});
+    }
+
+    public List<Object[]> getPositions()
+    {
+        return positions;
+    }
+
+    public static List<String> formatText(String input, int columns)
+    {
+        ArrayList<String> lines = new ArrayList<String>();
+
+        if ((input == null) || (input.length() == 0))
+            return lines;
+
+        int current = 0;
+        int lineStart = -1;
+        int lineEnd = -1;
+        int wordStart = -1;
+        int wordEnd = -1;
+        boolean start = true;
+        boolean preserve = true;
+
+        while (true)
+        {
+            if (current < input.length())
+            {
+                boolean newline = input.charAt(current) == '\n';
+                boolean printable = (preserve && !newline) || !Character.isWhitespace(input.charAt(current));
+
+                if (start) // find a word
+                {
+                    if (printable)
+                    {
+                        if (lineStart == -1)
+                        {
+                            lineStart = current;
+                        }
+                        wordStart = current;
+                        start = false;
+                    }
+                    else
+                    {
+                        if (newline && lineStart != -1)
+                        {
+                            lines.add(input.substring(lineStart, current));
+                            lineStart = -1;
+                        }
+                        else if (newline)
+                        {
+                            lines.add("");
+                        }
+                        ++current;
+                    }
+                }
+                else
+                // have a word
+                {
+                    preserve = false;
+                    if (printable)
+                    {
+                        ++current;
+                    }
+                    else
+                    {
+                        wordEnd = current;
+                        if (lineEnd == -1)
+                        {
+                            lineEnd = current;
+                        }
+
+                        // two possibilities; if the new word fits in the current line length
+                        // without being too many columns, leave on current line.
+                        // otherwise, set it as the start of a new line.
+
+                        if (wordEnd - lineStart < columns)
+                        {
+                            if (newline)
+                            {
+                                lines.add(input.substring(lineStart, current));
+                                lineStart = -1;
+                                lineEnd = -1;
+                                wordStart = -1;
+                                start = true;
+                                preserve = true;
+                                ++current;
+                            }
+                            else
+                            {
+                                // we have room to add the current word to this line, find new word
+                                start = true;
+                                lineEnd = current;
+                            }
+                        }
+                        else
+                        {
+                            // current word pushes things beyond the requested column limit,
+                            // dump current text
+                            lines.add(input.substring(lineStart, lineEnd));
+                            lineStart = wordStart;
+                            lineEnd = -1;
+                            wordStart = -1;
+                            start = true;
+                            if (newline)
+                                preserve = true;
+                        }
+                    }
+                }
+            }
+            else
+            // we're done
+            {
+                // a) no line yet, so don't do anything
+                // b) have line and new word would push over edge, need two lines
+                // c) have line and current word fits, need one line
+                // d) only one word and its too long anyway, need one line
+
+                if (lineStart != -1) // we have a line in progress
+                {
+                    wordEnd = current;
+                    if (lineEnd == -1)
+                        lineEnd = current;
+
+                    if (((wordEnd - lineStart) < columns) // current word fits
+                        || (wordEnd == lineEnd)) // or one long word
+                    {
+                        lineEnd = wordEnd;
+                        lines.add(input.substring(lineStart, wordEnd));
+                    }
+                    else
+                    // didn't fit, multiple words
+                    {
+                        lines.add(input.substring(lineStart, lineEnd));
+                        lines.add(input.substring(wordStart, wordEnd));
+                    }
+                }
+                break;
+            }
+        }
+        return lines;
+    }
+
+    /**
+     * For debugging only.
+     * <p>
+     * Produces an alphabetized list of this buffer's configuration options and their values.
+     * An option such as
+     * <pre>
+     * -foo=aaa,bbb -foo+=ccc
+     * </pre>
+     * will appear as
+     * <pre>
+     * foo=aaa,bbb;ccc
+     * </pre>
+     */
+    @Override
+    public String toString()
+    {
+        StringBuffer sb = new StringBuffer();
+        
+        String[] variables = varMap.keySet().toArray(new String[0]);
+        Arrays.sort(variables);
+        
+        for (String var : variables)
+        {
+            sb.append(var);
+            sb.append("=");
+
+            ArrayList<String> commaSeparatedValues = new ArrayList<String>();
+            for (ConfigurationValue cv : varMap.get(var))
+            {
+                List<String> args = cv.getArgs();
+                String joinedArgs = Joiner.on(',').join(args);
+                commaSeparatedValues.add(joinedArgs);
+            }
+            String rhs = Joiner.on(';').join(commaSeparatedValues);
+            sb.append(rhs);
+            
+            sb.append('\n');
+        }
+
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationInfo.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationInfo.java b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationInfo.java
new file mode 100644
index 0000000..949814d
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationInfo.java
@@ -0,0 +1,473 @@
+/*
+ *
+ *  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 org.apache.flex.compiler.config;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.apache.flex.compiler.internal.config.annotations.DefaultArgumentValue;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+
+/**
+ * Meta information for each configuration options. It is created by
+ * {@link ConfigurationBuffer#loadCache} from either annotations or
+ * {@code public static ConfigurationInfo getFooInfo();} methods in
+ * {@link Configuration} class.
+ */
+public class ConfigurationInfo
+{
+    public static final int NOT_SET = -2;
+    public static final int INFINITE_ARGS = -1;
+
+    /**
+     * 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 = NOT_SET;
+        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;
+    }
+
+    protected int argcount = NOT_SET;
+
+    protected String[] defaultArgValues = null;
+
+    /**
+     * Get any default values for an argument
+     * 
+     * @return an array of default argument values.  May be null
+     */
+    public final String[] getDefaultArgValues()
+    {
+        return defaultArgValues;
+    }
+
+    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 The argument number.
+     * @return name of argument
+     */
+    public String getArgName(int argnum)
+    {
+        if (argNameGeneratorClass != null)
+        {
+            Method getArgNameMethod;
+            try
+            {
+                getArgNameMethod = argNameGeneratorClass.getMethod("getArgumentName", int.class);
+                return (String)getArgNameMethod.invoke(null, argnum);
+            }
+            catch (Exception e)
+            {
+                // TODO: connect these exception to our problem logging subsystem.
+                e.printStackTrace();
+            }
+
+            return "";
+        }
+
+        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 The argument number.
+     */
+    public final Class<?> getArgType(int argnum)
+    {
+        if (argnum >= argtypes.length)
+        {
+            return argtypes[argtypes.length - 1];
+        }
+        else
+        {
+            return argtypes[argnum];
+        }
+    }
+
+    protected Class<?> argNameGeneratorClass;
+    protected String[] argnames;
+    protected Class<?>[] argtypes;
+
+    protected String[] prerequisites = null;
+
+    /**
+     * 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 prerequisites;
+    }
+
+    protected String[] softPrerequisites = null;
+
+    /**
+     * Prerequisites which should be set before this one if they exist
+     */
+    public String[] getSoftPrerequisites()
+    {
+        return softPrerequisites;
+    }
+
+    protected boolean allowMultiple = false;
+
+    /**
+     * 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 allowMultiple;
+    }
+
+    protected String[] aliases = null;
+
+    /**
+     * Return an array of other names for this variable.
+     */
+    public String[] getAliases()
+    {
+        return aliases;
+    }
+
+    protected boolean isAdvanced = false;
+
+    /**
+     * Override to make a variable hidden by default (i.e. you need -advanced on
+     * the cmdline)
+     */
+    public boolean isAdvanced()
+    {
+        return isAdvanced;
+    }
+
+    protected boolean isHidden = false;
+
+    /**
+     * Override to make a variable completely hidden
+     */
+    public boolean isHidden()
+    {
+        return isHidden;
+    }
+
+    protected boolean isDisplayed = true;
+
+    /**
+     * Override to prevent printing when dumping configuration
+     */
+    public boolean isDisplayed()
+    {
+        return isDisplayed;
+    }
+
+    /**
+     * If a variable -must- be set, override this
+     */
+    public boolean isRequired()
+    {
+        return isRequired;
+    }
+
+    protected boolean isRequired = 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 isGreedy;
+    }
+
+    protected boolean isGreedy = false;
+
+    public boolean isPath()
+    {
+        return isPath;
+    }
+
+    protected boolean isPath = false;
+
+    public boolean doChecksum()
+    {
+        return true;
+    }
+
+    public String getDeprecatedMessage()
+    {
+        return deprecatedMessage;
+    }
+
+    protected String deprecatedMessage = null;
+
+    public boolean isDeprecated()
+    {
+        return isDeprecated;
+    }
+
+    protected boolean isDeprecated = false;
+
+    public String getDeprecatedReplacement()
+    {
+        return deprecatedReplacement;
+    }
+
+    protected String deprecatedReplacement;
+
+    public String getDeprecatedSince()
+    {
+        return deprecatedSince;
+    }
+
+    protected String deprecatedSince;
+    
+    /**
+     * @return True indicates that the option is no longer 
+     * supported and will not have any affect.
+     */
+    public boolean isRemoved() 
+    {
+        return isRemoved;
+    }
+
+    protected boolean isRemoved = false;
+    
+    /**
+     * @return True the option requires Flex in order to be useful. 
+     */
+    public boolean isFlexOnly() 
+    {
+        return isFlexOnly;
+    }
+
+    protected boolean isFlexOnly = false;
+    
+    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 == NOT_SET)
+                    argcount = -1; // infinite list
+
+                argtypes = new Class[] {String.class};
+                return;
+            }
+            else if (ConfigurationBuffer.isSupportedValueType(c))
+            {
+                assert (argcount == NOT_SET) : ("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 == NOT_SET) || (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;
+
+        DefaultArgumentValue defaultArgValuesAnno = setter.getAnnotation(DefaultArgumentValue.class);
+        if (defaultArgValuesAnno != null)
+            defaultArgValues = defaultArgValuesAnno.value();
+
+        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;
+    }
+
+    @Override
+    public String toString()
+    {
+        return Objects.toStringHelper("")
+                .add("alias", arrayAsString(getAliases()))
+                .add("argcount", getArgCount())
+                .add("argnames", arrayAsString(argnames))
+                .add("argtypes", arrayAsString(argtypes))
+                .add("deprecated", isDeprecated())
+                .add("deprecatedMessage", getDeprecatedMessage())
+                .add("deprecatedReplacement", getDeprecatedReplacement())
+                .add("deprecatedSince", getDeprecatedSince())
+                .add("getter", getGetterMethod() == null ? "null" : getGetterMethod().getName())
+                .add("setter", getSetterMethod() == null ? "null" : getSetterMethod().getName())
+                .add("required", isRequired())
+                .add("Prerequisites", arrayAsString(getPrerequisites()))
+                .add("softPrerequisites", arrayAsString(getSoftPrerequisites()))
+                .add("advanced", isAdvanced())
+                .add("allow multiple", allowMultiple())
+                //.add("doChecksum", doChecksum())
+                .add("displayed", isDisplayed())
+                .add("greedy", isGreedy())
+                .add("hidden", isHidden())
+                .add("removed", isRemoved())
+                .add("path", isPath())
+                .toString();
+    }
+
+    private String arrayAsString(Object[] array)
+    {
+        if (array == null)
+            return "";
+        else
+            return "[" + Joiner.on(",").join(array) + "]";
+    }
+
+    /**
+     * True if only {@code compc} client can use this option.
+     */
+    public boolean isCompcOnly = false;
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationPathResolver.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationPathResolver.java b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationPathResolver.java
new file mode 100644
index 0000000..f8f1c09
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationPathResolver.java
@@ -0,0 +1,80 @@
+/*
+ *
+ *  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 org.apache.flex.compiler.config;
+
+import java.io.File;
+
+import org.apache.flex.compiler.common.IPathResolver;
+import org.apache.flex.utils.FilenameNormalization;
+
+/**
+ * A path resolver for configuration options.
+ * 
+ * Paths in configuration options will be resolved as follows:
+ * 
+ *  1. Paths in configuration files will be resolved relative to 
+ *  the configuration file.
+ *  2. Paths in the command line will be resolved relative to the
+ *  where the compiler was launched.
+ */
+public class ConfigurationPathResolver implements IPathResolver
+{
+    /**
+     * Constructor.
+     * 
+     * @param rootDirectory the root directory of compilation. May
+     * not be null.
+     * 
+     * @throws NullPointerException if rootDirectory is null.
+     */
+    public ConfigurationPathResolver(String rootDirectory)
+    {
+        this.rootDirectory = rootDirectory;
+        
+        if (rootDirectory == null)
+            throw new NullPointerException("rootDirectory may not be null");
+        
+    }
+
+    private String rootDirectory;
+    
+    @Override
+    public File resolve(String path)
+    {
+        return tryResolve(rootDirectory, path);
+    }
+    
+    /**
+     * Try to resolve a file in the given folder.
+     * 
+     * @param parent parent folder
+     * @param name file name
+     * @return File object that may or may not exist.
+     */
+    private File tryResolve(final String parent, final String name)
+    {
+        final File file = new File(name);
+        if (file.isAbsolute())
+            return FilenameNormalization.normalize(file);
+
+        return FilenameNormalization.normalize(new File(parent, name));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationValue.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationValue.java b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationValue.java
new file mode 100644
index 0000000..5836f5c
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/config/ConfigurationValue.java
@@ -0,0 +1,109 @@
+/*
+ *
+ *  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 org.apache.flex.compiler.config;
+
+import java.util.List;
+import java.util.LinkedList;
+
+
+/**
+ * This class represents an instance of a configuration option.  For
+ * example, "-debug=true".
+ */
+public class ConfigurationValue
+{
+    protected ConfigurationValue( ConfigurationBuffer buffer, String var, List<String> args, String source, int line, String context )
+    {
+        this.buffer = buffer;
+        this.var = var;
+        this.args = new LinkedList<String>( args );
+        this.source = source;
+        this.line = line;
+        this.context = context;
+    }
+
+    /**
+     * getArgs
+     *
+     * @return list of values provided, in schema order
+     */
+    public final List<String> getArgs()
+    {
+        return args;
+    }
+
+    /**
+     * getBuffer
+     *
+     * @return a handle to the associated buffer holding this value
+     */
+    public final ConfigurationBuffer getBuffer()
+    {
+        return buffer;
+    }
+
+    /**
+     * getSource
+     *
+     * @return a string representing the origin of this value, or null if unknown
+     */
+    public final String getSource()
+    {
+        return source;
+    }
+
+    /**
+     * getLine
+     *
+     * @return the line number of the origin of this value, or -1 if unknown
+     */
+    public final int getLine()
+    {
+        return line;
+    }
+
+    /**
+     * getVar
+     *
+     * @return the full name of this configuration variable in the hierarchy
+     */
+    public final String getVar()
+    {
+        return var;
+    }
+
+    /**
+     * getContext
+     *
+     * @return the path of the enclosing context where the variable was set
+     * (i.e. the directory where the config file was found)
+     */
+    public final String getContext()
+    {
+        return context;
+    }
+
+    private final ConfigurationBuffer buffer;
+    private final String var;
+    private final List<String> args;
+    private final String source;
+    private final int line;
+    private final String context;
+}


Mime
View raw message