Package org.apache.flex.compiler.config

Source Code of org.apache.flex.compiler.config.Configuration

/*
*
*  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 java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import org.apache.flex.compiler.common.IPathResolver;
import org.apache.flex.compiler.common.VersionInfo;
import org.apache.flex.compiler.exceptions.ConfigurationException;
import org.apache.flex.compiler.exceptions.ConfigurationException.CannotOpen;
import org.apache.flex.compiler.exceptions.ConfigurationException.IncorrectArgumentCount;
import org.apache.flex.compiler.exceptions.ConfigurationException.NotAFile;
import org.apache.flex.compiler.exceptions.ConfigurationException.NotDirectory;
import org.apache.flex.compiler.exceptions.ConfigurationException.RedundantFile;
import org.apache.flex.compiler.filespecs.FileSpecification;
import org.apache.flex.compiler.filespecs.IFileSpecification;
import org.apache.flex.compiler.internal.config.FileConfigurator;
import org.apache.flex.compiler.internal.config.FrameInfo;
import org.apache.flex.compiler.internal.config.LoadExternsParser;
import org.apache.flex.compiler.internal.config.QNameNormalization;
import org.apache.flex.compiler.internal.config.RSLArgumentNameGenerator;
import org.apache.flex.compiler.internal.config.RuntimeSharedLibraryPathInfo;
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.DefaultArgumentValue;
import org.apache.flex.compiler.internal.config.annotations.DeprecatedConfig;
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.internal.config.localization.LocalizationManager;
import org.apache.flex.compiler.internal.mxml.MXMLNamespaceMapping;
import org.apache.flex.compiler.mxml.IMXMLTypeConstants;
import org.apache.flex.compiler.problems.ConfigurationProblem;
import org.apache.flex.compiler.problems.DeprecatedConfigurationOptionProblem;
import org.apache.flex.compiler.problems.FlexOnlyConfigurationOptionNotSupported;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.problems.RemovedConfigurationOptionProblem;
import org.apache.flex.swc.catalog.XMLFormatter;
import org.apache.flex.utils.FileUtils;
import org.apache.flex.utils.FilenameNormalization;

import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

/**
* The model for all the configuration options supported by the compiler. Each
* option is stored in a private field. It usually has a getter method, a setter
* method and a configuration method (started with "cfg"). The configuration
* methods are used by the configurators to set the values of a given option
* name.
* <p>
* This class is currently being reviewed and refactored. See CMP-471 as the
* master bug for the tasks.
* <p>
* Steps to refactor:
* <ol>
* <li>Check "mxmlc" manual (http://adobe.ly/bffN8A) and copy documents to
* Javadoc on setters of options.</li>
* <li>Check "configuration support" WIKI (http://bit.ly/oAI8gj) to see if an
* option should be deprecated.</li>
* <li>Merge {@code cfgXXX} to {@code setXXX}, and annotate the setter.</li>
* <li>Remove {@code getXXXInfo()} static method.</li>
* </ol>
*
* @see "http://bugs.adobe.com/jira/browse/CMP-471"
*/
public class Configuration
{
    private static final int DEFAULT_HEIGHT_MAX = 4096;
    private static final int DEFAULT_HEIGHT_MIN = 1;
    private static final int DEFAULT_WIDTH_MAX = 4096;
    private static final int DEFAULT_WIDTH_MIN = 1;

    public static final String DEFAULT_OUTPUT_DIRECTORY_TOKEN = "org.apache.flex.default.output.directory";
   
    public static final String SWC_AIRGLOBAL = "airglobal.swc";
   
    /**
     * Singleton empty string list. All getters returning a list of string
     * should return this object when the field is null.
     */
    private static final List<String> EMPTY_STRING_LIST = Collections.emptyList();

    private static final List<String> compcOnlyOptions = new ArrayList<String>(9);
   
    static
    {
        compcOnlyOptions.add("directory");
        compcOnlyOptions.add("include-classes");
        compcOnlyOptions.add("include-file");
        compcOnlyOptions.add("include-lookup-only");
        compcOnlyOptions.add("include-namespaces");
        compcOnlyOptions.add("include-sources");
        compcOnlyOptions.add("include-stylesheet");
        compcOnlyOptions.add("include-inheritance-dependencies-only");
    }
   
    /**
     * Validate configuration options values.
     *
     * @param configurationBuffer Configuration buffer.
     * @throws ConfigurationException Error.
     */
    public void validate(ConfigurationBuffer configurationBuffer) throws ConfigurationException
    {
        // process the merged configuration buffer. right, can't just process the args.
        processDeprecatedAndRemovedOptions(configurationBuffer);

        validateDumpConfig(configurationBuffer);
    }
   
    /**
     * Validate that no compc-only options are used in a given configuration buffer.
     * Use this method to verify no compc-only args are used in mxmlc.
     *
     * @param configurationBuffer the configuration buffer to check for compc-only options.
     *
     * @throws ConfigurationException if a compc-only option is found in the configuration buffer.
     */
    public static void validateNoCompcOnlyOptions(ConfigurationBuffer configurationBuffer) throws ConfigurationException
    {
        for (String option : compcOnlyOptions)
        {
            List<ConfigurationValue> values = configurationBuffer.getVar(option);
            if (values != null && values.size() > 0)
                throw new ConfigurationException.UnknownVariable(values.get(0).getVar(),
                        values.get(0).getSource(), values.get(0).getLine());
        }
    }
   
    /**
     * The path of a given file name based on the context of the configuration
     * value or the default output directory token.
     *
     * @param cv
     * @param fileName
     * @return the full path of the file.
     */
    protected String getOutputPath(ConfigurationValue cv, String fileName)
    {
        String result = fileName;

        if (fileName != null)
        {
            File file = new File(fileName);
            if (!FileUtils.isAbsolute(file))
            {
                String directory = cv.getBuffer().getToken(DEFAULT_OUTPUT_DIRECTORY_TOKEN);

                // if no default output directory, then use the configuration context.
                if (directory == null)
                {
                    directory = cv.getContext();
                }

                if (directory != null)
                {
                    result = FileUtils.addPathComponents(directory, fileName, File.separatorChar);
                }
            }
        }

        return pathResolver.resolve(result).getAbsolutePath();
    }

    private static Map<String, String> aliases = null;

    public static Map<String, String> getAliases()
    {
        if (aliases == null)
        {
            aliases = new HashMap<String, String>();

            aliases.put( "l", "compiler.library-path" );
            aliases.put( "el", "compiler.external-library-path" );
            aliases.put( "fb", "use-flashbuilder-project-files" );
            aliases.put( "is", "include-sources");
            aliases.put( "sp", "compiler.source-path");
            aliases.put( "rsl", "runtime-shared-libraries");
            aliases.put( "keep", "compiler.keep-generated-actionscript");
          aliases.put( "o", "output" );
          aliases.put("rslp", "runtime-shared-library-path");
          aliases.put("static-rsls", "static-link-runtime-shared-libraries");
        }
        return aliases;
    }

    //
    // PathResolver
    //
    private IPathResolver pathResolver;
   
    /**
     * Set a path resolver to resolver files relative to a
     * configuration. Files inside of configuration files are resolved relative
     * to those configuration files and files on the command line are resolved
     * relative to the root directory of the compile.
     *
     * @param pathResolver a path resolver for this configuration. May not be null.
     */
    public void setPathResolver(IPathResolver pathResolver)
    {
        this.pathResolver = pathResolver;
    }
   
    //
    // mainDefinition
    //
    private String mainDefinition;

    /**
     * Main definition is the root class of a SWF. {@code mxmlc} only takes one
     * file in the source list. The main definition name is the main source file
     * name.
     *
     * @return main definition
     */
    public String getMainDefinition()
    {
        return mainDefinition;
    }

    public void setMainDefinition(String mainDefinition)
    {
        assert mainDefinition != null : "main definition can't be null";
        assert !"".equals(mainDefinition) : "main definition can't be empty";

        this.mainDefinition = mainDefinition;
    }

    //
    // 'benchmark' option
    //
    @Config(removed = true)
    @Mapping("benchmark")
    public void setBenchmark(ConfigurationValue cv, boolean b)
    {
    }

    //
    // 'debug-password' option
    //

    private String debugPassword;

    /**
     * Lets you engage in remote debugging sessions with the Flash IDE.
     */
    public String getDebugPassword()
    {
        return debugPassword;
    }

    @Config(advanced = true)
    @Mapping("debug-password")
    @DefaultArgumentValue("")
    public void setDebugPassword(ConfigurationValue cv, String debugPassword)
    {
        this.debugPassword = debugPassword;
    }

    //
    // 'default-background-color' option
    //

    private int backgroundColor = 0x50727E;

    public int getDefaultBackgroundColor()
    {
        return this.backgroundColor;
    }

    @Config(advanced = true)
    @Mapping("default-background-color")
    public void setDefaultBackgroundColor(ConfigurationValue cv, int backgroundColor)
    {
        this.backgroundColor = backgroundColor;
    }

    //
    // 'default-frame-rate' option
    //

    private int frameRate = 24;

    public int getDefaultFrameRate()
    {
        return frameRate;
    }

    @Config(advanced = true)
    @Mapping("default-frame-rate")
    public void setDefaultFrameRate(ConfigurationValue cv, int rate)
            throws ConfigurationException
    {
        if (rate <= 0)
            throw new ConfigurationException.GreaterThanZero(cv.getVar(),
                                              cv.getSource(), cv.getLine());
        frameRate = rate;
    }

    //
    // 'default-script-limits' option
    //

    private int scriptLimit = 60;
    private int scriptRecursionLimit = 1000;
    private boolean scriptLimitsSet = false;

    public int getScriptTimeLimit()
    {
        return scriptLimit;
    }

    public int getScriptRecursionLimit()
    {
        return scriptRecursionLimit;
    }

    public boolean scriptLimitsSet()
    {
        return scriptLimitsSet;
    }

    @Config(advanced = true)
    @Mapping("default-script-limits")
    @Arguments({"max-recursion-depth", "max-execution-time"})
    public void setDefaultScriptLimits(ConfigurationValue cv, int scriptLimit, int scriptRecursionLimit)
            throws ConfigurationException
    {
        if (scriptLimit <= 0)
            throw new ConfigurationException.GreaterThanZero(cv.getVar(), cv.getSource(), cv.getLine());

        if (scriptRecursionLimit <= 0)
            throw new ConfigurationException.GreaterThanZero(cv.getVar(),
                                              cv.getSource(), cv.getLine());

        this.scriptLimitsSet = true;
        this.scriptLimit = scriptRecursionLimit;
        this.scriptRecursionLimit = scriptLimit;
    }

    //
    // 'default-size' option
    //

    private int defaultWidth = 500;
    private int defaultHeight = 375;

    public int getDefaultWidth()
    {
        return defaultWidth;
    }

    public int getDefaultHeight()
    {
        return defaultHeight;
    }

    @Config(advanced = true)
    @Arguments({"width", "height"})
    @Mapping("default-size")
    public void setDefaultSize(ConfigurationValue cv, int width, int height)
            throws ConfigurationException
    {
        if (width < DEFAULT_WIDTH_MIN || width > DEFAULT_WIDTH_MAX ||
            height < DEFAULT_HEIGHT_MIN || height > DEFAULT_HEIGHT_MAX)
            throw new ConfigurationException.IllegalDimensions(width, height, cv.getVar(), cv.getSource(), cv.getLine());

        this.defaultWidth = width;
        this.defaultHeight = height;
    }

    //
    // 'externs' option
    //

    private final Set<String> externs = new LinkedHashSet<String>();

    public Set<String> getExterns()
    {
        return externs;
    }

    /**
     * Sets a list of classes to exclude from linking when compiling a SWF file.
     * This option provides compile-time link checking for external references
     * that are dynamically linked.
     */
    @Config(advanced = true, allowMultiple = true)
    @Mapping("externs")
    @Arguments("symbol")
    @InfiniteArguments
    public void setExterns(ConfigurationValue cfgval, List<String> vals)
    {
        externs.addAll(QNameNormalization.normalize(vals));
    }

    //
    // 'includes' option
    //

    private final Set<String> includes = new LinkedHashSet<String>();

    public Set<String> getIncludes()
    {
        return includes;
    }

    /**
     * Links one or more classes to the resulting application SWF file, whether
     * or not those classes are required at compile time. To link an entire SWC
     * file rather than individual classes, use the include-libraries option.
     */
    @Config(allowMultiple = true, advanced = true)
    @Mapping("includes")
    @Arguments("symbol")
    @InfiniteArguments
    public void setIncludes(ConfigurationValue cfgval, List<String> vals)
    {
        includes.addAll(QNameNormalization.normalize(vals));
    }

    //
    // 'framework' option
    //
    //
    @Config(allowMultiple = true, advanced = true, removed = true)
    @Mapping("framework")
    public void setFramework(ConfigurationValue cfgval, String value)
    {
    }
   
    // 'link-report' option
    //

    private String linkReportFileName = null;

    public File getLinkReport()
    {
        return linkReportFileName != null ? new File(linkReportFileName) : null;
    }

    /**
     * Prints linking information to the specified output file. This file is an
     * XML file that contains {@code def} tags, {@code pre} tags and {@code ext}
     * tags showing linker dependencies in the final SWF file. The file format
     * output by this command can be used to write a file for input to the
     * {@code load-externs} option.
     */
    @Config(advanced = true)
    @Mapping("link-report")
    @Arguments("filename")
    public void setLinkReport(ConfigurationValue cv, String filename)
    {
        this.linkReportFileName = getOutputPath(cv, filename);
    }

    //
    // 'size-report' option
    //

    private String sizeReportFileName = null;

    public File getSizeReport()
    {
        return sizeReportFileName != null ? new File(sizeReportFileName) : null;
    }

    @Config(advanced = true)
    @Mapping("size-report")
    @Arguments("filename")
    public void setSizeReport(ConfigurationValue cv, String filename)
    {
        this.sizeReportFileName = getOutputPath(cv, filename);
    }

    //
    // 'load-externs' option
    //

    /**
     * Specifies the location of an XML file that contains def, pre, and ext
     * symbols to omit from linking when compiling a SWF file. The XML file uses
     * the same syntax as the one produced by the link-report option.
     * <p>
     * This option provides compile-time link checking for external components
     * that are dynamically linked.
     */
    @Config(allowMultiple = true, advanced = true)
    @Mapping("load-externs")
    @Arguments("filename")
    public void setLoadExterns(ConfigurationValue cfgval, String filename) throws ConfigurationException
    {
        final String path = resolvePathStrict(filename, cfgval);
        final FileSpecification f = new FileSpecification(path);
        final List<String> externsFromFile = LoadExternsParser.collectExterns(cfgval, f);
        externs.addAll(externsFromFile);
    }

    //
    // 'raw-metadata' option
    //

    private String metadata = null;

    public String getRawMetadata()
    {
        if (metadata != null)
            return metadata;

        return generateMetadata();
    }

    private static final String RDF_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
    private static final String DC_URI = "http://purl.org/dc/elements/1.1";
   
    /**
     * @return Metadata XML string.
     */
    private final String generateMetadata()
    {
        final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
        assert xmlOutputFactory != null : "Expect XMLOutputFactory implementation.";
        final StringWriter stringWriter = new StringWriter();
        XMLStreamWriter xmlWriter = null;
       
        try
        {
            xmlWriter = new XMLFormatter(
                    xmlOutputFactory.createXMLStreamWriter(stringWriter));
            xmlWriter.writeStartDocument();
           
            xmlWriter.writeStartElement("rdf", "RDF", RDF_URI);
            xmlWriter.setPrefix("rdf", RDF_URI);
            xmlWriter.writeNamespace("rdf", RDF_URI);
           
            // write rdf:Description
            xmlWriter.writeStartElement(RDF_URI, "Description");
            xmlWriter.setPrefix("dc", DC_URI);
            xmlWriter.setPrefix(VersionInfo.COMPILER_NAMESPACE_PREFIX,
                    VersionInfo.COMPILER_NAMESPACE_URI);
            xmlWriter.writeNamespace("dc", DC_URI);
            xmlWriter.writeNamespace(VersionInfo.COMPILER_NAMESPACE_PREFIX,
                    VersionInfo.COMPILER_NAMESPACE_URI);
           
            // write dc:format
            xmlWriter.writeStartElement(DC_URI, "format");
            xmlWriter.writeCharacters("application/x-shockwave-flash");
            xmlWriter.writeEndElement();
           
            if (isFlex())
            {
                // write localizedTitles
                writeMap(xmlWriter, DC_URI, "description", localizedDescriptions);
           
                // write localizedDescription
                writeMap(xmlWriter, DC_URI, "title", localizedTitles);

                // write publisher
                writeCollection(xmlWriter, DC_URI, "publisher", publishers);
               
                // write creators
                writeCollection(xmlWriter, DC_URI, "creator", creators);
               
                // write contributor
                writeCollection(xmlWriter, DC_URI, "contributor", contributors);
               
                // write language
                writeCollection(xmlWriter, DC_URI, "language", langs);
               
                // write date
                writeDate(xmlWriter);
            }
           
            // write compiledBy
            writeCompiledBy(xmlWriter);
           
            // write
            xmlWriter.writeEndElement(); // Description
            xmlWriter.writeEndDocument();
        }
        catch (XMLStreamException e)
        {
            return "";
        }
       
        return stringWriter.toString();
    }

    /**
     * Write information about the compiler that compiled the swf.
     *
     * @param writer
     * @throws XMLStreamException
     */
    private void writeCompiledBy(XMLStreamWriter writer) throws XMLStreamException
    {
        writer.writeEmptyElement(VersionInfo.COMPILER_NAMESPACE_URI, VersionInfo.COMPILER_ELEMENT);
        writer.writeAttribute(VersionInfo.COMPILER_NAME_ATTRIBUTE, VersionInfo.getCompilerName());
        writer.writeAttribute(VersionInfo.COMPILER_VERSION_ATTRIBUTE, VersionInfo.getCompilerVersion());
        writer.writeAttribute(VersionInfo.COMPILER_BUILD_ATTRIBUTE, VersionInfo.getCompilerBuild());

        writer.writeEndElement(); // compiledBy
    }

    /**
     * Write the data to rdf/xml
     *
     * @param writer
     * @throws XMLStreamException
     */
    private void writeDate(XMLStreamWriter writer) throws XMLStreamException
    {
        if (date == null)
        {
            date = DateFormat.getDateInstance().format(new Date());
        }
       
        writer.writeStartElement(DC_URI, "date");
        writer.writeCharacters(date);
        writer.writeEndElement();
    }

    /**
     * Write a map of values to rdf/xml
     *
     * @param writer
     * @param namespaceURI
     * @param localName
     * @param mapData
     * @throws XMLStreamException
     */
    private void writeMap(XMLStreamWriter writer, String namespaceURI,
            String localName, Map<String, String> mapData) throws XMLStreamException
    {
        if (mapData.size() > 0)
        {
            writer.writeStartElement(namespaceURI, localName);
            if ((mapData.size() == 1) && (mapData.get("x-default") != null))
            {
                String data = mapData.get("x-default");
                writer.writeCharacters(data);
            }
            else
            {
                writer.writeStartElement(RDF_URI, "Alt");
                for (final String key : mapData.keySet())
                {
                    final String value = mapData.get(key);
                    writer.writeStartElement(RDF_URI, "li");
                    writer.writeAttribute("xml", "", "lang", key);
                    writer.writeCharacters(value);
                    writer.writeEndElement();
                }
                writer.writeEndElement();
            }

            writer.writeEndElement();
        }

    }

    /**
     * Write a collection values to rdf/xml.
     *
     * @param writer
     * @param namespaceURI
     * @param localName
     * @param values
     * @throws XMLStreamException
     */
    private void writeCollection(XMLStreamWriter writer, String namespaceURI,
            String localName, Collection<String> values) throws XMLStreamException
    {
        if (values.isEmpty())
            return;
       
        writer.writeStartElement(namespaceURI, localName);
       
        if (values.size() > 1)
            writer.writeStartElement(RDF_URI, "Bag");
       
        for (String value : values)
        {
            writer.writeCharacters(value);
        }

        if (values.size() > 1)
            writer.writeEndElement();

        writer.writeEndElement();
       
    }

    /**
     * Defines the metadata for the resulting SWF file. The value of this option
     * overrides any metadata-related compiler options such as contributor,
     * creator, date, and description.
     */
    @Config(advanced = true)
    @Mapping("raw-metadata")
    @Arguments("text")
    public void setRawMetadata(ConfigurationValue cv, String xml) throws ConfigurationException
    {
        if (metadata != null)
        {
            throw new ConfigurationException.BadMetadataCombo(cv.getVar(), cv.getSource(), cv.getLine());
        }

        this.metadata = xml;
    }

    //
    // 'resource-bundle-list' option
    //

    private String rbListFileName = null;

    public String getResourceBundleList()
    {
        return rbListFileName;
    }
   
   
    //
    // 'include-resource-bundles' option
    //
   
    /**
     * include-resource-bundles [...]
     */
    private List<String> includeResourceBundles = new ArrayList<String>();

    /**
     * @return a list of resource bundles to include in the swc
     */
    public List<String> getIncludeResourceBundles()
    {
        return includeResourceBundles;
    }
   
    /**
     * Sets the resource bundles that should be included in this SWC or SWF. This list
     * can have locale independent qualified name for property files
     * (with the name not including the suffix) or qualified name for classes that
     * extend ResourceBundle.
     * <p>
     * Qualified name of a properties file is determined by its relative path to its
     * parent source folder. Such as:
     * <p>
     * Source path: locale/{locale}
     * <p>
     * Path of properties file 1: locale/en_US/A.properties
     * Qualified name of properties file 1: A
     * <p>
     * Path of properties file 2: locale/en_US/com/resources/B.properties
     * Qualified name of properties file 1: com.resources.B
     * <p>
     * Note: Source folders of all the properties files passed using this argument
     * should be in the project's source path list.
     *
     * @param cv configuration value objects
     * @param values list of resource bundles to include in the swc or swf
     */
    @Config(allowMultiple = true)
    @Mapping("include-resource-bundles")
    @Arguments("bundle")
    @FlexOnly
    public void setIncludeResourceBundles(ConfigurationValue cv, List<String> values)
    {
        includeResourceBundles.addAll(values);
    }

    /**
     * Prints a list of resource bundles that are used by the current
     * application to a file named with the filename argument. You then use this
     * list as input that you specify with the include-resource-bundles option
     * to create a resource module.
     */
    @Config(advanced = true)
    @Mapping("resource-bundle-list")
    @Arguments("filename")
    @FlexOnly
    public void setResourceBundleList(ConfigurationValue cv, String filename)
    {
        this.rbListFileName = getOutputPath(cv, filename);
    }

    //
    // 'runtime-shared-libraries' option
    //

    private List<String> rslList = new LinkedList<String>();
   
    public List<String> getRuntimeSharedLibraries()
    {
        return rslList;
    }

    /**
     * Specifies a list of runtime shared libraries (RSLs) to use for this
     * application. RSLs are dynamically-linked at run time. The compiler
     * externalizes the contents of the application that you are compiling that
     * overlap with the RSL.
     * <p>
     * You specify the location of the SWF file relative to the deployment
     * location of the application. For example, if you store a file named
     * library.swf file in the web_root/libraries directory on the web server,
     * and the application in the web root, you specify libraries/library.swf.
     */
    @Config(allowMultiple = true)
    @Mapping("runtime-shared-libraries")
    @Arguments("url")
    @InfiniteArguments
    @FlexOnly
    public void setRuntimeSharedLibraries(ConfigurationValue cfgval, List<String> urls) throws ConfigurationException
    {
        rslList.addAll(urls);
    }

    //
    // 'use-network' option
    //

    private boolean useNetwork = true;

    public boolean getUseNetwork()
    {
        return useNetwork;
    }

    /**
     * Specifies that the current application uses network services.
     * <p>
     * The default value is true.
     * <p>
     * When the use-network property is set to false, the application can access
     * the local filesystem (for example, use the XML.load() method with file:
     * URLs) but not network services. In most circumstances, the value of this
     * property should be true.
     */
    @Config
    @Mapping("use-network")
    public void setUseNetwork(ConfigurationValue cv, boolean b)
    {
        this.useNetwork = b;
    }

    //
    // runtime-shared-library-path
    //

    private List<RuntimeSharedLibraryPathInfo> rslPathInfoList;

    /**
     * @return List of of all the -runtime-shared-libraries-path options.
     * Each-runtime-shared-libraries-path option supplied results in a
     * RslPathInfo object. Each object in the list is of type RslPathInfo.
     * <p>
     * The list will be empty if -static-link-runtime-shared-libraries=true.
     * <p>
     * TODO Verify if this is still true and make the code do what it says.
     */
    public List<RuntimeSharedLibraryPathInfo> getRslPathInfo()
    {
        if (rslPathInfoList == null)
            return Collections.emptyList();
        else
            return rslPathInfoList;
    }

    public List<File> getRslExcludedLibraries()
    {
        if (rslPathInfoList == null || getStaticLinkRsl())
            return Collections.emptyList();

        return Lists.transform(
                rslPathInfoList,
                new Function<RuntimeSharedLibraryPathInfo, File>()
                {
                    @Override
                    public File apply(RuntimeSharedLibraryPathInfo info)
                    {
                        return info.getSWCFile();
                    }
                });
    }

    /**
     * Specifies the location of a runtime shared library (RSL). The compiler
     * externalizes the contents of the application that you are compiling that
     * overlap with the RSL.
     * <p>
     * The path-element argument is the location of the SWC file or open
     * directory to compile against. For example,
     * c:\flexsdk\frameworks\libs\framework.swc. This is the equivalent of the
     * using the external-library-path option when compiling against an RSL
     * using the runtime-shared-libraries option.
     * <p>
     * The rsl-url argument is the URL of the RSL that will be used to load the
     * RSL at runtime. The compiler does not verify the existence of the SWF
     * file at this location at compile time. It does store this string in the
     * application, however, and uses it at run time. As a result, the SWF file
     * must be available at run time but necessarily not at compile time.
     * <p>
     * The policy-file-url is the location of the crossdomain.xml file that
     * gives permission to read the RSL from the server. This might be necessary
     * because the RSL can be on a separate server as the application. For
     * example, http://www.mydomain.com/rsls/crossdomain.xml.
     * <p>
     * The failover-url and second policy-file-url arguments specify the
     * location of the secondary RSL and crossdomain.xml file if the first RSL
     * cannot be loaded. This most commonly happens when the client Player
     * version does not support cross-domain RSLs. You can add any number of
     * failover RSLs, but must include a policy file URL for each one.
     * <p>
     * Do not include spaces between the comma-separated values. The following
     * example shows how to use this option:
     *
     * <pre>
     * mxmlc -o=../lib/app.swf -runtime-shared-library-path=../lib/mylib.swc,../bin/myrsl.swf Main.mxml
     * </pre>
     *
     * You can specify more than one library file to be used as an RSL. You do
     * this by adding additional runtime-shared-library-path options.
     * <p>
     * You can also use the runtime-shared-libraries command to use RSLs with
     * your applications. However, the runtime-shared-library-path option lets
     * you also specify the location of the policy file and failover RSL.
     * <p>
     */
    // NOTE: if the annotations are modified, then also modify the annotations
    // in COMPCConfiguration.
    @Config(allowMultiple = true)
    @Mapping({"runtime-shared-library-path"})
    @SoftPrerequisites({"static-link-runtime-shared-libraries"})
    @ArgumentNameGenerator(RSLArgumentNameGenerator.class)
    @InfiniteArguments
    @FlexOnly
    public void setRuntimeSharedLibraryPath(
            ConfigurationValue cfgval,
            List<String> urls) throws ConfigurationException
    {

        if (urls.isEmpty())
            return;

        // Usage rule: if you use -rslp on the command line
        // it will take effect unless you also specify -static-rsls=true on the command line.
        if (CommandLineConfigurator.SOURCE_COMMAND_LINE.equals(cfgval.getSource()))
        {
            setOverrideStaticLinkRsl(false);
        }

        // ignore rsl if told to
        if (getStaticLinkRsl())
        {
            return;
        }

        if (urls.size() < 2)
        {
            // insufficent arguments
            throw new ConfigurationException.MissingArgument("rsl-url",
            "runtime-shared-library-path", cfgval.getSource(),
            cfgval.getLine());
        }

        RuntimeSharedLibraryPathInfo info = new RuntimeSharedLibraryPathInfo();

        // validate the first argument, the swc or open directory, required.
        String include = resolvePathStrict(urls.get(0), cfgval, true);
        info.setSWCPath(urls.get(0));
        info.setSWCFile(new File(include));

        // the rest of the args are: rsl-url, policy-file-url, rsl-url, policy-file-url,...
        for (int i = 1; i < urls.size(); ++i)
        {
            final String url = urls.get(i);
            if ((i + 1) % 2 == 0)
            {
                if ("".equals(url.length()))
                {
                    // rsl urls is required
                    throw new ConfigurationException.MissingArgument("rsl-url",
                    "runtime-shared-library-path", cfgval.getSource(),
                    cfgval.getLine());
                }
                info.addRSLURL(url);
            }
            else
            {
                info.addPolicyFileURL(url);
            }
        }

        // if the last policy file was not specified, then add an empty one so
        // there are always the same number of rsls and policy files.
        if ((urls.size() % 2) == 0)
        {
            info.addPolicyFileURL("");
        }

        // take local variables and add to overall arguments.
        if (rslPathInfoList == null)
        {
            rslPathInfoList = new ArrayList<RuntimeSharedLibraryPathInfo>();
        }

        rslPathInfoList.add(info);
    }

    //
    // 'static-link-runtime-shared-libraries' option
    //

    private boolean staticLinkRsl = true;
    private String staticLinkRslSource;

    /**
     * @return true if -cd-rsl option should be used. False otherwise.
     */
    public boolean getStaticLinkRsl()
    {
        return staticLinkRsl;
    }

    /**
     * Allow another option, namely -rslp to override the value of static-rsls.
     * But you can not override a -static-rsls option that came from the command
     * line.
     *
     * @param staticLinkRsl
     */
    protected void setOverrideStaticLinkRsl(boolean staticLinkRsl)
    {
        if (CommandLineConfigurator.SOURCE_COMMAND_LINE.equals(staticLinkRslSource))
        {
            return;
        }

        this.staticLinkRsl = staticLinkRsl;
    }

    /**
     * Determines whether to compile against libraries statically or use RSLs.
     * Set this option to true to ignore the RSLs specified by the
     * runtime-shared-library-path option. Set this option to false to use the
     * RSLs.
     * <p>
     * This option is useful so that you can quickly switch between a statically
     * and dynamically linked application without having to change the
     * runtime-shared-library-path option, which can be verbose, or edit the
     * configuration files.
     */
    @Config
    @Mapping("static-link-runtime-shared-libraries")
    @FlexOnly
    public void setStaticLinkRuntimeSharedLibraries(ConfigurationValue cv, boolean b)
    {
        staticLinkRsl = b;
        staticLinkRslSource = cv.getSource();
    }

    //
    // 'use-flashbuilder-project-files' option
    //
    private Boolean useFlashBuilderProjectFiles = false;
   
    public Boolean getUseFlashBuilderProjectFiles()
    {
        return useFlashBuilderProjectFiles;
    }

    @Config
    @Mapping({"use-flashbuilder-project-files"})
    @FlexOnly
    public void setUseFlashBuilderProjectFiles(ConfigurationValue cv, Boolean useFiles) throws ConfigurationException
    {
        useFlashBuilderProjectFiles = useFiles;
    }

    //
    // 'verify-digests' options
    //

    private boolean verifyDigests = true;

    /**
     * @return true if digest information associated with the -cd-rsl option is
     * used by the application at runtime. False otherwise.
     */
    public boolean getVerifyDigests()
    {
        return verifyDigests;
    }

    /**
     * Instructs the application to check the digest of the RSL SWF file against
     * the digest that was compiled into the application at compile time. This
     * is a security measure that lets you load RSLs from remote domains or
     * different sub-domains. It also lets you enforce versioning of your RSLs
     * by forcing an application's digest to match the RSL's digest. If the
     * digests are out of sync, you must recompile your application or load a
     * different RSL SWF file.
     */
    @Config(advanced = true)
    @FlexOnly
    public void setVerifyDigests(ConfigurationValue cv, boolean b)
    {
        verifyDigests = b;
    }

    //
    // 'remove-unused-rsls' option
    //

    private boolean removeUnusedRSLs = false;

    /**
     * @return true if the user wants to remove unused RSLs. Otherwise false.
     */
    public boolean getRemoveUnusedRsls()
    {
        return removeUnusedRSLs;
    }

    @Config(advanced = true)
    @FlexOnly
    public void setRemoveUnusedRsls(ConfigurationValue cv, boolean b)
    {
        removeUnusedRSLs = b;
    }

    //
    // '-include-inheritance-dependencies-only' option
    //

    private boolean includeInheritanceDependenciesOnly = false;

    /**
     * @return true if the user want to include inheritance dependencies only.
     */
    public boolean getIncludeInheritanceDependenciesOnly()
    {
        return includeInheritanceDependenciesOnly;
    }

    @Config(advanced = true)
    public void setIncludeInheritanceDependenciesOnly(ConfigurationValue cv, boolean b)
    {
        includeInheritanceDependenciesOnly = b;
    }

    //
    // 'target-player' option
    //

    // targeted player version (also set in DefaultsConfigurator)
    private int majorVersionTarget = 11;
    private int minorVersionTarget = 1;
    private int revisionTarget = 0;
   
    /**
     * The major part the earliest player version that this compiler can target.
     * The code generator generates bytecode which will not pass
     * verification on players earlier than 10.1.
     */
    public static final int TARGET_PLAYER_MAJOR_VERSION_MIN = 10;

    /**
     * The minor part the earliest player version that this compiler can target.
     * The code generator generates bytecode which will not pass
     * verification on players earlier than 10.1.
     */
    public static final int TARGET_PLAYER_MINOR_VERSION_MIN = 1;
  
    /**
     * @return The major version of the player targeted by this application. The
     * returned value will be greater to or equal to 9.
     */
    public int getTargetPlayerMajorVersion()
    {
        return majorVersionTarget;
    }

    /**
     * @return The minor version of the player targeted by this application. The
     * returned value will be greater to or equal to 0.
     */
    public int getTargetPlayerMinorVersion()
    {
        return minorVersionTarget;
    }

    /**
     * @return The revision of the player targeted by this application. The
     * returned value will be greater to or equal to 0.
     */
    public int getTargetPlayerRevision()
    {
        return revisionTarget;
    }

    /**
     * Specifies the version of Flash Player that you want to target with the
     * application. Features requiring a later version of Flash Player are not
     * compiled into the application.
     * <p>
     * The player_version parameter has the following format:<br>
     * <code>major_version.minor_version.revision</code>
     * <p>
     * The major_version is required while minor_version and revision are
     * optional. The minimum value is 10.0.0. If you do not specify the
     * minor_version or revision, then the compiler uses zeros.
     * <p>
     * The value of major_version is also used by the {targetPlayerMajorVersion}
     * token in the flex-config.xml file. This token can be used in any
     * <path-element> element.
     * <p>
     * If you do not explicitly set the value of this option, the compiler uses
     * the default from the flex-config.xml file. The value in flex-config.xml
     * is the version of Flash Player that shipped with the SDK.
     * <p>
     * This option is useful if your application's audience has a specific
     * player and cannot upgrade. You can use this to "downgrade" your
     * application for that audience.
     */
    @Config
    @Arguments("version")
    public void setTargetPlayer(ConfigurationValue cv, String version)
            throws ConfigurationException
    {
        if (version == null || version.equals(""))
            return;

        final String[] results = Iterables.toArray(
                Splitter.on(".").omitEmptyStrings().trimResults().split(version),
                String.class);

        // major.minor.revision
        // major is required, minor and revision are optional
        if (results.length < 1 || results.length > 3)
            throw new ConfigurationException.BadVersion(version, "target-player");

        final String majorVersion = results[0];

        final String minorVersion;
        if (results.length > 1)
            minorVersion = results[1];
        else
            minorVersion = "0";

        final String revision;
        if (results.length > 2)
            revision = results[2];
        else
            revision = "0";

        try
        {
            majorVersionTarget = Integer.parseInt(majorVersion);
            minorVersionTarget = Integer.parseInt(minorVersion);
            revisionTarget = Integer.parseInt(revision);
        }
        catch (NumberFormatException e)
        {
            throw new ConfigurationException.BadVersion(version, "target-player");
        }

        if (majorVersionTarget < TARGET_PLAYER_MAJOR_VERSION_MIN ||
            majorVersionTarget == TARGET_PLAYER_MAJOR_VERSION_MIN &&
            minorVersionTarget < TARGET_PLAYER_MINOR_VERSION_MIN)
        {
            throw new ConfigurationException.BadVersion(version, "target-player");
        }
    }

    //
    // 'swf-version' option
    //

    // swf version 13 is what shipped with player 11, and is the min version for
    // LZMA compression
    private static final Map<String,Integer> targetPlayerToSWFVersionMap = getSwfVersionMap();
   
    private static Map<String,Integer> getSwfVersionMap()
    {
        // Player 9 and below are not supported.
        // 10.0 -> 10
        // 10.1 -> 10
        // 10.2 -> 11
        // 10.3 -> 12
        // 11.0 -> 13
        // 11.1 -> 14
        // 11.2 -> 15
        // 11.3 -> 16
        // 11.4 -> 17
        // 11.5 -> 18
        // 11.6 -> 19
        // 11.7 -> 20
        // 11.8 -> 21
        // 11.9 -> 22
       
        Map<String, Integer> map  = new HashMap<String, Integer>(10);
       
        map.put("10.0", 10);
        map.put("10.1", 10);
        map.put("10.2", 11);
        map.put("10.3", 12);
        map.put("11.0", 13);
        map.put("11.1", 14);
        map.put("11.2", 15);
        map.put("11.3", 16);
        map.put("11.4", 17);
        map.put("11.5", 18);
        map.put("11.6", 19);
        map.put("11.7", 20);
        map.put("11.8", 21);
        map.put("11.9", 22);
   
        return map;
    }
   
    private int lookupSwfVersion()
    {
        int swfVersion = DEFAULT_SWF_VERSION;
        Integer lookupVersion = targetPlayerToSWFVersionMap.get(
                Integer.toString(getTargetPlayerMajorVersion()) + "." +
                Integer.toString(getTargetPlayerMinorVersion()));
        if (lookupVersion != null)
            swfVersion = lookupVersion;
       
        return swfVersion;
    }
   
    private final int UNSET_SWF_VERSION = -1;
    private final int DEFAULT_SWF_VERSION = 14; // matches default target-player
    private final int MINIMUM_SWF_VERSION = 10; // matches minimum target-player
   
    private int swfVersion = UNSET_SWF_VERSION;

    public int getSwfVersion()
    {
        if (swfVersion == UNSET_SWF_VERSION)
            swfVersion = lookupSwfVersion();
        return swfVersion;
    }

    @Config
    @Mapping("swf-version")
    public void setSwfVersion(ConfigurationValue cv, int version)
        throws ConfigurationException
    {
        if (version < MINIMUM_SWF_VERSION)
            throw new ConfigurationException.BadVersion(Integer.toString(version), "swf-version");
       
        swfVersion = version;
    }

    //
    // 'use-direct-blit' option
    //

    private boolean useDirectBlit = true;

    public boolean getUseDirectBlit()
    {
        return useDirectBlit;
    }

    @Config
    public void setUseDirectBlit(ConfigurationValue cv, boolean value)
    {
        useDirectBlit = value;
    }

    //
    // 'use-gpu' option
    //

    private boolean useGpu = true;

    public boolean getUseGpu()
    {
        return useGpu;
    }

    @Config
    public void setUseGpu(ConfigurationValue cv, boolean value)
    {
        useGpu = value;
    }

    //
    // 'tools-locale' options
    //
   
    private Locale toolsLocale = null;
   
    /**
     * @return locale to use when reporting compile time errors, or
     * <code>null</code> if not specified. In that case, system's
     * locale is used.
     */
    public Locale getToolsLocale()
    {
        return toolsLocale;
    }
           
    /**
     * Configures the LocalizationManager's locale, which is used when reporting
     * compile time errors, warnings, and info.
     *
     * @param toolsLocale A locale in Java format. For example, "en" or "ja_JP".
     * @throws ConfigurationException When the specified toolsLocale is not
     * available a ToolsLocaleNotAvailable error is reported.
     */
    @Config
    @Mapping("tools-locale")
    public void setToolsLocale(ConfigurationValue cv, String toolsLocale)
            throws ConfigurationException
    {
        Locale[] locales = Locale.getAvailableLocales();

        for (int i = 0; i < locales.length; i++)
        {
            if (locales[i].toString().equals(toolsLocale))
            {
                this.toolsLocale = locales[i];

                LocalizationManager.get().setLocale(locales[i]);
                return;
            }
        }

        throw new ConfigurationException.ToolsLocaleNotAvailable(cv.getVar(),
                                                                 cv.getSource(),
                                                                 cv.getLine());
    }

    //
    // 'compiler.accessible' option
    //

    private boolean accessible = false;

    public boolean getCompilerAccessible()
    {
        return accessible;
    }

    /**
     * Enables accessibility features when compiling the application or SWC
     * file.
     */
    @Config
    @Mapping({"compiler", "accessible"})
    @FlexOnly
    public void setCompilerAccessible(ConfigurationValue cv, boolean accessible)
    {
        this.accessible = accessible;
    }

    //
    // 'compiler.actionscript-file-encoding' option
    //

    private String actionscriptFileEncoding = null;

    public String getCompilerActionscriptFileEncoding()
    {
        return actionscriptFileEncoding;
    }

    /**
     * Sets the file encoding for ActionScript files.
     */
    @Config
    @Mapping({"compiler", "actionscript-file-encoding"})
    public void setCompilerActionscriptFileEncoding(ConfigurationValue cv, String encoding)
    {
        actionscriptFileEncoding = encoding;
    }

    //
    // 'compiler.adjust-opdebugline' option (hidden)
    //

    private boolean adjustOpDebugLine = true;

    public boolean getAdjustOpDebugLine()
    {
        return adjustOpDebugLine;
    }

    /**
     * For internal use only. Set it to false so that debugging mxmlc
     * auto-generated code is easier.
     */
    @Config(advanced = true, hidden = true)
    public void setCompilerAdjustOpdebugline(ConfigurationValue cv, boolean b)
    {
        adjustOpDebugLine = b;
    }

    //
    // 'compiler.allow-source-path-overlap' option
    //

    private boolean allowSourcePathOverlap = false;

    public boolean getAllowSourcePathOverlap()
    {
        return allowSourcePathOverlap;
    }

    /**
     * Checks if a source-path entry is a sub-directory of another source-path
     * entry. It helps make the package names of MXML components unambiguous.
     */
    @Config(advanced = true)
    public void setCompilerAllowSourcePathOverlap(ConfigurationValue cv, boolean b)
    {
        allowSourcePathOverlap = b;
    }

    //
    // 'compiler.binding-value-change-event' option
    //

    private String bindingValueChangeEvent = "mx.events.PropertyChangeEvent";

    public String getBindingValueChangeEvent()
    {
        return bindingValueChangeEvent;
    }

    /**
     * The change event class for generated binding code
     */
    @Config(advanced = true)
    public void setCompilerBindingValueChangeEvent(ConfigurationValue cv, String b)
    {
        bindingValueChangeEvent = b;
    }

    //
    // 'compiler.binding-value-change-event-kind' option
    //

    private String bindingValueChangeEventKind = "mx.events.PropertyChangeEventKind";

    public String getBindingValueChangeEventKind()
    {
        return bindingValueChangeEventKind;
    }

    /**
     * The change event kind for generated binding code
     */
    @Config(advanced = true)
    public void setCompilerBindingValueChangeEventKind(ConfigurationValue cv, String b)
    {
        bindingValueChangeEventKind = b;
    }

    //
    // 'compiler.binding-value-change-event-type' option
    //

    private String bindingValueChangeEventType = "propertyChange";

    public String getBindingValueChangeEventType()
    {
        return bindingValueChangeEventType;
    }

    /**
     * The change event type for generated binding code
     */
    @Config(advanced = true)
    public void setCompilerBindingValueChangeEventType(ConfigurationValue cv, String b)
    {
        bindingValueChangeEventType = b;
    }

    //
    // 'compiler.binding-event-handler-event' option
    //

    private String bindingEventHandlerEvent = "flash.events.Event";

    public String getBindingEventHandlerEvent()
    {
        return bindingEventHandlerEvent;
    }

    /**
     * The event handler event for generated binding code
     */
    @Config(advanced = true)
    public void setCompilerBindingEventHandlerEvent(ConfigurationValue cv, String b)
    {
        bindingEventHandlerEvent = b;
    }

    //
    // 'compiler.binding-event-handler-class' option
    //

    private String bindingEventHandlerClass = "flash.events.EventDispatcher";

    public String getBindingEventHandlerClass()
    {
        return bindingEventHandlerClass;
    }

    /**
     * The event handler class for generated binding code
     */
    @Config(advanced = true)
    public void setCompilerBindingEventHandlerClass(ConfigurationValue cv, String b)
    {
        bindingEventHandlerClass = b;
    }
   
    //
    // 'compiler.binding-event-handler-interface' option
    //

    private String bindingEventHandlerInterface = "flash.events.IEventDispatcher";

    public String getBindingEventHandlerInterface()
    {
        return bindingEventHandlerInterface;
    }

    /**
     * The event handler interface for generated binding code
     */
    @Config(advanced = true)
    public void setCompilerBindingEventHandlerInterface(ConfigurationValue cv, String b)
    {
        bindingEventHandlerInterface = b;
    }
   
    /**
     * Syntax:<br/>
     * <code>-define=&lt;name&gt;,&lt;value&gt;</code>
     * where name is <code>NAMESPACE::name</code> and value is a legal definition value
     * (e.g. <code>true</code> or <code>1</code> or <code>!CONFIG::debugging</code>)
     *
     * Example: <code>-define=CONFIG::debugging,true</code>
     *
     * In <code>flex-config.xml</code>:<br/>
     * <pre>
     * <flex-config>
     *    <compiler>
     *       <define>
     *          <name>CONFIG::debugging</name>
     *          <value>true</value>
     *       </define>
     *       ...
     *    </compile>
     * </flex-config>
     * </pre>
     *
     * Values:<br/>
     * Values are ActionScript expressions that must coerce and evaluate to constants at compile-time.
     * Effectively, they are replaced in AS code, verbatim, so <code>-define=TEST::oneGreaterTwo,"1>2"</code>
     * will getCompiler coerced and evaluated, at compile-time, to <code>false</code>.
     *
     * It is good practice to wrap values with double-quotes,
     * so that MXMLC correctly parses them as a single argument:<br/>
     * <code>-define=TEST::oneShiftRightTwo,"1 >> 2"</code>
     *
     * Values may contain compile-time constants and other configuration values:<br/>
     * <code>-define=CONFIG::bool2,false -define=CONFIG::and1,"CONFIG::bool2 && false" TestApp.mxml</code>
     *
     * String values on the command-line <i>must</i> be surrounded by double-quotes, and either
     * escape-quoted (<code>"\"foo\""</code> or <code>"\'foo\'"</code>) or single-quoted
     * (<code>"'foo'"</code>).
     *
     * String values in configuration files need only be single- or double- quoted:<br/>
     * <pre>
     * <flex-config>
     *    <compiler>
     *       <define>
     *          <name>NAMES::Organization</name>
     *          <value>'Apache Software Foundation'</value>
     *       </define>
     *       <define>
     *          <name>NAMES::Application</name>
     *          <value>"Flex 4.8.0"</value>
     *       </define>
     *       ...
     *    </compile>
     * </flex-config>
     * </pre>
     *
     * Empty strings <i>must</i> be passed as <code>"''"</code> on the command-line, and
     * <code>''</code> or <code>""</code> in configuration files.
     *
     * Finally, if you have existing definitions in a configuration file, and you would
     * like to add to them with the command-line (let's say most of your build setCompilertings
     * are in the configuration, and that you are adding one temporarily using the
     * command-line), you use the following syntax:
     * <code>-define+=TEST::temporary,false</code> (noting the plus sign)
     *
     * Note that definitions can be overridden/redefined if you use the append ("+=") syntax
     * (on the commandline or in a user config file, for instance) with the same namespace
     * and name, and a new value.
     *
     * Definitions cannot be removed/undefined. You can undefine ALL existing definitions
     * from (e.g. from flex-config.xml) if you do not use append syntax ("=" or append="false").
     *
     * IMPORTANT FOR FLEXBUILDER
     * If you are using "Additional commandline arguments" to "-define", don't use the following
     * syntax though I suggest it above:
     *     -define+=CONFIG::foo,"'value'"
     * The trouble is that FB parses the double quotes incorrectly as <"'value'> -- the trailing
     * double-quote is dropped. The solution is to avoid inner double-quotes and put them around the whole expression:
     *    -define+="CONFIG::foo,'value'"
     */
    private Map<String, String> configVars;

    /**
     * @return A list of ConfigVars
     */

    public Map<String, String> getCompilerDefine()
    {
        return configVars;
    }

    @Config(advanced = true, allowMultiple = true)
    @Arguments({"name", "value"})
    public void setCompilerDefine(ConfigurationValue cv, String name, String value) throws ConfigurationException
    {
        if (configVars == null)
            configVars = new LinkedHashMap<String, String>();

        configVars.put(name, value);
    }

    //
    // 'compiler.conservative' option (hidden)
    //

    private boolean useConservativeAlgorithm = false;

    public boolean useConservativeAlgorithm()
    {
        return useConservativeAlgorithm;
    }

    @Config(advanced = true, hidden = true)
    @Mapping({"compiler", "conservative"})
    public void setCompilerConservative(ConfigurationValue cv, boolean c)
    {
        useConservativeAlgorithm = c;
    }

    //
    // 'compiler.context-root' option
    //

    private String contextRoot = null;

    public String getCompilerContextRoot()
    {
        return contextRoot;
    }

    /**
     * "Context root" is used to resolve {context.root} tokens in services
     * configuration files to improve portability.
     */
    @Config
    @Mapping({"compiler", "context-root"})
    @Arguments("context-path")
    @FlexOnly
    public void setCompilerContextRoot(ConfigurationValue cv, String contextRoot)
    {
        this.contextRoot = contextRoot;
    }

    //
    // 'compiler.debug' option
    //

    private boolean generateDebugTags = false;

    public boolean isDebuggingEnabled()
    {
        return generateDebugTags;
    }
   
    protected void setDebug(boolean value)
    {
        generateDebugTags = value;
    }
   
    @Config
    @Mapping({"compiler", "debug"})
    public void setCompilerDebug(ConfigurationValue cv, boolean generateDebugTags)
    {
        this.generateDebugTags = generateDebugTags;
    }

    //
    // 'compiler.defaults-css-url' option
    //

    /**
     * Location of defaults stylesheet.
     */
    private String defaultsCssUrl;

    public String getCompilerDefaultsCssUrl()
    {
        return defaultsCssUrl;
    }

    /**
     * Defines the location of the default style sheet. Setting this option
     * overrides the implicit use of the defaults.css style sheet in the
     * framework.swc file.
     */
    @Config(advanced = true)
    @Mapping({"compiler", "defaults-css-url"})
    @FlexOnly
    public void setCompilerDefaultsCssUrl(ConfigurationValue cv, String defaultsCssUrlPath) throws CannotOpen
    {
        defaultsCssUrl = resolvePathStrict(defaultsCssUrlPath, cv);
    }

    //
    // 'compiler.doc' option (hidden)
    //

    private boolean doc = false;

    public boolean getCompilerDoc()
    {
        return this.doc;
    }

    @Config(advanced = true, hidden = true)
    @Mapping({"compiler", "doc"})
    public void setCompilerDoc(ConfigurationValue cv, boolean doc)
    {
        this.doc = doc;
    }

    //
    // 'compiler.external-library-path' option
    //

    private final List<String> externalLibraryPath = new ArrayList<String>();

    public List<String> getCompilerExternalLibraryPath()
    {
        return externalLibraryPath;
    }

    private boolean compilingForAIR = false;

    /**
     * @return True if AIR libraries are included in the
     * {@code external-library-path}.
     */
    public boolean getCompilingForAIR()
    {
        return compilingForAIR;
    }

    @Config(allowMultiple = true, isPath = true)
    @Mapping({"compiler", "external-library-path"})
    @Arguments(Arguments.PATH_ELEMENT)
    @SoftPrerequisites({"target-player"})
    @InfiniteArguments
    public void setCompilerExternalLibraryPath(ConfigurationValue cv, String[] pathlist) throws ConfigurationException
    {
        final ImmutableList<String> pathElements = ImmutableList.copyOf(pathlist);
        final ImmutableList<String> resolvedPaths = expandTokens(pathElements,
                locales, cv, !reportMissingCompilerLibraries);
        externalLibraryPath.addAll(resolvedPaths);

        // TODO: Review usages of "compilingForAIR", because only looking at path elements
        // on library path isn't enough. There might be a folder on the library path that
        // contains AIR libraries.
        compilingForAIR = containsAIRLibraries(pathElements);
    }

    /**
     * Returns true if there's AIR libraries on the library paths.
     *
     * @param libraryPaths Library paths.
     * @return True if there's AIR libraries on the library paths.
     */
    private static boolean containsAIRLibraries(final ImmutableList<String> libraryPaths)
    {
        for (final String path : libraryPaths)
        {
            if (path.equals(SWC_AIRGLOBAL) ||
                path.endsWith("/" + SWC_AIRGLOBAL) ||
                path.endsWith("\\" + SWC_AIRGLOBAL))
            {
                return true;
            }
        }
        return false;
    }

    //
    // 'compiler.generated-directory' option
    // This can only be configured using getter and setter.
    //

    private String generatedDir = null;

    public String getCompilerGeneratedDirectory()
    {
        return generatedDir;
    }

    public void setCompilerGeneratedDirectory(String generatedDir)
    {
        this.generatedDir = generatedDir;
    }

    //
    // 'compiler.headless-server' option
    //

    private boolean headlessServer;

    public boolean isHeadlessServer()
    {
        return headlessServer;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "headless-server"})
    @FlexOnly
    public void setCompilerHeadlessServer(ConfigurationValue cv, boolean headlessServer)
    {
        this.headlessServer = headlessServer;
    }

    //
    // 'compiler.include-libraries' option
    //

    private final List<String> includeLibraries = new ArrayList<String>();

    public List<String> getCompilerIncludeLibraries()
    {
        return includeLibraries;
    }

    /**
     * Links all classes inside a SWC file to the resulting application SWF
     * file, regardless of whether or not they are used.
     * <p>
     * Contrast this option with the library-path option that includes only
     * those classes that are referenced at compile time.
     * <p>
     * To link one or more classes whether or not they are used and not an
     * entire SWC file, use the includes option.
     * <p>
     * This option is commonly used to specify resource bundles.
     */
    @Config(allowMultiple = true, isPath = true)
    @Mapping({"compiler", "include-libraries"})
    @Arguments("library")
    @InfiniteArguments
    public void setCompilerIncludeLibraries(ConfigurationValue cv, String[] pathlist) throws CannotOpen
    {
        final ImmutableList<String> resolvedPaths = expandTokens(
                Arrays.asList(pathlist),
                locales,
                cv, !reportMissingCompilerLibraries);
        includeLibraries.addAll(resolvedPaths);
    }

    //
    // 'compiler.incremental' option
    //

    @Config(removed = true)
    @Mapping({"compiler", "incremental"})
    public void setCompilerIncremental(ConfigurationValue cv, boolean b)
    {
    }

    //
    // 'compiler.keep-all-type-selectors' option. 

    /**
     * This was initially used by Flex Builder when building design view, but
     * they no longer use it.
     */
    private boolean keepAllTypeSelectors;

    public boolean keepAllTypeSelectors()
    {
        return keepAllTypeSelectors;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "keep-all-type-selectors"})
    @FlexOnly
    public void setCompilerKeepAllTypeSelectors(ConfigurationValue cv, boolean keepAllTypeSelectors)
    {
        this.keepAllTypeSelectors = keepAllTypeSelectors;
    }

    //
    // 'compiler.keep-as3-metadata' option
    //

    private Set<String> as3metadata = null;

    public Set<String> getCompilerKeepAs3Metadata()
    {
        return as3metadata == null ? Collections.<String> emptySet() : as3metadata;
    }

    @Config(advanced = true, allowMultiple = true)
    @Mapping({"compiler", "keep-as3-metadata"})
    @Arguments("name")
    @InfiniteArguments
    public void setCompilerKeepAs3Metadata(ConfigurationValue cv, List<String> values)
    {
        if (as3metadata == null)
            as3metadata = new HashSet<String>();
        as3metadata.addAll(values);
    }

    //
    // 'compiler.keep-generated-actionscript' option (removed)
    //

    @Config(removed = true)
    @Mapping({"compiler", "keep-generated-actionscript"})
    public void setCompilerKeepGeneratedActionscript(ConfigurationValue cv, boolean keep)
    {
    }

    //
    // 'compiler.keep-generated-signatures' option (removed)
    //

    @Config(removed = true)
    @Mapping({"compiler", "keep-generated-signatures"})
    public void setCompilerKeepGeneratedSignatures(ConfigurationValue cv, boolean keep)
    {
    }

    //
    // 'compiler.enable-runtime-design-layers' option
    //

    private boolean enableRuntimeDesignLayers = true;

    public boolean getEnableRuntimeDesignLayers()
    {
        return enableRuntimeDesignLayers;
    }

    @Config
    @Mapping({"compiler", "enable-runtime-design-layers"})
    @FlexOnly
    public void setCompilerEnableRuntimeDesignLayers(ConfigurationValue cv, boolean enable)
    {
        enableRuntimeDesignLayers = enable;
    }

    //
    // 'compiler.enable-swc-version-filtering' option
    //

    private boolean enableSwcVersionFiltering = true;

    public boolean getEnableSwcVersionFiltering()
    {
        return enableSwcVersionFiltering;
    }

    @Config(advanced = true, hidden = true)
    @Mapping({"compiler", "enable-swc-version-filtering"})
    public void setCompilerEnableSwcVersionFiltering(ConfigurationValue cv, boolean enable)
    {
        this.enableSwcVersionFiltering = enable;
    }

    //
    // 'compiler.library-path' option
    //

    private final List<String> libraryPath = new ArrayList<String>();
    private boolean reportMissingCompilerLibraries = true;

    /**
     * Sets whether to report missing libraries in the configuration.  If this is false
     * any missing libraries will not be warned about, and the filename will also be added to list
     * of libraries in the project when it doesn't exist.  If reportMissingCompilerLibraries
     * is true, any missing libraries will not be added to the project.
     *
     * @param reportMissingCompilerLibraries true to report missing libraries
     */
    public void setReportMissingCompilerLibraries(boolean reportMissingCompilerLibraries)
    {
        this.reportMissingCompilerLibraries = reportMissingCompilerLibraries;
    }

    public List<String> getCompilerLibraryPath()
    {
        return libraryPath;
    }

    /**
     * Links SWC files to the resulting application SWF file. The compiler only
     * links in those classes for the SWC file that are required. You can
     * specify a directory or individual SWC files.
     */
    @Config(allowMultiple = true, isPath = true)
    @Mapping({"compiler", "library-path"})
    @Arguments(Arguments.PATH_ELEMENT)
    @InfiniteArguments
    @SoftPrerequisites({"locale", "target-player"})
    public void setCompilerLibraryPath(ConfigurationValue cv, String[] pathlist) throws CannotOpen
    {
        final ImmutableList<String> resolvedPaths = expandTokens(
                Arrays.asList(pathlist),
                locales,
                cv, !reportMissingCompilerLibraries);
        libraryPath.addAll(resolvedPaths);
    }

    //
    // 'compiler.locale' option
    //

    private final List<String> locales = new ArrayList<String>();

    public List<String> getCompilerLocales()
    {
        return locales;
    }

    /**
     * Specifies one or more locales to be compiled into the SWF file. If you do
     * not specify a locale, then the compiler uses the default locale from the
     * flex-config.xml file. The default value is en_US. You can append
     * additional locales to the default locale by using the += operator. If you
     * remove the default locale from the flex-config.xml file, and do not
     * specify one on the command line, then the compiler will use the machine's
     * locale.
     */
    @Config(allowMultiple = true)
    @Mapping({"compiler", "locale"})
    @Arguments("locale-element")
    @InfiniteArguments
    @FlexOnly
    public void setCompilerLocale(ConfigurationValue cv, String[] newLocales)
    {
        locales.addAll(Arrays.asList(newLocales));
    }

    //
    // 'compiler.metadata-export' option (incomplete)
    //

    private boolean metadataExport;

    public boolean metadataExport()
    {
        return metadataExport;
    }

    // metadataExport does not have the normal configuration setCompilerter because it is not
    // a normal configuration value but rather something setCompiler by the compiler
    public void setCompilerMetadataExport(boolean metadataExport)
    {
        this.metadataExport = metadataExport;
    }

    //
    // 'compiler.mxml.children-as-data' option
    //
    private Boolean childrenAsData = false;
   
    public Boolean getCompilerMxmlChildrenAsData()
    {
        return childrenAsData;
    }

    @Config
    @Mapping({"compiler", "mxml", "children-as-data"})
    @FlexOnly
    public void setCompilerMxmlChildrenAsData(ConfigurationValue cv, Boolean asData) throws ConfigurationException
    {
        childrenAsData = asData;
    }

    //
    // 'compiler.mxml.implicitImports' option
    //
    private String[] implicitImports;
   
    public String[] getCompilerMxmlImplicitImports()
    {
        return implicitImports;
    }

    @Config(allowMultiple = true)
    @Mapping({"compiler", "mxml", "imports"})
    @Arguments("implicit-import")
    @InfiniteArguments
    @FlexOnly
    public void setCompilerMxmlImplicitImports(ConfigurationValue cv, String[] imports) throws ConfigurationException
    {
        implicitImports = imports;
    }

    //
    // 'compiler.mxml.compatibility-version' option
    //

    public String getCompilerCompatibilityVersionString()
    {
        return getCompilerMxmlCompatibilityVersionString();
    }

    public int getCompilerCompatibilityVersion()
    {
        return getCompilerMxmlCompatibilityVersion();
    }

    //
    // 'compiler.mxml.minimum-supported-version' option
    //

    public String getCompilerMinimumSupportedVersionString()
    {
        return getCompilerMxmlMinimumSupportedVersionString();
    }

    public int getCompilerMinimumSupportedVersion()
    {
        return getCompilerMxmlMinimumSupportedVersion();
    }

    @Config
    @Mapping({"compiler", "minimum-supported-version"})
    @FlexOnly
    public void setCompilerMinimumSupportedVersion(ConfigurationValue cv, String version) throws ConfigurationException
    {
        setCompilerMxmlMinimumSupportedVersion(cv, version);
    }

    //
    // 'qualified-type-selectors' option
    //
    @Config(advanced = true, removed = true)
    @Mapping({"compiler", "mxml", "qualified-type-selectors"})
    public void setCompilerMxmlQualifiedTypeSelectors(ConfigurationValue cv, boolean b)
    {
    }

    //
    // 'compiler.omit-trace-statements' option
    //

    private boolean omitTraceStatements = true;

    public boolean omitTraceStatements()
    {
        return omitTraceStatements;
    }

    @Config
    @Mapping({"compiler", "omit-trace-statements"})
    public void setCompilerOmitTraceStatements(ConfigurationValue cv, boolean b)
    {
        omitTraceStatements = b;
    }

    //
    // 'compiler.optimize' option
    //

    private boolean optimize = false;

    public boolean optimize()
    {
        return optimize;
    }

    public boolean getCompilerOptimize()
    {
        return optimize;
    }

    @Config
    @Mapping({"compiler", "optimize"})
    public void setCompilerOptimize(ConfigurationValue cv, boolean b)
    {
        optimize = b;
    }
   
   
    //
    // 'compiler.preloader' option
    //

    private String preloader = null;

    /**
     *
     * @return Returns the preloader class configured by the user. If the
     * user did not configure a preloader, the
     * "mx.preloader.DownloaderProgressBar" preloader will be returned if the
     * compatibility version is less than 4.0. Otherwise the
     * "mx.preloaders.SparkDownloadProgressBar" preloader will be returned.
     */
    public String getPreloader()
    {
        if (preloader != null)
            return preloader;
       
        if (getCompilerMxmlCompatibilityVersion() < MXML_VERSION_4_0)
            return IMXMLTypeConstants.DownloadProgressBar;
        else
            return IMXMLTypeConstants.SparkDownloadProgressBar;
    }
   
    public String getCompilerPreloader()
    {
        return preloader;
    }
   
    @Config
    @Mapping({"compiler", "preloader"})
    @FlexOnly
    public void setCompilerPreloader(ConfigurationValue cv, String value)
    {
        preloader = value;
    }

    //
    // 'compiler.services' option
    //

    private File servicesConfigFile;

    //protected ServicesDependencies servicesDependencies;

    public File getCompilerServices()
    {
        return servicesConfigFile;
    }

    /**
     * Used by the compiler to record the client dependencies from the Flex Data
     * Services configuration file.
     */
    /*
     * public ServicesDependencies getCompilerServicesDependencies() { if
     * (servicesDependencies == null && servicesConfigFile != null) { String
     * servicesPath = servicesConfigFile.getName(); servicesDependencies = new
     * ServicesDependencies(servicesPath, null, getCompilerContextRoot()); }
     * return servicesDependencies; } public void
     * setCompilerServicesDependencies(ServicesDependencies deps) {
     * servicesDependencies = deps; }
     */

    @Config
    @Mapping({"compiler", "services"})
    @Arguments("filename")
    @FlexOnly
    public void setCompilerServices(ConfigurationValue cv, String servicesPath) throws ConfigurationException
    {
        try
        {
            servicesConfigFile = new File(resolvePathStrict(servicesPath, cv));
        }
        catch (Throwable t)
        {
            throw new ConfigurationException.CannotOpen(servicesPath, cv.getVar(), cv.getSource(), cv.getLine());
        }
    }

    //
    // 'compiler.show-actionscript-warnings' option
    //

    /**
     * Enable asc -warnings
     */
    private boolean ascWarnings;

    public boolean warnings()
    {
        return this.ascWarnings;
    }

    @Config
    @Mapping({"compiler", "show-actionscript-warnings"})
    public void setCompilerShowActionscriptWarnings(ConfigurationValue cv, boolean ascWarnings)
    {
        this.ascWarnings = ascWarnings;
    }

    //
    // 'compiler.show-binding-warnings' option
    //

    /**
     * Controls whether binding warnings are displayed.
     */
    private boolean showBindingWarnings = true;

    public boolean showBindingWarnings()
    {
        return showBindingWarnings;
    }

    @Config
    @Mapping({"compiler", "show-binding-warnings"})
    public void setCompilerShowBindingWarnings(ConfigurationValue cv, boolean show)
    {
        this.showBindingWarnings = show;
    }

    @Config
    @Mapping({"compiler", "show-multiple-definition-warnings"})
    public void setCompilerShowMultipleDefinitionWarnings(ConfigurationValue cv, boolean show)
    {
        this.showMultipleDefinitionWarnings = show;
    }
    //
    // 'compiler.show-dependency-warnings' option (hidden)
    //

    private boolean showDependencyWarnings = false;

    public boolean showDependencyWarnings()
    {
        return showDependencyWarnings;
    }

    @Config(advanced = true, hidden = true)
    @Mapping({"compiler", "show-dependency-warnings"})
    public void setCompilerShowDependencyWarnings(ConfigurationValue cv, boolean show)
    {
        this.showDependencyWarnings = show;
    }

    //
    // 'compiler.report-invalid-styles-as-warnings' option
    //
   
    /**
     * Controls whether invalid styles are report as errors or warnings.
     */
    private boolean reportInvalidStylesAsWarnings = false;

    /**
     * Get value of {@code compiler.report-invalid-styles-as-warnings} option
     * value.
     * <p>
     * <h2>What's "invalid styles"?</h2> The term "invalid style" only applies
     * to MXML style specifier (a.k.a. inline style). If a style of a component
     * is defined with "theme" attribute, the style is only effective with such
     * theme. If a theme-specific style is used in an application who doesn't
     * use the required theme, the style is considered invalid.
     * <p>
     * For example, style "fooStyle" is defined to used only with theme called
     * "fooTheme":
     *
     * <pre>
     * [Style(name="fooStyle", type="uint", format="Color", inherit="yes", theme="fooTheme")]
     * public class MyComponent extends UIComponent
     * </pre>
     *
     * If "fooTheme" isn't used by the current application, the following style
     * specifier is considered "invalid styles". <br>
     * {@code <local:MyComponent fooStyle="white" />}
     *
     * @return True if invalid styles are reported as warnings instead of
     * errors.
     */
    public boolean getReportInvalidStylesAsWarnings()
    {
        return reportInvalidStylesAsWarnings;
    }

    @Config
    @Mapping({"compiler", "report-invalid-styles-as-warnings"})
    @FlexOnly
    public void setCompilerReportInvalidStylesAsWarnings(ConfigurationValue cv, boolean show)
    {
        this.reportInvalidStylesAsWarnings = show;
    }

    //
    // 'compiler.report-missing-required-skin-parts-as-warnings' option
    //

    private boolean reportMissingRequiredSkinPartsAsWarnings = false;

    /**
     * Allow the user to configure whether it should be considered an error to
     * not create a required skin part or if it should just be a warning.
     */
    public boolean reportMissingRequiredSkinPartsAsWarnings()
    {
        return reportMissingRequiredSkinPartsAsWarnings;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "report-missing-required-skin-parts-as-warnings"})
    @FlexOnly
    public void setCompilerReportMissingRequiredSkinPartsAsWarnings(ConfigurationValue cv, boolean b)
    {
        reportMissingRequiredSkinPartsAsWarnings = b;
    }

    //
    // 'compiler.show-invalid-css-property-warnings' option
    //
   
    private boolean showInvalidCSSPropertyWarnings = true;

    /**
     * Controls whether warnings are displayed when styles, which don't apply to
     * the current theme(s), are used in CSS.
     * <p>
     * See {@link #getReportInvalidStylesAsWarnings()} for definition of
     * "invalid style".
     * <p>
     * This option applies to <i>invalid styles</i> in a {@code <fx:Style>}
     * block.
     */
    public boolean getShowInvalidCSSPropertyWarnings()
    {
        return showInvalidCSSPropertyWarnings;
    }

    @Config
    @Mapping({"compiler", "show-invalid-css-property-warnings"})
    @FlexOnly
    public void setShowInvalidCssPropertyWarnings(ConfigurationValue cv, boolean show)
    {
        this.showInvalidCSSPropertyWarnings = show;
    }

    //
    // 'compiler.show-deprecation-warnings' option
    //

    /**
     * Controls whether warnings are displayed when a deprecated API is used.
     */
    private boolean showDeprecationWarnings = false;

    public boolean showDeprecationWarnings()
    {
        return showDeprecationWarnings;
    }

    @Config(advanced = true, hidden = true)
    @Mapping({"compiler", "show-deprecation-warnings"})
    public void setCompilerShowDeprecationWarnings(ConfigurationValue cv, boolean show)
    {
        this.showDeprecationWarnings = show;
    }

    //
    // 'compiler.show-shadowed-device-font-warnings' option
    //
    @Config
    @Mapping({"compiler", "show-shadowed-device-font-warnings"})
    @FlexOnly
    public void setCompilerShowShadowedDeviceFontWarnings(ConfigurationValue cv, boolean show)
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    //
    // 'compiler.show-unused-type-selector-warnings' option
    //

    private boolean showUnusedTypeSelectorWarnings = true;

    public boolean showUnusedTypeSelectorWarnings()
    {
        return showUnusedTypeSelectorWarnings;
    }

    //
    // 'compiler.show-multiple-definition-warnings' option
    //

    private boolean showMultipleDefinitionWarnings = true;

    public boolean showMultipleDefinitionWarnings()
    {
        return showMultipleDefinitionWarnings;
    }
   
    @Config
    @Mapping({"compiler", "show-unused-type-selector-warnings"})
    @FlexOnly
    public void setCompilerShowUnusedTypeSelectorWarnings(ConfigurationValue cv, boolean show)
    {
        this.showUnusedTypeSelectorWarnings = show;
    }

    //
    // 'compiler.source-path' option
    //

    /**
     * Source path elements searched for ActionScript class files, possibly
     * containing a {locale} token.
     */
    private final List<String> unexpandedSourcePath = new ArrayList<String>();

    /**
     * Directories searched for ActionScript class files. The specified
     * compiler.source-path can have path elements which contain a special
     * {locale} token. If you compile for a single locale, this token is
     * replaced by the specified locale. If you compile for multiple locales,
     * any path element with the {locale} token is ignored, because we do not
     * support compiling, for example, both en_US and ja_JP versions of
     * MyComponent into the same SWF. A path element with {locale} is similarly
     * ignored if you compile for no locale.
     */
    private final List<String> sourcePath = new ArrayList<String>();

    /** Context object for "source-path" option. */
    private ConfigurationValue sourcePathContext = null;

    public List<String> getCompilerSourcePath()
    {
        return sourcePath;
    }

    /**
     * Get the source paths computed from the given {@code locale}. The locale
     * must be included in the configuration.
     *
     * @param locale Locale name.
     * @return Source paths computed from the given {@code locale}.
     * @throws CannotOpen Error resolving one of the paths from this locale.
     */
    public ImmutableList<String> getCompilerResourceBundlePathForLocale(String locale) throws CannotOpen
    {
        assert locales.contains(locale) : "Locale is not configured: " + locale;
        return expandTokens(unexpandedSourcePath, ImmutableSet.of(locale),
                sourcePathContext, !reportMissingCompilerLibraries);
    }

    @Config(allowMultiple = true)
    @Arguments(Arguments.PATH_ELEMENT)
    @SoftPrerequisites("locale")
    public void setCompilerSourcePath(ConfigurationValue cv, String[] paths) throws ConfigurationException
    {
        final List<String> pathList = Arrays.asList(paths);
        unexpandedSourcePath.addAll(pathList);

        final ImmutableList<String> resolvedSourcePaths = expandTokens(pathList, locales, cv);
        assertThatAllPathsAreDirectories(resolvedSourcePaths, cv);
        sourcePath.addAll(resolvedSourcePaths);

        sourcePathContext = cv;
    }

    /**
     * Check that all paths in the path list are directories.
     *
     * @param paths A list of paths.
     * @param cv Context.
     * @throws NotDirectory Path is not a directory exception.
     */
    public static void assertThatAllPathsAreDirectories(
            final List<String> paths,
            final ConfigurationValue cv)
            throws NotDirectory
    {
        assert paths != null : "Expected path list.";
        assert cv != null : "Expected ConfigurationValue as context.";

        for (final String path : paths)
        {
            final File file = new File(path);
            if (!file.isDirectory())
                throw new NotDirectory(path, cv.getVar(), cv.getSource(), cv.getLine());
        }
    }

    public static ConfigurationInfo getCompilerSourcePathInfo()
    {
        return new ConfigurationInfo(-1, new String[] {"path-element"})
        {
            @Override
            public boolean allowMultiple()
            {
                return true;
            }

            @Override
            public boolean isPath()
            {
                return true;
            }
        };
    }

    //
    // 'compiler.strict' option
    //

    /**
     * Run the AS3 compiler in strict mode
     */
    private boolean strict;

    public boolean strict()
    {
        return this.strict;
    }

    @Config
    @Mapping({"compiler", "strict"})
    public void setCompilerStrict(ConfigurationValue cv, boolean strict)
    {
        this.strict = strict;
    }

    //
    // 'compiler.suppress-warnings-in-incremental' option (incomplete)
    //

    // for Zorn
    //
    // When doing incremental compilation, the compiler doesn't recompile codes that are previously compiled with
    // warnings. It only outputs the warning messages so as to remind users of the warnings.
    //
    // The command-line tool and Zorn work differently in that Zorn keeps the warning logger while the commnad-line
    // tool, of course, can't keep the warning logger alive...
    //
    // Zorn needs this flag to tell the compiler not to output warnings again in incremental compilations because
    // it keeps its own log.
    private boolean suppressWarningsInIncremental = false;

    public boolean suppressWarningsInIncremental()
    {
        return suppressWarningsInIncremental;
    }

    public void setCompilerSuppressWarningsInIncremental(boolean b)
    {
        suppressWarningsInIncremental = b;
    }

    //
    // 'compiler.theme' option
    //

    private List<String> themeFiles = null;

    /**
     * Get normalized theme file paths. If a the compiler is in
     * "Flex 3 compatibility" mode and only "Spark" theme is used, it will be
     * replaced with the legacy "Halo" theme.
     *
     * @return A list of normalized paths to the theme files.
     */
    public List<String> getCompilerThemeFiles()
    {
        if (themeFiles == null)
            return EMPTY_STRING_LIST;

        final boolean isVersion3OrEarlier = getCompilerMxmlCompatibilityVersion() <= MXML_VERSION_3_0;
        final boolean hasOnlyOneThemeFile = themeFiles.size() == 1;
        if (isVersion3OrEarlier && hasOnlyOneThemeFile)
        {
            // Swap in the default Flex 3 theme of Halo.
            final String path = FilenameUtils.normalize(themeFiles.get(0), true);
            final String sparkPath = "/themes/Spark/spark.css";

            if (path.endsWith(sparkPath))
            {
                int index = path.indexOf(sparkPath);
                final String haloPath = path.substring(0, index) + "/themes/Halo/halo.swc";
                themeFiles.set(0, FilenameNormalization.normalize(haloPath));
            }
        }
        return themeFiles;
    }

    @Config(allowMultiple = true)
    @Mapping({"compiler", "theme"})
    @Arguments("filename")
    @InfiniteArguments
    @FlexOnly
    public void setCompilerTheme(ConfigurationValue cv, List<String> paths) throws CannotOpen
    {
        // Use "resolvePathsStrict()" if invalid theme file can't be ignored.
        final ImmutableList<String> resolved = resolvePathsStrict(ImmutableList.copyOf(paths), cv);

        if (themeFiles == null)
            themeFiles = new ArrayList<String>();
        themeFiles.addAll(resolved);
    }

    //
    // 'compiler.defaults-css-files' option
    //

    private Deque<String> defaultsCSSFiles = new ArrayDeque<String>();

    /**
     * List of filenames of defaults style stylesheets (css only).
     * <p>
     * <b>For example:</b><br>
     * <code>-defaults-css-files=[A, B, C]</code><br>
     * Then, 'A' should have precedence over 'B', then 'C', then SWCs
     * defaultsCssFiles should have the order: SWCS, C, B, A
     *
     * @see #setDefaultsCSSFiles
     */
    public Deque<String> getDefaultsCSSFiles()
    {
        return defaultsCSSFiles;
    }

    /**
     * Inserts CSS files into the output the same way that a per-SWC
     * defaults.css file works, but without having to re-archive the SWC file to
     * test each change.
     * <p>
     * CSS files included in the output with this option have a higher
     * precedence than default CSS files in existing SWCs. For example, a CSS
     * file included with this option overrides definitions in framework.swc's
     * defaults.css file, but it has the same overall precedence as other
     * included CSS files inside the SWC file.
     * <p>
     * This option does not actually insert the CSS file into the SWC file; it
     * simulates it. When you finish developing the CSS file, you should rebuild
     * the SWC file with the new integrated CSS file.
     * <p>
     * This option takes one or more files. The precedence for multiple CSS
     * files included with this option is from first to last.
     */
    @Config(allowMultiple = true, advanced = true)
    @Mapping({"compiler", "defaults-css-files"})
    @Arguments("filename")
    @InfiniteArguments
    @FlexOnly
    public void setDefaultsCSSFiles(ConfigurationValue cv, List<String> paths) throws CannotOpen
    {
        final ImmutableList<String> resolved = resolvePathsStrict(ImmutableList.copyOf(paths), cv);
        for (final String path : resolved)
        {
            defaultsCSSFiles.addFirst(path);
        }
    }
   
    /**
     * Location of theme style stylesheets (css only, configured via themefiles
     * above).
     */
    private List<IFileSpecification> themeCssFiles = new LinkedList<IFileSpecification>();

    public List<IFileSpecification> getCompilerThemeCssFiles()
    {
        return themeCssFiles;
    }

    public void addThemeCssFiles(List<IFileSpecification> files)
    {
        themeCssFiles.addAll(files);
    }

    //
    // 'compiler.use-resource-bundle-metadata' option
    //

    @Config(removed = true)
    @Mapping({"compiler", "use-resource-bundle-metadata"})
    public void setCompilerUseResourceBundleMetadata(ConfigurationValue cv, boolean b)
    {
    }

    //
    // 'compiler.verbose-stacktraces' option
    //

    private boolean verboseStacktraces;

    // from as3 and mxml configuration interface
    public boolean debug()
    {
        // the debug() in as3 and mxml configuration maps to stacktraceLineNumbers
        return verboseStacktraces;
    }

    @Config
    @Mapping({"compiler", "verbose-stacktraces"})
    public void setCompilerVerboseStacktraces(ConfigurationValue cv, boolean verboseStacktraces)
    {
        if (generateDebugTags)
        {
            this.verboseStacktraces = true;
        }
        else
        {
            this.verboseStacktraces = verboseStacktraces;
        }
    }

    //
    // 'compiler.warn-array-tostring-changes' option
    //

    private boolean warn_array_tostring_changes = false;

    public boolean warn_array_tostring_changes()
    {
        return warn_array_tostring_changes;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-array-tostring-changes"})
    public void setCompilerWarnArrayTostringChanges(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_array_tostring_changes)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-assignment-within-conditional' option
    //

    private boolean warn_assignment_within_conditional = true;

    public boolean warn_assignment_within_conditional()
    {
        return warn_assignment_within_conditional;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-assignment-within-conditional"})
    public void setCompilerWarnAssignmentWithinConditional(ConfigurationValue cv, boolean b)
    {
        warn_assignment_within_conditional = b;
    }

    //
    // 'compiler.warn-bad-array-cast' option
    //

    private boolean warn_bad_array_cast = true;

    public boolean warn_bad_array_cast()
    {
        return warn_bad_array_cast;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-array-cast"})
    public void setCompilerWarnBadArrayCast(ConfigurationValue cv, boolean b)
    {
        warn_bad_array_cast = b;
    }

    //
    // 'compiler.warn-bad-bool-assignment' option
    //

    private boolean warn_bad_bool_assignment = true;

    public boolean warn_bad_bool_assignment()
    {
        return warn_bad_bool_assignment;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-bool-assignment"})
    public void setCompilerWarnBadBoolAssignment(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_bad_bool_assignment)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-bad-date-cast' option
    //

    private boolean warn_bad_date_cast = true;

    public boolean warn_bad_date_cast()
    {
        return warn_bad_date_cast;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-date-cast"})
    public void setCompilerWarnBadDateCast(ConfigurationValue cv, boolean b)
    {
        warn_bad_date_cast = b;
    }

    //
    // 'compiler.warn-bad-es3-type-method' option
    //

    private boolean warn_bad_es3_type_method = true;

    public boolean warn_bad_es3_type_method()
    {
        return warn_bad_es3_type_method;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-es3-type-method"})
    public void setCompilerWarnBadEs3TypeMethod(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_bad_es3_type_method)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-bad-es3-type-prop' option
    //

    private boolean warn_bad_es3_type_prop = true;

    public boolean warn_bad_es3_type_prop()
    {
        return warn_bad_es3_type_prop;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-es3-type-prop"})
    public void setCompilerWarnBadEs3TypeProp(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_bad_es3_type_prop)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-bad-nan-comparison' option
    //

    private boolean warn_bad_nan_comparison = true;

    public boolean warn_bad_nan_comparison()
    {
        return warn_bad_nan_comparison;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-nan-comparison"})
    public void setCompilerWarnBadNanComparison(ConfigurationValue cv, boolean b)
    {
        warn_bad_nan_comparison = b;
    }

    //
    // 'compiler.warn-bad-null-assignment' option
    //

    private boolean warn_bad_null_assignment = true;

    public boolean warn_bad_null_assignment()
    {
        return warn_bad_null_assignment;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-null-assignment"})
    public void setCompilerWarnBadNullAssignment(ConfigurationValue cv, boolean b)
    {
        warn_bad_null_assignment = b;
    }

    //
    // 'compiler.warn-bad-null-comparison' option
    //

    private boolean warn_bad_null_comparison = true;

    public boolean warn_bad_null_comparison()
    {
        return warn_bad_null_comparison;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-null-comparison"})
    public void setCompilerWarnBadNullComparison(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_bad_null_comparison)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-bad-undefined-comparison' option
    //

    private boolean warn_bad_undefined_comparison = true;

    public boolean warn_bad_undefined_comparison()
    {
        return warn_bad_undefined_comparison;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-bad-undefined-comparison"})
    public void setCompilerWarnBadUndefinedComparison(ConfigurationValue cv, boolean b)
    {
        warn_bad_undefined_comparison = b;
    }

    //
    // 'compiler.warn-boolean-constructor-with-no-args' option
    //

    private boolean warn_boolean_constructor_with_no_args = false;

    public boolean warn_boolean_constructor_with_no_args()
    {
        return warn_boolean_constructor_with_no_args;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-boolean-constructor-with-no-args"})
    public void setCompilerWarnBooleanConstructorWithNoArgs(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_boolean_constructor_with_no_args)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-changes-in-resolve' option
    //

    private boolean warn_changes_in_resolve = false;

    public boolean warn_changes_in_resolve()
    {
        return warn_changes_in_resolve;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-changes-in-resolve"})
    public void setCompilerWarnChangesInResolve(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_changes_in_resolve)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-class-is-sealed' option
    //

    private boolean warn_class_is_sealed = true;

    public boolean warn_class_is_sealed()
    {
        return warn_class_is_sealed;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-class-is-sealed"})
    public void setCompilerWarnClassIsSealed(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_class_is_sealed)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-const-not-initialized' option
    //

    private boolean warn_const_not_initialized = true;

    public boolean warn_const_not_initialized()
    {
        return warn_const_not_initialized;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-const-not-initialized"})
    public void setCompilerWarnConstNotInitialized(ConfigurationValue cv, boolean b)
    {
        warn_const_not_initialized = b;
    }

    //
    // 'compiler.warn-constructor-returns-value' option
    //

    private boolean warn_constructor_returns_value = false;

    public boolean warn_constructor_returns_value()
    {
        return warn_constructor_returns_value;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-constructor-returns-value"})
    public void setCompilerWarnConstructorReturnsValue(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_constructor_returns_value)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-deprecated-event-handler-error' option
    //

    private boolean warn_deprecated_event_handler_error = false;

    public boolean warn_deprecated_event_handler_error()
    {
        return warn_deprecated_event_handler_error;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-deprecated-event-handler-error"})
    public void setCompilerWarnDeprecatedEventHandlerError(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_deprecated_event_handler_error)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-deprecated-function-error' option
    //

    private boolean warn_deprecated_function_error = true;

    public boolean warn_deprecated_function_error()
    {
        return warn_deprecated_function_error;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-deprecated-function-error"})
    public void setCompilerWarnDeprecatedFunctionError(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_deprecated_function_error)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-deprecated-property-error' option
    //

    private boolean warn_deprecated_property_error = true;

    public boolean warn_deprecated_property_error()
    {
        return warn_deprecated_property_error;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-deprecated-property-error"})
    public void setCompilerWarnDeprecatedPropertyError(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_deprecated_property_error)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-duplicate-argument-names' option
    //

    private boolean warn_duplicate_argument_names = true;

    public boolean warn_duplicate_argument_names()
    {
        return warn_duplicate_argument_names;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-duplicate-argument-names"})
    public void setCompilerWarnDuplicateArgumentNames(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_duplicate_argument_names)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-duplicate-variable-def' option
    //

    private boolean warn_duplicate_variable_def = true;

    public boolean warn_duplicate_variable_def()
    {
        return warn_duplicate_variable_def;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-duplicate-variable-def"})
    public void csetCompilerWarnDuplicateVariableDef(ConfigurationValue cv, boolean b)
    {
        warn_duplicate_variable_def = b;
    }

    //
    // 'compiler.warn-for-var-in-changes' option
    //

    private boolean warn_for_var_in_changes = false;

    public boolean warn_for_var_in_changes()
    {
        return warn_for_var_in_changes;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-for-var-in-changes"})
    public void setCompilerWarnForVarInChanges(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_for_var_in_changes)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-import-hides-class' option
    //

    private boolean warn_import_hides_class = true;

    public boolean warn_import_hides_class()
    {
        return warn_import_hides_class;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-import-hides-class"})
    public void setCompilerWarnImportHidesClass(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_import_hides_class)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-instance-of-changes' option
    //

    private boolean warn_instance_of_changes = true;

    public boolean warn_instance_of_changes()
    {
        return warn_instance_of_changes;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-instance-of-changes"})
    public void setCompilerWarnInstanceOfChanges(ConfigurationValue cv, boolean b)
    {
        warn_instance_of_changes = b;
    }

    //
    // 'compiler.warn-internal-error' option
    //

    private boolean warn_internal_error = true;

    public boolean warn_internal_error()
    {
        return warn_internal_error;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-internal-error"})
    public void setCompilerWarnInternalError(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_internal_error)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-level-not-supported' option
    //

    private boolean warn_level_not_supported = true;

    public boolean warn_level_not_supported()
    {
        return warn_level_not_supported;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-level-not-supported"})
    public void setCompilerWarnLevelNotSupported(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_level_not_supported)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-missing-namespace-decl' option
    //

    private boolean warn_missing_namespace_decl = true;

    public boolean warn_missing_namespace_decl()
    {
        return warn_missing_namespace_decl;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-missing-namespace-decl"})
    public void setCompilerWarnMissingNamespaceDecl(ConfigurationValue cv, boolean b)
    {
        warn_missing_namespace_decl = b;
    }

    //
    // 'compiler.warn-negative-uint-literal' option
    //

    private boolean warn_negative_uint_literal = true;

    public boolean warn_negative_uint_literal()
    {
        return warn_negative_uint_literal;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-negative-uint-literal"})
    public void setCompilerWarnNegativeUintLiteral(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_negative_uint_literal)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-no-constructor' option
    //

    private boolean warn_no_constructor = false;

    public boolean warn_no_constructor()
    {
        return warn_no_constructor;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-no-constructor"})
    public void setCompilerWarnNoConstructor(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_no_constructor)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-no-explicit-super-call-in-constructor' option
    //

    private boolean warn_no_explicit_super_call_in_constructor = false;

    public boolean warn_no_explicit_super_call_in_constructor()
    {
        return warn_no_explicit_super_call_in_constructor;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-no-explicit-super-call-in-constructor"})
    public void setCompilerWarnNoExplicitSuperCallInConstructor(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_no_explicit_super_call_in_constructor)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-no-type-decl' option
    //

    private boolean warn_no_type_decl = true;

    public boolean warn_no_type_decl()
    {
        return warn_no_type_decl;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-no-type-decl"})
    public void setCompilerWarnNoTypeDecl(ConfigurationValue cv, boolean b)
    {
        warn_no_type_decl = b;
    }

    //
    // 'compiler.warn-number-from-string-changes' option
    //

    private boolean warn_number_from_string_changes = false;

    public boolean warn_number_from_string_changes()
    {
        return warn_number_from_string_changes;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-number-from-string-changes"})
    public void setCompilerWarnNumberFromStringChanges(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_number_from_string_changes)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-scoping-change-in-this' option
    //

    private boolean warn_scoping_change_in_this = false;

    public boolean warn_scoping_change_in_this()
    {
        return warn_scoping_change_in_this;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-scoping-change-in-this"})
    public void setCompilerWarnScopingChangeInThis(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_scoping_change_in_this)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-slow-text-field-addition' option
    //

    private boolean warn_slow_text_field_addition = true;

    public boolean warn_slow_text_field_addition()
    {
        return warn_slow_text_field_addition;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-slow-text-field-addition"})
    public void setCompilerWarnSlowTextFieldAddition(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_slow_text_field_addition)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-unlikely-function-value' option
    //

    private boolean warn_unlikely_function_value = true;

    public boolean warn_unlikely_function_value()
    {
        return warn_unlikely_function_value;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-unlikely-function-value"})
    public void setCompilerWarnUnlikelyFunctionValue(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_unlikely_function_value)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // 'compiler.warn-xml-class-has-changed' option
    //

    private boolean warn_xml_class_has_changed = false;

    public boolean warn_xml_class_has_changed()
    {
        return warn_xml_class_has_changed;
    }

    @Config(advanced = true)
    @Mapping({"compiler", "warn-xml-class-has-changed"})
    public void setCompilerWarnXmlClassHasChanged(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != warn_xml_class_has_changed)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // compiler.generate-abstract-syntax-tree
    //

    private boolean generateAbstractSyntaxTree = true;

    @Config(hidden = true)
    @Mapping({"compiler", "generate-abstract-syntax-tree"})
    public void setCompilerGenerateAbstractSyntaxTree(ConfigurationValue cv, boolean b)
    {
        generateAbstractSyntaxTree = b;
    }

    public boolean getCompilerGenerateAbstractSyntaxTree()
    {
        return generateAbstractSyntaxTree;
    }

    //
    // 'compiler.isolateStyles' option
    //

    /**
     * Allow the user to decide if the compiled application/module should have
     * its own style manager. Turn off isolate styles for compatibility less
     * than 4.0.
     */
    private boolean isolateStyles = true;

    public boolean getCompilerIsolateStyles()
    {
        return isolateStyles &&
               (getCompilerCompatibilityVersion() >= MXML_VERSION_4_0);
    }

    @Config(advanced = true)
    @Mapping({"compiler", "isolate-styles"})
    @FlexOnly
    public void setCompilerIsolateStyles(ConfigurationValue cv, boolean isolateStyles)
    {
        this.isolateStyles = isolateStyles;
    }

    //
    // 'compiler.compress' option (default is true)
    //
   
    private boolean useCompression = true;
   
    @Config
    @Mapping({"compiler", "compress"})
    public void setCompress( ConfigurationValue cv, boolean useCompression )
    {
        this.useCompression = useCompression;
    }
   
    /**
     * Setting {@code -compiler.compress=false} will force compiler not to compress the output SWF.
     */
    public boolean useCompression()
    {
        return this.useCompression;
    }
   
    // ATTENTION: Please set default values in DefaultsConfigurator.

    private static final String LOCALE_TOKEN = "{locale}";
    private static final String TARGET_PLAYER_MAJOR_VERSION_TOKEN = "{targetPlayerMajorVersion}";
    private static final String TARGET_PLAYER_MINOR_VERSION_TOKEN = "{targetPlayerMinorVersion}";
    private static final String TARGET_PLAYER_MAJOR_VERSION_TOKEN_REGEX_ESCAPED = Pattern.quote(TARGET_PLAYER_MAJOR_VERSION_TOKEN);
    private static final String TARGET_PLAYER_MINOR_VERSION_TOKEN_REGEX_ESCAPED = Pattern.quote(TARGET_PLAYER_MINOR_VERSION_TOKEN);
   
    // Special Case for Apache.  These are not currently exposed with command line options.
    public static final String PLAYERGLOBAL_HOME_TOKEN = "{playerglobalHome}";
    public static final String AIR_HOME_TOKEN = "{airHome}";

    public static final String STRICT = "compiler.strict";
    public static final String AS3 = "compiler.as3";
    public static final String ES = "compiler.es";

    public ImmutableList<String> expandTokens(
            final Iterable<String> pathElements,
            final Iterable<String> locales,
            final ConfigurationValue configurationValue)
    {
        return expandTokens(pathElements, locales, configurationValue, false);
    }

    /**
     * All path-tokens get expanded from this method, as of now, {locale} and
     * {targetPlayerMajorVersion} Replaces instances of
     * "{targetPlayerMajorVersion}" and "{targetPlayerMinorVersion}" with
     * configured value. Expands the {locale} token in a list of path elements
     * for the source-path or library-path. The treatment of a path element
     * containing "{locale}" depends on whether we are processing a source-path
     * or a library-path, and on whether we are compiling for a single locale,
     * multiple locales, or no locale:
     *
     * <pre>
     * -source-path=foo,bar/{locale},baz -locale=en_US
     * -> foo,bar/en_US,baz
     *
     * -source-path=foo,bar/{locale},baz -locale=en_US,ja_JP
     * -> foo,bar/en_US,bar/ja_JP,baz
     *
     * -source-path=foo,bar/{locale},baz -locale=
     * -> foo,baz
     *
     * -library-path=foo,bar/{locale},baz -locale=en_US
     * -> foo,bar/en_US,baz
     *
     * -library-path=foo,bar/{locale},baz -locale=en_US,ja_JP
     * -> foo,bar/en_US,bar/ja_JP,baz
     *
     * -library-path=foo,bar/{locale},baz -locale=
     * -> foo,baz
     * </pre>
     *
     * @param pathElements A list of unprocessed paths from configuration
     * values.
     * @param locales A set of locales.
     * @param configurationValue Context.
     * @param returnMissingFiles controls whether or not files that do not
     * exist are included in the list of expanded files. Pass true to include
     * files that do not exist, false otherwise.
     * @return A list of normalized and resolved file paths.
     * @throws CannotOpen
     */
    protected ImmutableList<String> expandTokens(
            final Iterable<String> pathElements,
            final Iterable<String> locales,
            final ConfigurationValue configurationValue,
            final boolean returnMissingFiles)
    {
        assert pathElements != null : "Expected path list.";
        assert locales != null : "Expected locales.";
        assert configurationValue != null : "Expected ConfigurationValue as a context.";
       
        String targetPlayerMajorVersion = String.valueOf(getTargetPlayerMajorVersion());
        String targetPlayerMinorVersion = String.valueOf(getTargetPlayerMinorVersion());

        // Expand target player and locale tokens.
        final ImmutableList.Builder<String> resolvedPaths = new ImmutableList.Builder<String>();
        for (String pathElement : pathElements)
        {
            pathElement = expandRuntimeTokens(pathElement);
           
            String playerExpandedPath = pathElement
                    .replaceAll(TARGET_PLAYER_MAJOR_VERSION_TOKEN_REGEX_ESCAPED, targetPlayerMajorVersion)
                    .replaceAll(TARGET_PLAYER_MINOR_VERSION_TOKEN_REGEX_ESCAPED, targetPlayerMinorVersion);

            try
            {
                if (playerExpandedPath.contains(LOCALE_TOKEN))
                {
                    for (final String locale : locales)
                    {
                        final String expandedPath = playerExpandedPath.replace(LOCALE_TOKEN, locale);
                        String resolvedPath = resolvePathStrict(expandedPath, configurationValue);
                        resolvedPaths.add(resolvedPath);

                        //Add this to the locale dependent sources map
                        localeDependentSources.put(resolvedPath, locale);
                    }
                }
                else
                {
                    String resolvedPath = resolvePathStrict(playerExpandedPath, configurationValue,
                            returnMissingFiles);
                    resolvedPaths.add(resolvedPath);
                }
            }
            catch (CannotOpen e)
            {
                // Making an exception here and catching this fatal error.
                // This is an exception because library paths come thru this
                // code path and we don't want to throw all of the libraries
                // out because one of the paths is bad. We want to load as
                // many libraries as we can an report the ones we couldn't load.
                configurationProblems.add(new ConfigurationProblem(e));
            }
        }

        return resolvedPaths.build();
    }
   
    /**
     * Replaces instances of {playerglobalHome} and {airHome}.
     * Values can come from either ../env.properties (relative to jar file) or
     * environment variables. The property file values have precedence.
     * The pairs are env.PLAYERGLOBAL_HOME and PLAYERGLOBAL_HOME, and,
     * env.AIR_HOME and AIR_HOME.
     */
    private String expandRuntimeTokens(String pathElement)
    {
        // Look at property file first, if it exists, and see if the particular property
        // is defined.  If not found, then look for the environment variable.
        // If there is neither leave the token in place since it is easier to
        // diagnose the problem with a token in the error message path then it is with
        // a "" in the path.
        Properties envProperties = loadEnvPropertyFile();
       
        String playerglobalHome =
            envProperties != null ?
            envProperties.getProperty("env.PLAYERGLOBAL_HOME", System.getenv("PLAYERGLOBAL_HOME")) :
            System.getenv("PLAYERGLOBAL_HOME");
           
        if (playerglobalHome == null)
            playerglobalHome = PLAYERGLOBAL_HOME_TOKEN;
               
        String airHome =
            envProperties != null ?
            envProperties.getProperty("env.AIR_HOME", System.getenv("AIR_HOME")) :
            System.getenv("AIR_HOME");

        if (airHome == null)
            airHome = AIR_HOME_TOKEN;
       
        pathElement = pathElement.replace(PLAYERGLOBAL_HOME_TOKEN, playerglobalHome);
        pathElement = pathElement.replace(AIR_HOME_TOKEN, airHome);
       
        return pathElement;
    }
   
    /**
     * Load the env.properties file from the classpath.
    
     * @return null if env.properties does not exist in classpath or could not be loaded
     */
    private Properties loadEnvPropertyFile()
    {
        Properties properties = null;
        InputStream in = null;
        
        try
        {
            in = getClass().getClassLoader().getResourceAsStream("env.properties");
            if (in == null)
            {
                try {
                    File f = new File("unittest.properties");
                    in = new FileInputStream( f );
                    properties = new Properties();
                    properties.load(in);
                    in.close();
                    properties.setProperty("env.PLAYERGLOBAL_HOME", properties.getProperty("PLAYERGLOBAL_HOME"));
                    properties.setProperty("env.AIR_HOME", properties.getProperty("AIR_HOME"));
                    properties.setProperty("env.PLAYERGLOBAL_VERSION", properties.getProperty("PLAYERGLOBAL_VERSION"));
                    return properties;
                } catch (FileNotFoundException e) {
                    return null;
                } catch (IOException e) {
                    return null;
                }
            }
           
            properties = new Properties();
            properties.load(in);
            in.close();
        }
        catch (Exception e)
        {
        }
       
        return properties;
    }

    private Map<String, String> localeDependentSources = new HashMap<String, String>();

    /**
     * Returns a map that stores locale dependent files. For each item in this
     * map, key is the path of the resource and value id the locale it belongs
     * to.
     */
    public Map<String, String> getLocaleDependentSources()
    {
        return localeDependentSources;
    }

    /**
     *
     * @param path A path to resolve.
     * @param cv Configuration context.
     * @return A single normalized resolved file. If the path could be expanded
     * into more than one path, then use {@link resolvePathsStrict}
     * @throws CannotOpen
     */
    private String resolvePathStrict(final String path,
            final ConfigurationValue cv) throws CannotOpen
    {
        return resolvePathStrict(path, cv, false);
    }

    /**
     * Resolve a single path. This is a more strict version of
     * {@link #resolvePaths()} in that it throws {@link CannotOpen} exception
     * when a file path element can't be resolved.
     *
     * @param path A path to resolve.
     * @param cv Configuration context.
     * @param returnMissingFiles Determines if the CannotOpen exception is thrown
     * if a file does not exist. Pass true to disable exceptions and return
     * files that do not exist. Pass false to throw exceptions.
     * @return A single normalized resolved file. If the path could be expanded
     * into more than one path, then use {@link resolvePathsStrict}.
     * @throws CannotOpen error
     * @see #resolvePaths(ImmutableList, ConfigurationValue)
     */
    private String resolvePathStrict(final String path,
                final ConfigurationValue cv,
                final boolean returnMissingFiles) throws CannotOpen
    {
        ImmutableList<String> singletonPath = ImmutableList.of(path);
        ImmutableList<String> results = resolvePathsStrict(singletonPath, cv,
                returnMissingFiles);
        return results.get(0);
    }

    /**
     * Resolve a list of paths. This is a more strict version of
     * {@link #resolvePaths()} in that it throws {@link CannotOpen} exception
     * when a file path element can't be resolved.
     *
     * @param paths A list of paths to resolve.
     * @param cv Configuration context.
     * @return A list of normalized resolved file paths.
     * @throws CannotOpen error
     * @see #resolvePaths(ImmutableList, ConfigurationValue)
     */
    private ImmutableList<String> resolvePathsStrict(final ImmutableList<String> paths,
                                         final ConfigurationValue cv) throws CannotOpen
    {
        return resolvePathsStrict(paths, cv, false);
    }
                                                   
    /**
     * Resolve a list of paths. This is a more strict version of
     * {@link #resolvePaths()} in that it throws {@link CannotOpen} exception
     * when a file path element can't be resolved.
     *
     * @param paths A list of paths to resolve.
     * @param cv Configuration context.
     * @param returnMissingFiles Determines if the CannotOpen exception is thrown
     * if a file does not exist. Pass true to disable exceptions and return
     * files that do not exist. Pass false to throw exceptions.
     * @return A list of normalized resolved file paths.
     * @throws CannotOpen error
     * @see #resolvePaths(ImmutableList, ConfigurationValue)
     */
    private ImmutableList<String> resolvePathsStrict(final ImmutableList<String> paths,
                                                    final ConfigurationValue cv,
                                                    final boolean returnMissingFiles) throws CannotOpen
    {
        assert paths != null : "Expected paths";
        assert cv != null : "Require ConfigurationValue as context.";

        final ImmutableList.Builder<String> resolvedPathsBuilder = new ImmutableList.Builder<String>();
        for (String processedPath : paths)
        {
            if (cv.getContext() != null)
            {
                boolean isAbsolute = new File(processedPath).isAbsolute();
                if (!isAbsolute)
                    processedPath = new File(cv.getContext(), processedPath).getAbsolutePath();
            }
            final File fileSpec = pathResolver.resolve(processedPath);
            if (!returnMissingFiles && !fileSpec.exists())
            {
                throw new CannotOpen(FilenameNormalization.normalize(processedPath),
                        cv.getVar(), cv.getSource(), cv.getLine());
            }
           
            resolvedPathsBuilder.add(fileSpec.getAbsolutePath());
        }
        return resolvedPathsBuilder.build();
    }

    //////////////////////////////////////////////////////////////////////////
    // compiler.extensions.*
    //////////////////////////////////////////////////////////////////////////

    //
    // 'compiler.extensions.extension' option
    //

    /**
     * Configures a list of many extensions mapped to a single Extension URI. <extension>
     * <extension>something-extension.jar</extension> <parameters>version=1.1,content=1.2</parameters> </extension>
     *
     * @param cv The configuration value context.
     * @param pathlist A List of values for the Extension element, with the first item expected to be the uri and the
     *            remaining are extension paths.
     * @throws CannotOpen When no arg is provided or when the jar does not exist.
     */
    @Config(allowMultiple = true, removed=true)
    @Mapping({"compiler", "extensions", "extension"})
    @Arguments({"extension", "parameters"})
    @InfiniteArguments
    public void setExtension(ConfigurationValue cv, String[] pathlist) throws CannotOpen
    {
    }

    //////////////////////////////////////////////////////////////////////////
    // compiler.fonts.*
    //////////////////////////////////////////////////////////////////////////

    @Config
    @Mapping({"compiler", "fonts", "advanced-anti-aliasing"})
    @FlexOnly
    public void setCompilerFontsAdvancedAntiAliasing(ConfigurationValue cv, boolean val)
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    @Config(allowMultiple = true, advanced = true)
    @Mapping({"compiler", "fonts", "languages", "language-range"})
    @Arguments({"lang", "range"})
    @FlexOnly
    public void setCompilerFontsLanguagesLanguageRange(ConfigurationValue cv, String lang, String range)
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    @Config
    @Mapping({"compiler", "fonts", "local-fonts-snapshot"})
    @FlexOnly
    public void setCompilerFontsLocalFontsSnapshot(ConfigurationValue cv, String localFontsSnapshotPath) throws CannotOpen
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    @Config
    @Mapping({"compiler", "fonts", "local-font-paths"})
    @Arguments(Arguments.PATH_ELEMENT)
    @InfiniteArguments
    @FlexOnly
    public void setCompilerFontsLocalFontPaths(ConfigurationValue cv, List<String> list) throws CannotOpen
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    @Config(advanced = true)
    @Mapping({"compiler", "fonts", "managers"})
    @Arguments("manager-class")
    @InfiniteArguments
    @FlexOnly
    public void setCompilerFontsManagers(ConfigurationValue cv, List<String> list)
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    @Config
    @Mapping({"compiler", "fonts", "max-cached-fonts"})
    @FlexOnly
    public void setCompilerFontsMaxCachedFonts(ConfigurationValue cv, String val)
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    @Config
    @Mapping({"compiler", "fonts", "max-glyphs-per-face"})
    @FlexOnly
    public void setCompilerFontsMaxGlyphsPerFace(ConfigurationValue cv, String val)
    {
        // intentionally do nothing here as feature removed, but don't annotate as removed
        // as to not generate warnings for flex-config's which still set this options
    }

    //////////////////////////////////////////////////////////////////////////
    // compiler.namespaces
    //////////////////////////////////////////////////////////////////////////

    private List<MXMLNamespaceMapping> manifestMappings;

    public List<MXMLNamespaceMapping> getCompilerNamespacesManifestMappings()
    {
        return manifestMappings;
    }

    /**
     * Configures a list of many manifests mapped to a single namespace URI.
     * <namespace> <uri>library:adobe/flex/something</uri>
     * <manifest>something-manifest.xml</manifest>
     * <manifest>something-else-manifest.xml</manifest> ... </namespace>
     *
     * @param cfgval The configuration value context.
     * @param args A List of values for the namespace element, with the first
     * item expected to be the uri and the remaining are manifest paths.
     */
    @Config(allowMultiple = true)
    @Mapping({"compiler", "namespaces", "namespace"})
    @Arguments({"uri", "manifest"})
    @InfiniteArguments
    @FlexOnly
    public void setCompilerNamespacesNamespace(ConfigurationValue cfgval, List<String> args)
            throws ConfigurationException
    {
        if (args == null)
            throw new ConfigurationException.CannotOpen(null, cfgval.getVar(), cfgval.getSource(), cfgval.getLine());

        // allow -compiler.namespaces.namespace= which means don't add
        // anything, which matches the behavior of things like -compiler.library-path
        // which don't throw an error in this case either.
        if (args.isEmpty())
            return;

        if (args.size() < 2)
            throw new ConfigurationException.NamespaceMissingManifest("namespace", cfgval.getSource(), cfgval.getLine());

        if (args.size() % 2 != 0)
            throw new ConfigurationException.IncorrectArgumentCount(args.size() + 1, args.size(), cfgval.getVar(), cfgval.getSource(), cfgval.getLine());

        if (manifestMappings == null)
            manifestMappings = new ArrayList<MXMLNamespaceMapping>();

        for (int i = 0; i < args.size() - 1; i += 2)
        {
            final String uri = args.get(i);
            final String manifestFile = args.get(i + 1);
            final String path = resolvePathStrict(manifestFile, cfgval);
            manifestMappings.add(new MXMLNamespaceMapping(uri, path));
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // metadata.*
    ///////////////////////////////////////////////////////////////////////////

    //
    // 'metadata.contributor' option
    //

    private final Set<String> contributors = new TreeSet<String>();

    @Config(allowMultiple = true)
    @Mapping({"metadata", "contributor"})
    @Arguments("name")
    public void setMetadataContributor(ConfigurationValue cv, String name)
    {
        contributors.add(name);
    }

    //
    // 'metadata.creator' option
    //

    private final Set<String> creators = new TreeSet<String>();

    @Config(allowMultiple = true)
    @Mapping({"metadata", "creator"})
    @Arguments("name")
    public void setMetadataCreator(ConfigurationValue cv, String name)
    {
        creators.add(name);
    }

    //
    // 'metadata.date' option
    //

    public String date = null;

    @Config
    @Mapping({"metadata", "date"})
    @Arguments("text")
    public void setMetadataDate(ConfigurationValue cv, String text)
    {
        date = text;
    }

    //
    // 'metadata.description' option
    //

    private final Map<String, String> localizedDescriptions = new LinkedHashMap<String, String>();

    @Config
    @Mapping({"metadata", "description"})
    @Arguments("text")
    public void setMetadataDescription(ConfigurationValue cv, String text)
    {
        localizedDescriptions.put("x-default", text);
    }

    //
    // 'metadata.language' option
    //

    public final Set<String> langs = new TreeSet<String>();

    @Config(allowMultiple = true)
    @Mapping({"metadata", "language"})
    @Arguments("code")
    public void setMetadataLanguage(ConfigurationValue cv, String code)
    {
        langs.add(code);
    }

    //
    // 'metadata.localized-description' option
    //

    @Config(allowMultiple = true)
    @Mapping({"metadata", "localized-description"})
    @Arguments({"text", "lang"})
    public void setMetadataLocalizedDescription(ConfigurationValue cv, String text, String lang)
    {
        localizedDescriptions.put(lang, text);
    }

    //
    // 'metadata.localized-title' option
    //

    @Config(allowMultiple = true)
    @Mapping({"metadata", "localized-title"})
    @Arguments({"title", "lang"})
    public void setMetadataLocalizedTitle(ConfigurationValue cv, String title, String lang)
    {
        localizedTitles.put(lang, title);
    }

    //
    // 'metadata.publisher' option
    //

    private final Set<String> publishers = new TreeSet<String>();

    @Config(allowMultiple = true)
    @Mapping({"metadata", "publisher"})
    @Arguments("name")
    public void setMetadataPublisher(ConfigurationValue cv, String name)
    {
        publishers.add(name);
    }

    //
    // 'metadata.title' option
    //

    private final Map<String, String> localizedTitles = new LinkedHashMap<String, String>();

    @Config
    @Mapping({"metadata", "title"})
    @Arguments("text")
    public void setMetadataTitle(ConfigurationValue cv, String title)
    {
        localizedTitles.put("x-default", title);
    }

    //////////////////////////////////////////////////////////////////////////
    // runtime-shared-library-settings
    //////////////////////////////////////////////////////////////////////////

    //
    // 'force-rsl' option
    //
    private Set<String> forceRsls;

    /**
     * Get the array of SWCs that should have their RSLs loaded, even if the
     * compiler detects no classes being used from the SWC.
     *
     * @return Array of SWCs that should have their RSLs loaded.
     */
    public Set<String> getForceRsls()
    {
        if (forceRsls == null)
        {
            return Collections.emptySet();
        }

        return forceRsls;
    }

    @Config(advanced=true, allowMultiple=true)
    @Mapping({"runtime-shared-library-settings", "force-rsls"})
    @SoftPrerequisites({"runtime-shared-library-path"})
    @Arguments(Arguments.PATH_ELEMENT)
    @InfiniteArguments   
    @FlexOnly
    public void setForceRsls(ConfigurationValue cfgval,
            String[] args) throws ConfigurationException
    {
        if (forceRsls == null)
        {
            forceRsls = new HashSet<String>();
        }

        // Add swc to the forceRsls set.
        for (String arg : args)
        {
            // path-element parameter (swc)
            // verify path exists and the swc has an
            // existing -rslp option specified.
            String swcPath = resolvePathStrict(arg, cfgval);

            // verify the swc is used in an the RSL configuration.
            if (!doesSwcHaveRSLInfo(swcPath))
            {
                throw new ConfigurationException.SwcDoesNotHaveRslData(swcPath,
                              cfgval.getVar(), cfgval.getSource(), cfgval.getLine());
            }

            forceRsls.add(swcPath);
        }
    }

    //
    // 'application-domain' option
    //
   
    /*
     * Key: swc file path; Value: application domain target
     */
    private HashMap<String,ApplicationDomainTarget> applicationDomains;

    /**
     * Get the application domain an RSL should be loaded into. The default is
     * the current application domain but the user can override this setting.
     * @param swcPath The full path of the swc file.
     *
     * @return The application domain the RSL should be loaded into. If the
     * swc is not found, then 'default' is returned.
     */
    public ApplicationDomainTarget getApplicationDomain(String swcPath)
    {
        if (applicationDomains == null || swcPath == null)
        {
            return ApplicationDomainTarget.DEFAULT;
        }
       
        for (Map.Entry<String, ApplicationDomainTarget> entry : applicationDomains.entrySet())
        {
            if (entry.getKey().equals(swcPath))
            {
                return entry.getValue();
            }
        }

        return ApplicationDomainTarget.DEFAULT;
    }
   
    @Config(advanced=true, allowMultiple=true)
    @SoftPrerequisites({"runtime-shared-library-path"})
    @Mapping({"runtime-shared-library-settings", "application-domain"})
    @Arguments({"path-element", "application-domain-target"})
    @InfiniteArguments   
    @FlexOnly
    // TODO: need to create an argument name generator for the args.
    public void setApplicationDomain(ConfigurationValue cfgval,
            String[] argsthrows ConfigurationException
    {
        // ignore the force option if we are static linking
        if (getStaticLinkRsl())
            return;
       
        if (applicationDomains == null)
        {
            applicationDomains = new HashMap<String,ApplicationDomainTarget>();
        }
        // Add swc and application domain target to the map.
        // The args are: swc file path, application domain type, ...
        for (int i = 0; i < args.length; i++)
        {
            String arg = args[i++];
           
            // path-element parameter (swc)
            // verify path exists and the swc has an
            // existing -rslp option specified.
            String swcPath = resolvePathStrict(arg, cfgval);

            // verify the swc is used in an the RSL configuration.
            if (!doesSwcHaveRSLInfo(swcPath))
            {
                throw new ConfigurationException.SwcDoesNotHaveRslData(swcPath,
                              cfgval.getVar(), cfgval.getSource(), cfgval.getLine());
            }

            // Verify the application domain target is valid.
            arg = args[i];
            ApplicationDomainTarget adTarget = getApplicationDomainTarget(arg);
            if (adTarget == null)
            {
                // throw a configuration exception that the application domain
                // type is incorrect.
                throw new ConfigurationException.BadApplicationDomainValue(swcPath, arg, cfgval.getVar(),
                        cfgval.getSource(), cfgval.getLine());
            }
           
            applicationDomains.put(swcPath, adTarget);
        }
    }
   
    /**
     * Test if the specified parameter is a valid application domain type. If
     * it is then return the corresponding enum.
     *
     * @param arg String value representing an ApplicationDomainTarget.
     * @return An ApplicationDomainTarget enum if the parameter is a valid
     * application domain target, null otherwise.
     */
    private ApplicationDomainTarget getApplicationDomainTarget(String arg)
    {
        for (ApplicationDomainTarget appDomain : ApplicationDomainTarget.values())
        {
            if (appDomain.getApplicationDomainValue().equals(arg))
                return appDomain;
        }
       
        return null;
    }

    /**
     * Check if the SWC has any RSL info associated with it.
     *
     * @param swcPath
     * @return true if the swc has RSL info, false otherwise.
     */
    private boolean doesSwcHaveRSLInfo(String swcPath)
    {
        if (swcPath == null)
            return false;

        List<RuntimeSharedLibraryPathInfo> rslInfoList = getRslPathInfo();
        for (RuntimeSharedLibraryPathInfo rslInfo : rslInfoList)
        {
            if (swcPath.equals(rslInfo.getSWCFile().getPath()))
                return true;
        }

        return false;
    }

    //////////////////////////////////////////////////////////////////////////
    // compiler.mxml
    //////////////////////////////////////////////////////////////////////////

    //
    // 'compiler.mxml.compatibility-version' option
    //

    public static final int MXML_VERSION_4_6 = 0x04060000;   
    public static final int MXML_VERSION_4_5 = 0x04050000;   
    public static final int MXML_VERSION_4_0 = 0x04000000;
    public static final int MXML_VERSION_3_0 = 0x03000000;
    public static final int MXML_VERSION_2_0_1 = 0x02000001;
    public static final int MXML_VERSION_2_0 = 0x02000000;
    public static final int MXML_CURRENT_VERSION = MXML_VERSION_4_6;
    public static final int MXML_EARLIEST_MAJOR_VERSION = 3;
    public static final int MXML_LATEST_MAJOR_VERSION = 4;
    public static final int MXML_LATEST_MINOR_VERSION = 6;

    private int mxml_major = MXML_LATEST_MAJOR_VERSION;
    private int mxml_minor = MXML_LATEST_MINOR_VERSION;
    private int mxml_revision;

    private int mxmlMinMajor = MXML_EARLIEST_MAJOR_VERSION;
    private int mxmlMinMinor;
    private int mxmlMinRevision;

    public int getCompilerMxmlMajorCompatibilityVersion()
    {
        return mxml_major;
    }

    public int getCompilerMxmlMinorCompatibilityVersion()
    {
        return mxml_minor;
    }

    public int getCompilerMxmlRevisionCompatibilityVersion()
    {
        return mxml_revision;
    }

    /*
     * Unlike the framework's FlexVersion.compatibilityVersionString, this
     * returns null rather than a string like "3.0.0" for the current version.
     * But if a -compatibility-version was specified, this string will always be
     * of the form N.N.N. For example, if -compatibility-version=2, this string
     * is "2.0.0", not "2".
     */
    public String getCompilerMxmlCompatibilityVersionString()
    {
        return (mxml_major == 0 && mxml_minor == 0 && mxml_revision == 0) ? null : mxml_major + "." + mxml_minor + "." + mxml_revision;
    }

    /*
     * This returns an int that can be compared with version constants such as
     * MxmlConfiguration.VERSION_3_0.
     */
    public int getCompilerMxmlCompatibilityVersion()
    {
        int version = (mxml_major << 24) + (mxml_minor << 16) + mxml_revision;
        return version != 0 ? version : MXML_CURRENT_VERSION;
    }

    @Config
    @Mapping({"compiler", "mxml", "compatibility-version"})
    @Arguments("version")
    @FlexOnly
    public void setCompilerMxmlCompatibilityVersion(ConfigurationValue cv, String version) throws ConfigurationException
    {
        if (version == null)
        {
            return;
        }

        String[] results = version.split("\\.");

        if (results.length == 0)
        {
            throw new ConfigurationException.BadVersion(version, "compatibility-version");

        }
       
        // Set minor and revision numbers to zero in case only a major number
        // was specified.
        this.mxml_minor = 0;
        this.mxml_revision = 0;

        for (int i = 0; i < results.length; i++)
        {
            int versionNum = 0;

            try
            {
                versionNum = Integer.parseInt(results[i]);
            }
            catch (NumberFormatException e)
            {
                throw new ConfigurationException.BadVersion(version, "compatibility-version");
            }

            if (i == 0)
            {
                if (versionNum >= MXML_EARLIEST_MAJOR_VERSION && versionNum <= MXML_LATEST_MAJOR_VERSION)
                {
                    this.mxml_major = versionNum;
                }
                else
                {
                    throw new ConfigurationException.BadVersion(version, "compatibility-version");
                }
            }
            else
            {
                if (versionNum >= 0)
                {
                    if (i == 1)
                    {
                        this.mxml_minor = versionNum;
                    }
                    else
                    {
                        this.mxml_revision = versionNum;
                    }
                }
                else
                {
                    throw new ConfigurationException.BadVersion(version, "compatibility-version");
                }
            }
        }
    }

    /*
     * Minimum supported SDK version for this library. This string will always
     * be of the form N.N.N. For example, if -minimum-supported-version=2, this
     * string is "2.0.0", not "2".
     */
    public String getCompilerMxmlMinimumSupportedVersionString()
    {
        return (mxmlMinMajor == 0 && mxmlMinMinor == 0 && mxmlMinRevision == 0) ?
                null : mxmlMinMajor + "." + mxmlMinMinor + "." + mxmlMinRevision;
    }

    /*
     * This returns an int that can be compared with version constants such as
     * MxmlConfiguration.VERSION_3_0.
     */
    public int getCompilerMxmlMinimumSupportedVersion()
    {
        int version = (mxmlMinMajor << 24) + (mxmlMinMinor << 16) + mxmlMinRevision;
        return version != 0 ? version : (MXML_EARLIEST_MAJOR_VERSION << 24);
    }

    public void setCompilerMxmlMinimumSupportedVersion(int version)
    {
        mxmlMinMajor = version >> 24 & 0xFF;
        mxmlMinMinor = version >> 16 & 0xFF;
        mxmlMinRevision = version & 0xFF;
    }

    @Config
    @Mapping({"compiler", "mxml", "minimum-supported-version"})
    @FlexOnly
    public void setCompilerMxmlMinimumSupportedVersion(ConfigurationValue cv, String version) throws ConfigurationException
    {
        if (version == null)
        {
            return;
        }

        String[] results = version.split("\\.");

        if (results.length == 0)
        {
            throw new ConfigurationException.BadVersion(version, "minimum-supported-version");

        }

        for (int i = 0; i < results.length; i++)
        {
            int versionNum = 0;

            try
            {
                versionNum = Integer.parseInt(results[i]);
            }
            catch (NumberFormatException e)
            {
                throw new ConfigurationException.BadVersion(version, "minimum-supported-version");
            }

            if (i == 0)
            {
                if (versionNum >= MXML_EARLIEST_MAJOR_VERSION && versionNum <= MXML_LATEST_MAJOR_VERSION)
                {
                    this.mxmlMinMajor = versionNum;
                }
                else
                {
                    throw new ConfigurationException.BadVersion(version, "minimum-supported-version");
                }
            }
            else
            {
                if (versionNum >= 0)
                {
                    if (i == 1)
                    {
                        mxmlMinMinor = versionNum;
                    }
                    else
                    {
                        mxmlMinRevision = versionNum;
                    }
                }
                else
                {
                    throw new ConfigurationException.BadVersion(version, "minimum-supported-version");
                }
            }
        }

        isMinimumSupportedVersionConfigured = true;
    }

    private boolean isMinimumSupportedVersionConfigured = false;

    public boolean isCompilerMxmlMinimumSupportedVersionConfigured()
    {
        return isMinimumSupportedVersionConfigured;
    }

    //
    // 'compiler.mobile' option
    //

    private boolean mobile = false;

    /**
     * @return determines whether the target runtime is a mobile device. This
     * may alter the features available, such as certain blend-modes when
     * compiling FXG.
     */
    public boolean getMobile()
    {
        return mobile;
    }

    @Config()
    @Mapping({"compiler", "mobile"})
    public void setMobile(ConfigurationValue cv, boolean b)
    {
        mobile = b;
    }

    ////////////////////////////////////////////////////////////////////////////
    // licenses.*
    ////////////////////////////////////////////////////////////////////////////

    //
    //  'license' option
    //

    @Config(allowMultiple = true, displayed = false, removed = true)
    @Mapping({"licenses", "license"})
    @Arguments({"product", "serial-number"})
    public void setLicensesLicense(ConfigurationValue cfgval, String product, String serialNumber)
            throws ConfigurationException
    {
    }

    ////////////////////////////////////////////////////////////////////////////
    // frames.*
    ////////////////////////////////////////////////////////////////////////////

    private List<FrameInfo> frameList = new LinkedList<FrameInfo>();

    public List<FrameInfo> getFrameList()
    {
        return frameList;
    }

    @Config(advanced = true, allowMultiple = true)
    @Mapping({"frames", "frame"})
    @Arguments({"label", "classname"})
    @InfiniteArguments
    public void setFramesFrame(ConfigurationValue cv, List<String> args) throws ConfigurationException
    {
        // "args" are [label, qname, qname, qname, ...]

        final FrameInfo info = new FrameInfo();

        if (args.size() < 2)
            throw new ConfigurationException.BadFrameParameters(cv.getVar(), cv.getSource(), cv.getLine());

        for (final String next : args)
        {
            if (info.getLabel() == null)
            {
                info.setLabel(next);
            }
            else
            {
                info.getFrameClasses().add(next);
            }
        }

        frameList.add(info);
    }

    //
    // -as3
    //

    private boolean as3 = true;
   
    @Config(advanced = true)
    @Mapping({"compiler", "as3"})
    @DeprecatedConfig
    public void setAS3(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != as3)
            addRemovedConfigurationOptionProblem(cv);
    }

    //
    // -es
    //

    private boolean es = false;
   
    @Config(advanced = true)
    @Mapping({"compiler", "es"})
    @DeprecatedConfig
    public void setES(ConfigurationValue cv, boolean b)
    {
        // This option is set in flex-config.xml so only warn
        // if the user sets a non-default value.
        if (b != es)
            addRemovedConfigurationOptionProblem(cv);
    }

    ////////////////////////////////////////////////////////////////////////////
    // Compc Options
    ////////////////////////////////////////////////////////////////////////////

    //
    // compute-digest=true|false
    //

    /**
     * Writes a digest to the catalog.xml of a library. Use this when the
     * library will be used as a cross-domain RSL or when you want to enforce
     * the versioning of RSLs. The default value is true.
     */
    @Config(compcOnly = true, removed = true)
    @Mapping("compute-digest")
    public void setComputeDigest(ConfigurationValue cv, boolean value)
    {
    }

    /**
     * directory=false|true
     */
    private boolean outputSwcAsDirectory = false;

    /**
     * Outputs the SWC file in an open directory format rather than a SWC file.
     * You use this option with the output option to specify a destination
     * directory, as the following example shows:
     *
     * <pre>
     * compc -directory=true -output=destination_directory
     * </pre>
     */
    @Config(compcOnly = true)
    @Mapping("directory")
    public void setOutputSwcAsDirectory(ConfigurationValue cv, boolean value)
    {
        outputSwcAsDirectory = value;
    }

    /**
     * @return True if the compiler will build the SWC file in an open directory
     * format rather than a SWC file.
     */
    public boolean getOutputSwcAsDirectory()
    {
        return outputSwcAsDirectory;
    }

    /**
     * include-classes class [...]
     */
    private final List<String> includeClasses = new ArrayList<String>();

    /**
     * Specifies classes to include in the SWC file. You provide the class name
     * (for example, MyClass) rather than the file name (for example,
     * MyClass.as) to the file for this option. As a result, all classes
     * specified with this option must be in the compiler's source path. You
     * specify this by using the source-path compiler option.
     * <p>
     * You can use packaged and unpackaged classes. To use components in
     * namespaces, use the include-namespaces option.
     * <p>
     * If the components are in packages, ensure that you use dot-notation
     * rather than slashes to separate package levels.
     * <p>
     * This is the default option for the component compiler.
     */
    @Config(compcOnly = true, allowMultiple = true)
    @Arguments(Arguments.CLASS)
    @Mapping("include-classes")
    public void setIncludeClasses(ConfigurationValue cv, List<String> values)
    {
        includeClasses.addAll(values);
    }

    /**
     * @return A list of class names to be included in the target.
     */
    public List<String> getIncludeClasses()
    {
        return includeClasses;
    }

    /**
     * include-file name path [...]
     */
    public final Map<String, String> includeFilesNamePath = new LinkedHashMap<String, String>();

    /**
     * Adds the file to the SWC file. This option does not embed files inside
     * the library.swf file. This is useful for adding graphics files, where you
     * want to add non-compiled files that can be referenced in a style sheet or
     * embedded as assets in MXML files.
     * <p>
     * If you add a stylesheet that references compiled resources such as
     * programmatic skins, use the include-stylesheet option.
     * <p>
     * If you use the [Embed] syntax to add a resource to your application, you
     * are not required to use this option to also link it into the SWC file.
     */
    @Config(compcOnly = true, allowMultiple = true)
    @Mapping("include-file")
    @Arguments({"name", "path"})
    public void setIncludeFiles(ConfigurationValue cv, List<String> values)
        throws IncorrectArgumentCount, CannotOpen, RedundantFile
    {
        // Expect name-path pairs in the arguments.
        final int size = values.size();
        if (size % 2 != 0)
            throw new IncorrectArgumentCount(size + 1, size, cv.getVar(), cv.getSource(), cv.getLine());

        for (int nameIndex = 0; nameIndex < size - 1; nameIndex += 2)
        {
            final String name = values.get(nameIndex);
            final String path = resolvePathStrict(values.get(nameIndex + 1), cv);
               
            if (includeFilesNamePath.containsKey(name))
            {
                throw new ConfigurationException.RedundantFile(name, cv.getVar(),
                        cv.getSource(), cv.getLine() );
            }

            includeFilesNamePath.put(name, path);
        }
    }

    /**
     * @return A map of included files. The keys are file entry names; The
     * values are file paths.
     */
    public Map<String, String> getIncludeFiles()
    {
        return includeFilesNamePath;
    }

    /**
     * include-lookup-only=false|true
     */
    private boolean includeLookupOnly = false;

    /**
     * If true, only manifest entries with lookupOnly=true are included in the
     * SWC catalog.
     */
    @Config(compcOnly = true, advanced = true)
    @Mapping("include-lookup-only")
    @FlexOnly
    public void setIncludeLookupOnly(ConfigurationValue cv, boolean value)
    {
        includeLookupOnly = value;
    }

    /**
     * @return If true, only manifest entries with lookupOnly=true are included
     * in the SWC catalog.
     */
    public boolean getIncludeLookupOnly()
    {
        return includeLookupOnly;
    }

    /**
     * include-namespaces uri [...]
     */
    private final List<String> includeNamespaces = new ArrayList<String>();

    /**
     * Specifies namespace-style components in the SWC file. You specify a list
     * of URIs to include in the SWC file. The uri argument must already be
     * defined with the namespace option.
     * <p>
     * To use components in packages, use the include-classes option.
     */
    @Config(compcOnly = true, allowMultiple = true)
    @Mapping("include-namespaces")
    @Arguments({"uri"})
    @FlexOnly
    public void setIncludeNamespaces(ConfigurationValue cv, List<String> values)
    {
        includeNamespaces.addAll(values);
    }

    /**
     * @return A list of URIs to include in the SWC file.
     */
    public List<String> getIncludeNamespaces()
    {
        return includeNamespaces;
    }

    /**
     * include-sources path-element
     */
    private final List<String> includeSources = new ArrayList<String>();

    /**
     * Specifies classes or directories to add to the SWC file. When specifying
     * classes, you specify the path to the class file (for example, MyClass.as)
     * rather than the class name itself (for example, MyClass). This lets you
     * add classes to the SWC file that are not in the source path. In general,
     * though, use the include-classes option, which lets you add classes that
     * are in the source path.
     * <p>
     * If you specify a directory, this option includes all files with an MXML
     * or AS extension, and ignores all other files.
     * <p>
     * If you use this option to include MXML components that are in a
     * non-default package, you must include the source folder in the source
     * path.
     */
    @Config(compcOnly = true, allowMultiple = true)
    @Mapping("include-sources")
    @Arguments(Arguments.PATH_ELEMENT)
    public void setIncludeSources(ConfigurationValue cv, List<String> values) throws NotAFile
    {
        fillListWithResolvedPaths(values, includeSources, cv);
    }

    /**
     * @return Normalized file paths of the included source files.
     */
    public List<String> getIncludeSources()
    {
        return includeSources;
    }

    /**
     * Add resolved file paths from
     * {@code source} list to {@code target} list.
     *
     * @param source Source list with un-resolved file paths.
     * @param target Target list.
     * @param cv Context.
     * @throws CannotOpen
     */
    private void fillListWithResolvedPaths(
            final List<String> source,
            final List<String> target,
            final ConfigurationValue cv) throws NotAFile
    {
        for (final String path : source)
        {
            String resolvedPath;
            try
            {
                resolvedPath = resolvePathStrict(path, cv);
                target.add(resolvedPath);
            }
            catch (CannotOpen e)
            {
                throw new ConfigurationException.NotAFile(path, cv.getVar(), cv.getSource(), cv.getLine());
            }
        }
    }

    /**
     * include-stylesheet namepath [...]
     */
    private final List<String> includeStyleSheets = new ArrayList<String>();

    /**
     * Specifies stylesheets to add to the SWC file. This option compiles
     * classes that are referenced by the stylesheet before including the
     * stylesheet in the SWC file.
     * <p>
     * You do not need to use this option for all stylesheets; only stylesheets
     * that reference assets that need to be compiled such as programmatic skins
     * or other class files. If your stylesheet does not reference compiled
     * assets, you can use the include-file option.
     * <p>
     * This option does not compile the stylesheet into a SWF file before
     * including it in the SWC file. You compile a CSS file into a SWF file when
     * you want to load it at run time.
     */
    @Config(compcOnly = true, allowMultiple = true)
    @Mapping("include-stylesheet")
    @Arguments({"name", "path"})
    @FlexOnly
    public void setIncludeStyleSheets(ConfigurationValue cv, List<String> values) throws NotAFile
    {
        fillListWithResolvedPaths(values, includeStyleSheets, cv);
    }   

    /**
     * @return A list of the normalized file path of stylesheets to add to the
     * SWC file.
     */
    public List<String> getIncludeStyleSheets()
    {
        return includeStyleSheets;
    }
   
   
    private File dependencyGraphOutput;
   
    /**
     * Specifies a file name that a graphml version of the dependency graph
     * should be written to.
     *
     */
    @Config(advanced = true)
    @Mapping("dependency-graph")
    @Arguments("filename")
    public void setDependencyGraphOutput(ConfigurationValue cv, String fileName)
    {
        dependencyGraphOutput = new File(getOutputPath(cv, fileName));
    }
   
    /**
     * Gets the location the graphml version of the dependency graph should be
     * written to, null if no dependecy graph should be written.
     *
     * @return The location the dependency graph should be written to.
     */
    public File getDependencyGraphOutput()
    {
        return dependencyGraphOutput;
    }
   
   
    //
    // 'output' option
    //
   
    private String output;

    public String getOutput()
    {
        return output;
    }

    @Config
    @Arguments("filename")
    public void setOutput(ConfigurationValue val, String output) throws ConfigurationException
    {
        this.output = getOutputPath(val, output);
    }

    //
    // 'dump-config-file' option from ToolsConfiguration
    //

    private String dumpConfigFile = null;

    /**
     * @return filename of the configuration dump
     */
    public String getDumpConfig()
    {
        return dumpConfigFile;
    }

    @Config(advanced = true, displayed = false)
    @Arguments("filename")
    @Mapping("dump-config")
    public void setDumpConfig(ConfigurationValue cv, String filename)
    {
        dumpConfigFile = getOutputPath(cv, filename);
    }

    //
    // 'warnings' option from ToolsConfiguration
    //

    private boolean warnings = true;

    public boolean getWarnings()
    {
        return warnings;
    }

    @Config
    @Mapping("warnings")
    public void setWarnings(ConfigurationValue cv, boolean b)
    {
        warnings = b;
    }
   
    //
    // 'error-problems'
    //
   
    private Collection<Class<ICompilerProblem>> errorClasses;

    /**
     * Get the collection of user specified problem classes that should be
     * treated as errors.
     *
     * @return list of problem classes that should be treated as errors.
     */
    public Collection<Class<ICompilerProblem>> getErrorProblems()
    {
        return errorClasses != null ? errorClasses : Collections.<Class<ICompilerProblem>>emptyList();
    }
   
    @Config(allowMultiple=true)
    @Arguments(Arguments.CLASS)
    @InfiniteArguments
    public void setErrorProblems(ConfigurationValue cv, List<String> classNames) throws ConfigurationException
    {
        if (errorClasses == null)
            errorClasses = new HashSet<Class<ICompilerProblem>>();
       
        // Convert string to a class and save.
        for (String className : classNames)
        {
            Class<ICompilerProblem> resolvedClass = resolveProblemClassName(className);
            if (resolvedClass == null)
            {
                throw new ConfigurationException.CompilerProblemClassNotFound(className,
                        cv.getVar(), cv.getSource(), cv.getLine());
            }
               
            errorClasses.add(resolvedClass);
        }
    }

    //
    // 'warning-problems'
    //
   
    private Collection<Class<ICompilerProblem>> warningClasses;
   
    /**
     * Get the collection of user specified problem classes that should be
     * treated as warnings.
     *
     * @return list of problem classes that should be treated as warnings.
     */
    public Collection<Class<ICompilerProblem>> getWarningProblems()
    {
        return warningClasses != null ? warningClasses : Collections.<Class<ICompilerProblem>>emptyList();
    }
   
    @Config(allowMultiple=true)
    @Arguments(Arguments.CLASS)
    @InfiniteArguments
    public void setWarningProblems(ConfigurationValue cv, List<String> classNames) throws ConfigurationException
    {
        if (warningClasses == null)
            warningClasses = new HashSet<Class<ICompilerProblem>>();
       
        // Convert string to a class and save.
        for (String className : classNames)
        {
            Class<ICompilerProblem> resolvedClass = resolveProblemClassName(className);
            if (resolvedClass == null)
            {
                throw new ConfigurationException.CompilerProblemClassNotFound(className,
                        cv.getVar(), cv.getSource(), cv.getLine());
            }
               
            warningClasses.add(resolvedClass);
        }
    }

    //
    // 'ignore-problems'
    //
   
    private Collection<Class<ICompilerProblem>> ignoreClasses;
   
    /**
     * Get the collection of user specified problem classes that should be
     * ignored.
     *
     * @return list of problem classes that should be ignored.
     */
    public Collection<Class<ICompilerProblem>> getIgnoreProblems()
    {
        return ignoreClasses != null ? ignoreClasses : Collections.<Class<ICompilerProblem>>emptyList();
    }
   
    @Config(allowMultiple=true)
    @Arguments(Arguments.CLASS)
    @InfiniteArguments
    public void setIgnoreProblems(ConfigurationValue cv, List<String> classNames) throws ConfigurationException
    {
        if (ignoreClasses == null)
            ignoreClasses = new HashSet<Class<ICompilerProblem>>();
       
        // Convert string to a class and save.
        for (String className : classNames)
        {
            Class<ICompilerProblem> resolvedClass = resolveProblemClassName(className);
            if (resolvedClass == null)
            {
                throw new ConfigurationException.CompilerProblemClassNotFound(className,
                        cv.getVar(), cv.getSource(), cv.getLine());
            }
               
            ignoreClasses.add(resolvedClass);
        }
    }
   
    //
    // 'legacy-message-format'
    //
   
    private boolean legacyMessageFormat = true;
   
    public boolean useLegacyMessageFormat()
    {
        return legacyMessageFormat;
    }
   
    @Config(hidden=true)
    public void setLegacyMessageFormat(ConfigurationValue cv, boolean value) throws ConfigurationException
    {
        legacyMessageFormat = value;
    }
       
    //
    // 'create-target-with-errors'
    //
   
    private boolean createTargetWithErrors = false;
   
    public boolean getCreateTargetWithErrors()
    {
        return createTargetWithErrors;
    }
   
    @Config(hidden=true)
    public void setCreateTargetWithErrors(ConfigurationValue cv, boolean value) throws ConfigurationException
    {
        createTargetWithErrors = value;
    }

    //
    // 'flex'
    //
    private boolean isFlex = false;
   
    public boolean isFlex()
    {
        return isFlex;
    }
   
    /**
     * Option to enable or prevent various Flex compiler behaviors. This is
     * currently used to enable/disable the generation of a root class for
     * library swfs and generation of Flex specific code for application swfs.
     */
    @Config(hidden=true)
    public void setFlex(ConfigurationValue cv, boolean value) throws ConfigurationException
    {
        isFlex = value;
    }

    /**
     * Resolve a problem class name to a Java Class.
     *
     * @param className May be fully qualified. If the class name is not fully
     * qualified, it is assumed to live in the "org.apache.flex.compiler.problems" package.
     *
     * @return A class corresponding to the className or null if the class name was not
     * found.
     */
    @SuppressWarnings("unchecked")
    private Class<ICompilerProblem> resolveProblemClassName(String className)
    {
        if (className == null)
            return null;
       
        Class<?> resolvedClass = null;
       
        try
        {
            resolvedClass = Class.forName(className);
        }
        catch (ClassNotFoundException e)
        {
            return null;
        }
       
        if (ICompilerProblem.class.isAssignableFrom(resolvedClass))
            return (Class<ICompilerProblem>)resolvedClass;
        else
            return null;
    }

   
    //
    // 'file-specs' option
    //

    private List<String> fileSpecs = new ArrayList<String>();

    /**
     * Get target file path. Target file is the last file in the
     * {@link #getFileSpecs()}.
     */
    public String getTargetFile()
    {
        if (fileSpecs.isEmpty())
            return null;
        else
            return Iterables.getLast(fileSpecs);
    }
   
    /**
     * @return Path of the target's parent directory.
     */
    public String getTargetFileDirectory()
    {
        final String targetFile = getTargetFile();
        if (targetFile == null)
            return null;
       
        final String normalizedTargetFile = FilenameNormalization.normalize(targetFile);
       
        return FilenameUtils.getFullPathNoEndSeparator(normalizedTargetFile);
    }

    /**
     * This has been added so that unit tests can change the target file.
     * It isn't normally called when running the command line compiler.
     */
    public void setTargetFile(String mainFile)
    {
        fileSpecs.clear();
        fileSpecs.add(mainFile);
    }

    /**
     * @return A list of filespecs. It's the default variable for command line.
     */
    public List<String> getFileSpecs()
    {
        return fileSpecs;
    }

    @Config(allowMultiple = true, hidden = true)
    @Arguments(Arguments.PATH_ELEMENT)
    @InfiniteArguments
    @SoftPrerequisites("flex")
    public void setFileSpecs(ConfigurationValue cv, List<String> args) throws ConfigurationException
    {
        this.fileSpecs.addAll(args);
       
        checkForMxmlFiles(args);
    }

    private void checkForMxmlFiles(List<String> paths)
    {
        // If there are mxml or css files then we are compiling a flex
        // application so enable "flex".
        for (String path : paths)
        {
            if (path.endsWith(".mxml") || path.endsWith(".css"))
                isFlex = true;
        }
    }

    //
    // 'help' option from CommandLineConfiguration
    //

    /**
     * dummy, just a trigger for help text
     */
    @Config(displayed = false, greedy = true)
    @Arguments("keyword")
    @InfiniteArguments
    public void setHelp(ConfigurationValue cv, String[] keywords)
    {

    }

    //
    // 'load-config' option from CommandLineConfiguration
    //

    private String configFile = null;

    /**
     * @return Normalized path to a Flex configuration file.
     */
    public String getLoadConfig()
    {
        return configFile;
    }

    /**
     * Since {@link ConfigurationBuffer} loads the "load-config" files, the
     * value of this configuration option isn't intersting to the rest part of
     * the compiler.
     */
    @Config(allowMultiple = true)
    @Arguments("filename")
    public void setLoadConfig(ConfigurationValue cv, String filename) throws ConfigurationException
    {
        configFile = resolvePathStrict(filename, cv);
    }

    //
    // 'version' option from CommandLineConfiguration
    //

    /**
     * Dummy option. Just a trigger for version info.
     */
    @Config
    public void setVersion(ConfigurationValue cv, boolean value)
    {
    }

    //
    // 'verbose' option from CommandLineConfiguration
    //

    private boolean verbose = false;

    public boolean isVerbose()
    {
        return verbose;
    }

    @Config(hidden = true)
    public void setVerbose(ConfigurationValue cfgval, boolean b)
    {
        verbose = b;
    }

    //
    // 'dump-ast' option from CommandLineConfiguration
    //

    private boolean dumpAst;

    public boolean isDumpAst()
    {
        return dumpAst;
    }

    @Config(hidden = true)
    public void setDumpAst(ConfigurationValue cfgval, boolean b)
    {
        dumpAst = b;
    }

    //
    // 'enable-inlining' option from CommandLineConfiguration
    //
    private boolean enableInlining = false;

    /**
     * @return true if function inlining has been enabled.
     */
    public boolean isInliningEnabled()
    {
        return enableInlining;
    }

    /**
     * Enable or disable function inlining.
     *
     * @param cfgval The configuration value context.
     * @param b true to enable inlining, false otherwise.
     */
    @Config(hidden = true)
    @Mapping({"compiler", "inline"})
    public void setEnableInlining(ConfigurationValue cfgval, boolean b)
    {
        enableInlining = b;
    }

    //
    // 'remove-dead-code' option from CommandLineConfiguration
    //
    private boolean removeDeadCode = false;

    /**
     * @return true if dead code removal has been enabled.
     */
    public boolean getRemoveDeadCode()
    {
        return this.removeDeadCode;
    }

    /**
     * Enable or disable dead code removal.
     * @param cfgval the configuration value context.
     * @param b true to enable dead code removal,
     * false to disable.
     */
    @Config(advanced=true)
    @Mapping({"compiler", "remove-dead-code"})
    public void setRemoveDeadCode(ConfigurationValue cfgval, boolean b)
    {
        this.removeDeadCode = b;
    }

    //
    // Validation methods from ToolsConfiguration
    //
   
    void validateDumpConfig(ConfigurationBuffer configurationBuffer)
            throws ConfigurationException
    {
        if (dumpConfigFile != null)
        {
            final String text = FileConfigurator.formatBuffer(
                    configurationBuffer, "flex-config",
                    LocalizationManager.get(),
                    "flex2.configuration");
            try
            {
                final Writer writer = new FileWriter(dumpConfigFile);
                IOUtils.write(text, writer);
                IOUtils.closeQuietly(writer);
            }
            catch (IOException e)
            {
                throw new ConfigurationException.IOError(dumpConfigFile);
            }
        }
    }

    /**
     * Collection of fatal and non-fatal configuration problems.
     */
    private Collection<ICompilerProblem> configurationProblems = new ArrayList<ICompilerProblem>();
   
    /**
     * Get the configuration problems. This should be called after the configuration
     * has been processed.
     *
     * @return a collection of fatal and non-fatal configuration problems.
     */
    public Collection<ICompilerProblem> getConfigurationProblems()
    {
        return configurationProblems;
    }
   
    private boolean warnOnFlexOnlyOptionUsage = false;

    /**
     * @return True if warnings are generated when "Flex only" options are used,
     * false otherwise.
     */
    public boolean getWarnOnFlexOnlyOptionUsage()
    {
        return warnOnFlexOnlyOptionUsage;
    }
   
    /**
     * Controls if the compiler warns when "Flex only" configuration options
     * are used in the compiler.
     *
     * @param value True to enable warnings, false to disable warnings. The
     * default is to not warn.
     */
    public void setWarnOnFlexOnlyOptionUsage(boolean value)
    {
        this.warnOnFlexOnlyOptionUsage = value;
    }
   
    private void processDeprecatedAndRemovedOptions(ConfigurationBuffer configurationBuffer)
    {
        for (final String var : configurationBuffer.getVars())
        {
            ConfigurationInfo info = configurationBuffer.getInfo(var);
            List<ConfigurationValue> values = configurationBuffer.getVar(var);
            if (values != null)
            {
                for (final ConfigurationValue cv : values)
                {
                    if (info.isRemoved())
                    {
                        //addRemovedConfigurationOptionProblem(cv);
                    }
                    else if (info.isDeprecated() && configurationBuffer.getVar(var) != null)
                    {
                        String replacement = info.getDeprecatedReplacement();
                        String since = info.getDeprecatedSince();
                        DeprecatedConfigurationOptionProblem problem =
                            new DeprecatedConfigurationOptionProblem(var, replacement, since,
                                    cv.getSource(), cv.getLine());
                        configurationProblems.add(problem);
                    }
                    else if (warnOnFlexOnlyOptionUsage && info.isFlexOnly())
                    {
                        FlexOnlyConfigurationOptionNotSupported problem =
                            new FlexOnlyConfigurationOptionNotSupported(var, cv.getSource(),
                                    cv.getLine());
                        configurationProblems.add(problem);
                    }
                }
            }
        }
    }

    /**
     * Add a RemovedConfigurationOptionProblem to the list of configuration
     * problems.
     *
     * @param cv
     */
    private void addRemovedConfigurationOptionProblem(ConfigurationValue cv)
    {
        RemovedConfigurationOptionProblem problem =
            new RemovedConfigurationOptionProblem(cv.getVar(), cv.getSource(), cv.getLine());
        configurationProblems.add(problem);
    }
   
}
TOP

Related Classes of org.apache.flex.compiler.config.Configuration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.