You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cd...@apache.org on 2014/07/22 15:35:58 UTC

[32/51] [partial] - Migrated the directory structure to a more Maven style structure. - Started migrating the Parser from Antlr2+3 and JFlex to Antlr4.

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/MXMLC.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/MXMLC.java b/compiler/src/main/java/org/apache/flex/compiler/clients/MXMLC.java
new file mode 100644
index 0000000..9181c82
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/MXMLC.java
@@ -0,0 +1,1156 @@
+/*
+ *
+ *  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.clients;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.output.CountingOutputStream;
+
+import org.apache.flex.compiler.Messages;
+import org.apache.flex.compiler.clients.problems.CompilerProblemCategorizer;
+import org.apache.flex.compiler.clients.problems.ProblemFormatter;
+import org.apache.flex.compiler.clients.problems.ProblemPrinter;
+import org.apache.flex.compiler.clients.problems.ProblemQuery;
+import org.apache.flex.compiler.clients.problems.WorkspaceProblemFormatter;
+import org.apache.flex.compiler.common.VersionInfo;
+import org.apache.flex.compiler.config.CommandLineConfigurator;
+import org.apache.flex.compiler.config.Configuration;
+import org.apache.flex.compiler.config.ConfigurationBuffer;
+import org.apache.flex.compiler.config.ConfigurationPathResolver;
+import org.apache.flex.compiler.config.ConfigurationValue;
+import org.apache.flex.compiler.config.Configurator;
+import org.apache.flex.compiler.config.ICompilerProblemSettings;
+import org.apache.flex.compiler.config.ICompilerSettingsConstants;
+import org.apache.flex.compiler.config.RSLSettings;
+import org.apache.flex.compiler.config.RSLSettings.RSLAndPolicyFileURLPair;
+import org.apache.flex.compiler.exceptions.ConfigurationException;
+import org.apache.flex.compiler.filespecs.IFileSpecification;
+import org.apache.flex.compiler.internal.common.Counter;
+import org.apache.flex.compiler.internal.config.FlashBuilderConfigurator;
+import org.apache.flex.compiler.internal.config.localization.LocalizationManager;
+import org.apache.flex.compiler.internal.graph.GraphMLWriter;
+import org.apache.flex.compiler.internal.projects.FlexProject;
+import org.apache.flex.compiler.internal.projects.DefinitionPriority.BasePriority;
+import org.apache.flex.compiler.internal.targets.LinkageChecker;
+import org.apache.flex.compiler.internal.targets.SWFTarget;
+import org.apache.flex.compiler.internal.targets.Target;
+import org.apache.flex.compiler.internal.units.ResourceModuleCompilationUnit;
+import org.apache.flex.compiler.internal.units.SourceCompilationUnitFactory;
+import org.apache.flex.compiler.internal.units.StyleModuleCompilationUnit;
+import org.apache.flex.compiler.internal.workspaces.Workspace;
+import org.apache.flex.compiler.problems.ConfigurationProblem;
+import org.apache.flex.compiler.problems.FileIOProblem;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.compiler.problems.InternalCompilerProblem;
+import org.apache.flex.compiler.problems.UnableToBuildSWFProblem;
+import org.apache.flex.compiler.projects.ICompilerProject;
+import org.apache.flex.compiler.targets.ISWFTarget;
+import org.apache.flex.compiler.targets.ITargetReport;
+import org.apache.flex.compiler.targets.ITargetSettings;
+import org.apache.flex.compiler.targets.ITarget.TargetType;
+import org.apache.flex.compiler.tree.as.IASNode;
+import org.apache.flex.compiler.tree.as.IFileNode;
+import org.apache.flex.compiler.units.ICompilationUnit;
+import org.apache.flex.compiler.units.ICompilationUnit.UnitType;
+import org.apache.flex.swc.io.ISWFWriterFactory;
+import org.apache.flex.swf.Header;
+import org.apache.flex.swf.ISWF;
+import org.apache.flex.swf.io.ISWFWriter;
+import org.apache.flex.swf.io.SWFWriterAndSizeReporter;
+import org.apache.flex.utils.FilenameNormalization;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+/**
+ * The entry-point class for mxmlc.
+ */
+public class MXMLC
+{
+    static final String NEWLINE = System.getProperty("line.separator");
+    private static final String SWF_EXT = ".swf";
+    private static final String DEFAULT_VAR = "file-specs";
+    private static final String L10N_CONFIG_PREFIX = "org.apache.flex.compiler.internal.config.configuration";
+
+    /**
+     * Exit code enumerations.
+     */
+    public static enum ExitCode
+    {
+        // NOTE: Negative error codes do not work on OSX.
+        // Therefore the following enum values must be non-negative.
+        SUCCESS(0),
+        PRINT_HELP(1),
+        FAILED_WITH_ERRORS(2),
+        FAILED_WITH_EXCEPTIONS(3),
+        FAILED_WITH_CONFIG_ERRORS(4);
+
+        ExitCode(int code)
+        {
+            assert code >= 0 : "Exit code must be non-negative";
+            this.code = code;
+        }
+
+        final int code;
+        
+        public int getCode()
+        {
+            return code;
+        }
+    }
+
+    /**
+     * Entry point for the <code>mxmlc</code> tool.
+     * 
+     * @param args Command line arguments.
+     */
+    public static void main(final String[] args)
+    {
+        final int exitCode = staticMainNoExit(args);
+        System.exit(exitCode);
+    }
+    
+    /**
+     * Entry point for the {@code <mxmlc>} Ant task.
+     * 
+     * @param args Command line arguments.
+     * @return An exit code.
+     */
+    public static int staticMainNoExit(final String[] args)
+    {
+        final MXMLC mxmlc = new MXMLC();
+        return mxmlc.mainNoExit(args);
+    }
+    
+    /**
+     * Determines whether an exit code should be considered
+     * a fatal failure, such as for an Ant task.
+     * 
+     * @param code A numeric exit code.
+     * @return <code>true</code> if the Ant task failed.
+     */
+    public static boolean isFatalFailure(final int code)
+    {
+        // This method really belongs in ExitCode
+        // but that would complicate FlexTask.
+        return code == ExitCode.FAILED_WITH_ERRORS.getCode() ||
+               code == ExitCode.FAILED_WITH_EXCEPTIONS.getCode() ||
+               code == ExitCode.FAILED_WITH_CONFIG_ERRORS.getCode();
+    }
+    
+    /**
+     * Entry point for when you already have an MXMLC instance.
+     * This is for unit testing.
+     * 
+     * @param args Command line arguments.
+     * @return An exit code.
+     */
+    public int mainNoExit(final String[] args)
+    {
+        return mainNoExit(args, System.err);
+    }
+
+    /**
+     * Entry point for when you already have an MXML instance and want
+     * to redirect <code>System.err</code>. This is for unit testing.
+     * 
+     * @param args Command line arguments.
+     * @param err An {@link OutputStream} to use instead of <code>System.err</code>.
+     * @return An exit code.
+     */
+    @SuppressWarnings("unused")
+    public int mainNoExit(final String[] args, OutputStream err)
+    {
+        startTime = System.nanoTime();
+        
+        ExitCode exitCode = ExitCode.SUCCESS;
+        try
+        {
+            final boolean continueCompilation = configure(args);
+            boolean legacyOutput = config.useLegacyMessageFormat();
+            CompilerProblemCategorizer categorizer = null;
+            
+            if (legacyOutput)
+                categorizer = createProblemCategorizer();
+            
+            ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, categorizer); 
+            
+            ProblemPrinter printer = new ProblemPrinter(formatter, err);
+
+            if (continueCompilation)
+            {
+                compile();
+                exitCode = printProblems(printer, legacyOutput);
+                reportTargetCompletion();
+            }
+            else if (problems.hasFilteredProblems())
+            {
+                printer.printProblems(problems.getFilteredProblems());
+                exitCode = ExitCode.FAILED_WITH_CONFIG_ERRORS;
+            }
+            else
+            {
+                exitCode = ExitCode.PRINT_HELP;
+            }
+        }
+        catch (Exception e)
+        {
+            (new PrintStream(err)).println(e.getMessage());
+            exitCode = ExitCode.FAILED_WITH_EXCEPTIONS;
+        }
+        finally
+        {
+            waitAndClose();
+            
+            if (Counter.COUNT_TOKENS || Counter.COUNT_NODES ||
+                Counter.COUNT_DEFINITIONS || Counter.COUNT_SCOPES)
+            {
+                Counter.getInstance().dumpCounts();
+            }
+        }
+        return exitCode.code;
+    }
+    
+    /**
+     * Entry point for when you already have an MXML instance and just want to
+     * compile and not link. This is for FB integration, but other IDEs could
+     * use this too.
+     * 
+     * @param args Command line arguments.
+     * @param err An {@link OutputStream} to use instead of <code>System.err</code>.
+     * @return An exit code.
+     */
+    @SuppressWarnings("unused")
+    public int mainCompileOnly(final String[] args, OutputStream err)
+    {
+        if (err == null)
+            err = System.err;
+        
+        startTime = System.nanoTime();
+        
+        ExitCode exitCode = ExitCode.SUCCESS;
+        try
+        {
+            final boolean continueCompilation = configure(args);
+            boolean legacyOutput = config.useLegacyMessageFormat();
+            CompilerProblemCategorizer categorizer = null;
+            
+            if (legacyOutput)
+                categorizer = createProblemCategorizer();
+            
+            ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, categorizer); 
+            
+            ProblemPrinter printer = new ProblemPrinter(formatter, err);
+
+            if (continueCompilation)
+            {
+                compile(true); // skip linking
+                exitCode = printProblems(printer, legacyOutput);
+                reportTargetCompletion();
+            }
+            else if (problems.hasFilteredProblems())
+            {
+                printer.printProblems(problems.getFilteredProblems());
+                exitCode = ExitCode.FAILED_WITH_CONFIG_ERRORS;
+            }
+            else
+            {
+                exitCode = ExitCode.PRINT_HELP;
+            }
+        }
+        catch (Exception e)
+        {
+            (new PrintStream(err)).println(e.getMessage());
+            exitCode = ExitCode.FAILED_WITH_EXCEPTIONS;
+        }
+        finally
+        {
+            waitAndClose();
+            
+            if (Counter.COUNT_TOKENS || Counter.COUNT_NODES ||
+                Counter.COUNT_DEFINITIONS || Counter.COUNT_SCOPES)
+            {
+                Counter.getInstance().dumpCounts();
+            }
+        }
+        return exitCode.code;
+    }
+
+    /** 
+     * Print the problems in either the legacy format or the new format.
+     * 
+     * @param printer
+     * @param legacyOutput
+     * @return ExitCode
+     */
+    private ExitCode printProblems(ProblemPrinter printer, boolean legacyOutput)
+    {
+        ExitCode exitCode = ExitCode.SUCCESS;
+        
+        if (legacyOutput)
+        {
+            if (printer.printProblems(problems.getFilteredProblems()) > 0)
+            {
+                if (problems.hasErrors())
+                    exitCode = ExitCode.FAILED_WITH_ERRORS;       
+            }
+        }
+        else
+        {
+            Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>();
+            Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>();
+            
+            problems.getErrorsAndWarnings(errors, warnings);
+            
+            int errorCount = errors.size();
+            int warningCount = warnings.size();
+            if (warningCount > 0)
+            {
+                System.err.println(Messages.getString("MXMLC.WarningsHeader"));
+                printer.printProblems(warnings);                    
+
+            }
+            
+            if (errorCount > 0)
+            {
+                System.err.println(Messages.getString("MXMLC.ErrorsHeader"));
+                printer.printProblems(errors);
+            }
+            
+            // Output summary of errors and warnings
+            if (errorCount == 1)
+                System.err.println(Messages.getString("MXMLC.1_error"));
+            else if (errorCount > 0)
+                System.err.println(Messages.getString("MXMLC.multiple_errors_format", 
+                        Collections.<String,Object>singletonMap("errorCount", errors.size())));
+
+            if (warningCount == 1)
+                System.err.println(Messages.getString("MXMLC.1_warning"));
+            else if (warningCount > 0)
+                System.err.println(Messages.getString("MXMLC.multiple_warnings_format", 
+                        Collections.<String,Object>singletonMap("warningCount", warnings.size())));
+            
+            if (errorCount > 0)
+                exitCode = ExitCode.FAILED_WITH_ERRORS;
+        }
+        
+        return exitCode;
+    }
+
+    public MXMLC()
+    {
+        workspace = new Workspace();
+        project = new FlexProject(workspace);
+        problems = new ProblemQuery();
+    }
+
+    protected Workspace workspace;
+    protected FlexProject project;
+    public Configuration config;
+    public ProblemQuery problems;
+    public ConfigurationBuffer configBuffer;
+
+    protected Configurator projectConfigurator;
+
+    protected ICompilationUnit mainCU;
+    protected SWFTarget target;
+    protected long startTime;     // start time of execution in nanoseconds
+    protected ITargetSettings targetSettings;
+    private ISWF swfTarget;
+    private String swfOutputMessage;
+    
+    /**
+     * Print a message.
+     * 
+     * @param msg Message text.
+     */
+    public void println(final String msg)
+    {
+        System.out.println(msg);
+    }
+
+    /**
+     * Wait till the workspace to finish compilation and close.
+     */
+    protected void waitAndClose()
+    {
+        workspace.startIdleState();
+        try
+        {
+            workspace.close();
+        }
+        finally
+        {
+            workspace.endIdleState(Collections.<ICompilerProject, Set<ICompilationUnit>>emptyMap());
+        }
+    }
+
+    /**
+     * Force terminate the compilation process.
+     */
+    protected void close()
+    {
+        workspace.close();
+    }
+
+    /**
+     * Create a new Configurator. This method may be overridden to allow
+     * Configurator subclasses to be created that have custom configurations.
+     * 
+     * @return a new instance or subclass of {@link Configurator}. 
+     * 
+     */
+    protected Configurator createConfigurator()
+    {
+        return new Configurator();
+    }
+    
+    /**
+     * Load configurations from all the sources.
+     * 
+     * @param args command line arguments
+     * @return True if mxmlc should continue with compilation.
+     */
+    public boolean configure(final String[] args)
+    {
+        projectConfigurator = createConfigurator();
+        
+        try
+        {
+            // Print brief usage if no arguments provided.
+            if (args.length == 0)
+            {
+                final String usage = CommandLineConfigurator.brief(
+                        getProgramName(),
+                        DEFAULT_VAR,
+                        LocalizationManager.get(),
+                        L10N_CONFIG_PREFIX);
+                println(getStartMessage());
+                if (usage != null)
+                    println(usage);
+
+                // Create a default configuration so we can exit gracefully.
+                config = new Configuration();
+                configBuffer = new ConfigurationBuffer(
+                        Configuration.class, Configuration.getAliases());
+                return false;
+            }
+
+            ConfigurationPathResolver resolver = new ConfigurationPathResolver(System.getProperty("user.dir")); 
+            projectConfigurator.setConfigurationPathResolver(resolver);
+            projectConfigurator.setWarnOnFlexOnlyOptionUsage(false);
+            if (useFlashBuilderProjectFiles(args))
+                projectConfigurator.setConfiguration(FlashBuilderConfigurator.computeFlashBuilderArgs(args, getTargetType().getExtension()), 
+                                                        getConfigurationDefaultVariable());
+            else
+                projectConfigurator.setConfiguration(args, getConfigurationDefaultVariable());
+            projectConfigurator.applyToProject(project);
+            getTargetSettings();    // get targetSettings here to flush out any configuration problems.
+            problems = new ProblemQuery(projectConfigurator.getCompilerProblemSettings());
+            
+            // Get the configuration and configBuffer which are now initialized.
+            config = projectConfigurator.getConfiguration();
+            Messages.setLocale(config.getToolsLocale());
+            configBuffer = projectConfigurator.getConfigurationBuffer();
+            problems.addAll(projectConfigurator.getConfigurationProblems());
+
+            // Print version if "-version" is present.
+            if (configBuffer.getVar("version") != null)
+            {
+                println(VersionInfo.buildMessage());
+                return false;
+            }
+
+            // Print help if "-help" is present.
+            final List<ConfigurationValue> helpVar = configBuffer.getVar("help");
+            if (helpVar != null)
+            {
+                processHelp(helpVar);
+                return false;
+            }
+            
+            for (String fileName : projectConfigurator.getLoadedConfigurationFiles())
+            {
+                println(Messages.getString("MXMLC.Loading_configuration_format", 
+                        Collections.<String,Object>singletonMap("configurationName", fileName)));                
+            }
+            
+            // Add a blank line between the configuration list and the rest of 
+            // the output to make the start of the output easier to detect.
+            println(""); 
+            
+            if (config.isVerbose())
+            {
+                for (final IFileSpecification themeFile : project.getThemeFiles())
+                {
+                    println(Messages.getString("MXMLC.Found_theme_file_format", 
+                            Collections.<String, Object>singletonMap("themePath", 
+                                    themeFile.getPath())));
+                }
+            }
+
+            // If we have configuration errors then exit before trying to 
+            // validate the target.
+            if (problems.hasErrors())
+                return false;
+            
+            validateTargetFile();
+            return true;
+        }
+        catch (ConfigurationException e)
+        {
+            final ICompilerProblem problem = new ConfigurationProblem(e);
+            problems.add(problem);
+            return false;
+        }
+        catch (Exception e)
+        {
+            final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, e.getMessage());
+            problems.add(problem);
+            return false;
+        }
+    }
+
+    /**
+     * Get the default variable for this configuration. MXMLC has a default 
+     * variable of "file-spec" and COMPC has default variable of 
+     * "include-classes".
+     * 
+     * @return the default variable for the configuration.
+     */
+    protected String getConfigurationDefaultVariable()
+    {
+        return ICompilerSettingsConstants.FILE_SPECS_VAR;
+    }
+    
+    private boolean useFlashBuilderProjectFiles(String[] args)
+    {
+        for (String arg : args)
+        {
+            if (arg.equals("-fb") || arg.equals("-use-flashbuilder-project-files"))
+                return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Validate target file.
+     * 
+     * @throws ConfigurationException
+     */
+    protected void validateTargetFile() throws ConfigurationException
+    {
+        if(mainCU instanceof ResourceModuleCompilationUnit)
+            return; //when compiling a Resource Module, no target file is defined.
+        
+        final String targetFile = config.getTargetFile();
+        if (targetFile == null)
+            throw new ConfigurationException.MustSpecifyTarget(null, null, -1);
+
+        final File file = new File(targetFile);
+        if (!file.exists())
+            throw new ConfigurationException.IOError(targetFile);
+    }
+
+    /**
+     * Main body of this program. This method is called from the public static
+     * method's for this program.
+     * 
+     * @return true if compiler succeeds
+     */
+    protected boolean compile()
+    {
+        return compile(false);
+    }
+    
+    private boolean compile(boolean skipLinking)
+    {
+        boolean compilationSuccess = false;
+        try
+        {
+            if (!setupTargetFile())
+                return false;
+
+            if (config.isDumpAst())
+                dumpAST();
+
+            buildArtifact();
+
+            if (swfTarget == null)
+                return false;
+
+            // Don't create a swf if there are errors unless a 
+            // developer requested otherwise.
+            if (!config.getCreateTargetWithErrors() && problems.hasErrors())
+                return false;
+
+            if (skipLinking)
+                return true;
+            final File outputFile = new File(getOutputFilePath());
+            final int swfSize = writeSWF(swfTarget, outputFile);
+            long endTime = System.nanoTime();
+            String seconds = String.format("%5.3f", (endTime - startTime) / 1e9);
+            Map<String, Object> params = new HashMap<String, Object>();
+            params.put("byteCount", swfSize);
+            params.put("path", outputFile.getCanonicalPath());
+            params.put("seconds", seconds);
+            swfOutputMessage = Messages.getString("MXMLC.bytes_written_to_file_in_seconds_format",
+                    params);
+            dumpDependencyGraphIfNeeded();
+
+            compilationSuccess = true;
+        }
+        catch (IOException e)
+        {
+            final FileIOProblem problem = new FileIOProblem(e);
+            problems.add(problem);
+        }
+        catch (Exception e)
+        {
+            final ICompilerProblem problem = new InternalCompilerProblem(e);
+            problems.add(problem);
+        }
+
+        return compilationSuccess;
+    }
+
+    /**
+     * Setup theme files.
+     */
+    protected void setupThemeFiles()
+    {
+        project.setThemeFiles(toFileSpecifications(config.getCompilerThemeFiles(), workspace));
+
+        if (config.isVerbose())
+        {
+            for (final IFileSpecification themeFile : project.getThemeFiles())
+            {
+                verboseMessage(Messages.getString("MXMLC.Found_theme_file_format", 
+                        Collections.<String,Object>singletonMap("themePath", themeFile.getPath())));
+            }
+        }
+    }
+
+    /**
+     * Reports the size and location of the target that was created.
+     * @throws InterruptedException 
+     * 
+     */
+    protected void reportTargetCompletion() throws InterruptedException
+    {
+        if (swfOutputMessage != null)
+        {
+            reportRequiredRSLs(target);
+            println(swfOutputMessage);
+        }
+    }
+
+    /**
+     * Set up any user defines customization of the problem severities.
+     * 
+     */
+    private CompilerProblemCategorizer createProblemCategorizer()
+    {
+        ICompilerProblemSettings problemSettings = null;
+        try
+        {
+            problemSettings = projectConfigurator.getCompilerProblemSettings();
+        }
+        catch (Exception e)
+        {
+            // Create a categorizer that will only use default settings.
+        }
+
+        return new CompilerProblemCategorizer(problemSettings);
+    }
+
+    /**
+     * Parse all source files and dumpAST
+     * 
+     * @throws InterruptedException
+     */
+    private void dumpAST() throws InterruptedException
+    {
+        final List<String> astDump = new ArrayList<String>();
+        final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
+        final ImmutableList<ICompilationUnit> compilationUnits = target.getReachableCompilationUnits(problems);
+        for (final ICompilationUnit compilationUnit : compilationUnits)
+        {
+            final IASNode ast = compilationUnit.getSyntaxTreeRequest().get().getAST();
+            if (ast != null)
+            {
+                ((IFileNode)ast).populateFunctionNodes();
+                astDump.add(ast.toString());
+            }
+        }
+
+        println(Joiner.on("\n\n").join(astDump));
+    }
+
+    /**
+     * Build target artifact.
+     * 
+     * @throws InterruptedException threading error
+     * @throws IOException IO error
+     */
+    protected void buildArtifact() throws InterruptedException, IOException
+    {
+        swfTarget = buildSWFModel();
+    }
+
+    /**
+     * Build SWF model object and collect problems building SWF in
+     * {@link #problems}.
+     * 
+     * @return SWF model or null if SWF can't be built.
+     * @throws InterruptedException concurrency problem
+     */
+    private ISWF buildSWFModel() throws InterruptedException
+    {
+        final List<ICompilerProblem> problemsBuildingSWF =
+                new ArrayList<ICompilerProblem>();
+        final ISWF swf = target.build(problemsBuildingSWF);
+        problems.addAll(problemsBuildingSWF);
+        if (swf == null)
+        {
+            ICompilerProblem problem = new UnableToBuildSWFProblem(getOutputFilePath());
+            problems.add(problem);
+        }
+        
+        return swf;
+    }
+
+    private void reportRequiredRSLs(ISWFTarget target) throws InterruptedException
+    {
+        // Report the required RSLs:
+        if (hasRSLs())
+        {
+            ITargetReport report = target.getTargetReport();
+            
+            if (report == null)
+                return;     // target must not have been built.
+            
+            List<RSLSettings> requiredRSLs = report.getRequiredRSLs();
+            List<String> legacyRSLs = targetSettings.getRuntimeSharedLibraries();
+            
+            if (requiredRSLs.isEmpty() && legacyRSLs.isEmpty())
+                return;
+            
+            println(Messages.getString("MXMLC.Required_RSLs"));                
+            
+            // loop thru the RSLs and print out the required RSLs.
+            for (RSLSettings rslSettings : requiredRSLs)
+            {
+                List<RSLAndPolicyFileURLPair>rslURLs = rslSettings.getRSLURLs();
+                Map<String,Object> params = new HashMap<String,Object>();
+                params.put("rslPath",rslURLs.get(0).getRSLURL());
+                
+                switch (rslURLs.size())
+                {
+                    case 0:
+                        assert false; // One RSL URL is required.
+                        break;
+                    case 1:
+                        println(Messages.getString("MXMLC.required_rsl_url_format", 
+                                params));
+                        break;
+                    case 2:
+                        println(Messages.getString("MXMLC.required_rsl_url_with_1_failover_format",
+                                params));
+                        break;
+                    default:
+                        params.put("failoverCount", rslURLs.size() - 1);
+                        println(Messages.getString("MXMLC.required_rsl_url_with_multiple_failovers_format", 
+                                params));
+                        break;
+                }
+
+            }
+            
+            // All -runtime-shared-libraries are required
+            for (String rslURL : legacyRSLs)
+                println(Messages.getString("MXMLC.required_rsl_url_format", 
+                        Collections.<String,Object>singletonMap("rslPath", rslURL)));
+        }
+    }
+
+    /**
+     * Virtual method that returns the type of target we are building.
+     * Subclasses will override this method to return different target types.
+     * 
+     * @return The {@link TargetType} of the target we are building.
+     */
+    protected TargetType getTargetType()
+    {
+        return TargetType.SWF;
+    }
+    
+    private ITargetSettings getTargetSettings()
+    {
+        if (targetSettings == null)
+            targetSettings = projectConfigurator.getTargetSettings(getTargetType());
+        
+        return targetSettings;
+    }
+
+    private boolean hasRSLs()
+    {
+        return (getTargetSettings().getRuntimeSharedLibraryPath().size() > 0) ||
+               (getTargetSettings().getRuntimeSharedLibraryPath().size() > 0);
+    }
+
+    /**
+     * Write out SWF file and return file size in bytes.
+     * 
+     * @param swf SWF model
+     * @param outputFile output SWF file handle
+     * @return SWF file size in bytes
+     * @throws IOException error
+     */
+    private int writeSWF(final ISWF swf, final File outputFile) throws IOException
+    {
+  
+        final Header.Compression compression = Header.decideCompression(
+                targetSettings.useCompression(), 
+                targetSettings.getSWFVersion(),
+                targetSettings.isDebugEnabled());
+        final ISWFWriterFactory writerFactory = SWFWriterAndSizeReporter.getSWFWriterFactory(
+                targetSettings.getSizeReport()); 
+        final ISWFWriter writer = writerFactory.createSWFWriter(swf, compression, targetSettings.isDebugEnabled());
+        
+        return writer.writeTo(outputFile);
+    }
+
+    /**
+     * MXMLC uses target file as the main compilation unit and derive the output
+     * SWF file name from this file.
+     *
+     * @return true if successful, false otherwise.
+     * 
+     * @throws InterruptedException
+     */
+    protected boolean setupTargetFile() throws InterruptedException
+    {
+        final String mainFileName = config.getTargetFile();
+
+        if(mainFileName != null)
+        {
+            final String normalizedMainFileName = FilenameNormalization.normalize(mainFileName);
+            
+            // Can not add a SourceHandler for *.css file because we don't want
+            // to create compilation units for CSS files on the source path.
+            if (mainFileName.toLowerCase().endsWith(".css"))
+            {
+                mainCU = new StyleModuleCompilationUnit(
+                        project, 
+                        workspace.getFileSpecification(normalizedMainFileName), 
+                        BasePriority.SOURCE_LIST);
+                // TODO: Use CSS file name once CSS module runtime code is finalized.
+                config.setMainDefinition("CSSModule2Main");
+                project.addCompilationUnitsAndUpdateDefinitions(
+                        Collections.singleton(mainCU));
+            }
+            else
+            {
+                final SourceCompilationUnitFactory compilationUnitFactory =
+                    project.getSourceCompilationUnitFactory();
+                File normalizedMainFile = new File(normalizedMainFileName);
+                if (compilationUnitFactory.canCreateCompilationUnit(normalizedMainFile))
+                {
+                    // Remove the main file from the source path and put it on the 
+                    // source list. The only reason this needs to be done is to
+                    // prevent the compilation unit of the main file from shadowing
+                    // another compilation unit with the same qname. This can 
+                    // happen in the odd case where you have test.mxml (main file)
+                    // and test.as in the same directory and test.mxml's compilation
+                    // unit end up shadowing test.as's cu.
+                    project.removeSourceFile(normalizedMainFile);
+                    project.addIncludeSourceFile(normalizedMainFile, true);
+                    
+                    Collection<ICompilationUnit> mainFileCompilationUnits =
+                        workspace.getCompilationUnits(normalizedMainFileName, project);
+                    
+                    assert mainFileCompilationUnits.size() == 1;
+                    mainCU = Iterables.getOnlyElement(mainFileCompilationUnits);
+                }
+            }
+        }
+        else 
+        {
+            final List<ICompilerProblem> resourceBundleProblems = new ArrayList<ICompilerProblem>();
+            Collection<ICompilationUnit> includedResourceBundles = target.getIncludedResourceBundlesCompilationUnits(resourceBundleProblems);
+            problems.addAll(resourceBundleProblems);
+
+            if(includedResourceBundles.size() > 0)
+            {
+                //This means that a Resource Module is requested to be built.
+                mainCU = new ResourceModuleCompilationUnit(project, 
+                        "GeneratedResourceModule", 
+                        includedResourceBundles, 
+                        BasePriority.SOURCE_LIST);
+                config.setMainDefinition("GeneratedResourceModule");
+                project.addCompilationUnitsAndUpdateDefinitions(
+                        Collections.singleton(mainCU));
+            }
+        }
+                
+        Preconditions.checkNotNull(mainCU, "Main compilation unit can't be null");
+
+        if (getTargetSettings() == null)
+            return false;            
+        
+        target = (SWFTarget)project.createSWFTarget(getTargetSettings(), null);
+        
+        return true;
+    }
+
+    /**
+     * Setups the locale related settings.
+     */
+    protected void setupLocaleSettings()
+    {
+        project.setLocales(config.getCompilerLocales());
+        project.setLocaleDependentResources(config.getLocaleDependentSources());
+    }
+
+    /**
+     * Get the output file path. If {@code -output} is specified, use its value;
+     * otherwise, use the same base name as the target file.
+     * 
+     * @return output file path
+     */
+    private String getOutputFilePath()
+    {
+        if (config.getOutput() == null)
+            return FilenameUtils.removeExtension(config.getTargetFile()).concat(SWF_EXT);
+        else
+            return config.getOutput();
+    }
+
+    private void verboseMessage(String s)
+    {
+        if (config.isVerbose())
+            println(s);
+    }
+
+    /**
+     * Convert file path strings to {@code File} objects. Null values are
+     * discarded.
+     * 
+     * @param paths list of paths.
+     * @return List of File objects. No null values will be returned.
+     */
+    public static List<File> toFiles(final List<String> paths)
+    {
+        final List<File> result = new ArrayList<File>();
+        for (final String path : paths)
+        {
+            if (path != null)
+                result.add(new File(path));
+        }
+        return result;
+    }
+
+    /**
+     * Resolve a list of normalized paths to {@link IFileSpecification} objects
+     * from the given {@code workspace}.
+     * 
+     * @param paths A list of normalized paths.
+     * @param workspace Workspace.
+     * @return A list of file specifications.
+     */
+    public static List<IFileSpecification> toFileSpecifications(
+            final List<String> paths,
+            final Workspace workspace)
+    {
+        return Lists.transform(paths, new Function<String, IFileSpecification>()
+        {
+            @Override
+            public IFileSpecification apply(final String path)
+            {
+                return workspace.getFileSpecification(path);
+            }
+        });
+    }
+
+    /**
+     * Get my program name.
+     * 
+     * @return always "mxmlc".
+     */
+    protected String getProgramName()
+    {
+        return "mxmlc";
+    }
+
+    /**
+     * Get the start up message that contains the program name 
+     * with the copyright notice.
+     * 
+     * @return The startup message.
+     */
+    protected String getStartMessage()
+    {
+        // This message should not be localized.
+        String message = "Apache Flex MXML and ActionScript Compiler (mxmlc)" + NEWLINE + 
+            VersionInfo.buildMessage() + NEWLINE;
+        return message;
+    }
+
+    /**
+     * Print detailed help information if -help is provided.
+     */
+    private void processHelp(final List<ConfigurationValue> helpVar)
+    {
+        final Set<String> keywords = new LinkedHashSet<String>();
+        for (final ConfigurationValue val : helpVar)
+        {
+            for (final Object element : val.getArgs())
+            {
+                String keyword = (String)element;
+                while (keyword.startsWith("-"))
+                    keyword = keyword.substring(1);
+                keywords.add(keyword);
+            }
+        }
+
+        if (keywords.size() == 0)
+            keywords.add("help");
+
+        final String usages = CommandLineConfigurator.usage(
+                    getProgramName(),
+                    DEFAULT_VAR,
+                    configBuffer,
+                    keywords,
+                    LocalizationManager.get(),
+                    L10N_CONFIG_PREFIX);
+        println(getStartMessage());
+        println(usages);
+    }
+
+    /**
+     * "compc" subclass will override this method.
+     * 
+     * @return False if the client is not "compc".
+     */
+    protected boolean isCompc()
+    {
+        return false;
+    }
+    
+    private void dumpDependencyGraphIfNeeded() throws IOException, InterruptedException
+    {
+        File dependencyGraphOutput = config.getDependencyGraphOutput();
+        if (dependencyGraphOutput != null)
+        {
+            LinkageChecker linkageChecker = new LinkageChecker(project, getTargetSettings());
+            Target.RootedCompilationUnits rootedCompilationUnits = target.getRootedCompilationUnits();
+            problems.addAll(rootedCompilationUnits.getProblems());
+            GraphMLWriter dependencyGraphWriter = 
+                new GraphMLWriter(project.getDependencyGraph(), 
+                        rootedCompilationUnits.getUnits(), true,
+                        linkageChecker);
+            BufferedOutputStream graphStream = new BufferedOutputStream(new FileOutputStream(dependencyGraphOutput));
+            LinkedList<ICompilerProblem> problemList = new LinkedList<ICompilerProblem>();
+            Iterables.addAll(problemList, rootedCompilationUnits.getProblems());
+            dependencyGraphWriter.writeToStream(graphStream, problemList);
+            problems.addAll(problemList);
+        }
+    }
+    
+    public ProblemQuery getProblems()
+    {
+        return problems;
+    }
+
+    public List<String> getSourceList()
+    {
+        ArrayList<String> list = new ArrayList<String>();
+        LinkedList<ICompilerProblem> problemList = new LinkedList<ICompilerProblem>();
+        try
+        {
+            ImmutableList<ICompilationUnit> units = target.getReachableCompilationUnits(problemList);
+            for (ICompilationUnit unit : units)
+            {
+                UnitType ut = unit.getCompilationUnitType();
+                if (ut == UnitType.AS_UNIT || ut == UnitType.MXML_UNIT)
+                {
+                    list.add(unit.getAbsoluteFilename());
+                }
+            }
+        }
+        catch (InterruptedException e)
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        
+        return list;
+    }
+    
+    public String getMainSource()
+    {
+        return mainCU.getAbsoluteFilename();
+    }
+    
+    public ISWF getSWFTarget()
+    {
+        return swfTarget;
+    }
+    
+    public int writeSWF(OutputStream outputStream)
+    {
+        
+        final Header.Compression compression = Header.decideCompression(
+                targetSettings.useCompression(), 
+                targetSettings.getSWFVersion(),
+                targetSettings.isDebugEnabled());
+        final ISWFWriterFactory writerFactory = SWFWriterAndSizeReporter.getSWFWriterFactory(
+                targetSettings.getSizeReport()); 
+        final ISWFWriter writer = writerFactory.createSWFWriter(swfTarget, compression, targetSettings.isDebugEnabled());
+        
+        // Write out the SWF, counting how many bytes were written.
+        final CountingOutputStream output =
+                new CountingOutputStream(outputStream);
+
+        writer.writeTo(output);
+        final int swfSize = output.getCount();
+        return swfSize;
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/Optimizer.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/Optimizer.java b/compiler/src/main/java/org/apache/flex/compiler/clients/Optimizer.java
new file mode 100644
index 0000000..eaa442a
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/Optimizer.java
@@ -0,0 +1,501 @@
+/*
+ *
+ *  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.clients;
+
+import org.apache.flex.compiler.Messages;
+import org.apache.flex.compiler.clients.problems.ProblemFormatter;
+import org.apache.flex.compiler.clients.problems.ProblemPrinter;
+import org.apache.flex.compiler.clients.problems.ProblemQuery;
+import org.apache.flex.compiler.clients.problems.WorkspaceProblemFormatter;
+import org.apache.flex.compiler.common.VersionInfo;
+import org.apache.flex.compiler.config.CommandLineConfigurator;
+import org.apache.flex.compiler.config.Configuration;
+import org.apache.flex.compiler.config.ConfigurationBuffer;
+import org.apache.flex.compiler.config.ConfigurationPathResolver;
+import org.apache.flex.compiler.config.ConfigurationValue;
+import org.apache.flex.compiler.config.Configurator;
+import org.apache.flex.compiler.exceptions.ConfigurationException;
+import org.apache.flex.compiler.internal.config.IConfigurationFilter;
+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.localization.LocalizationManager;
+import org.apache.flex.compiler.internal.projects.CompilerProject;
+import org.apache.flex.compiler.internal.projects.FlexProject;
+import org.apache.flex.compiler.internal.targets.AppSWFTarget;
+import org.apache.flex.compiler.internal.targets.Target;
+import org.apache.flex.compiler.internal.workspaces.Workspace;
+import org.apache.flex.compiler.problems.ConfigurationProblem;
+import org.apache.flex.compiler.problems.FileNotFoundProblem;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.compiler.targets.ITargetProgressMonitor;
+import org.apache.flex.compiler.targets.ITargetSettings;
+import org.apache.flex.swc.io.ISWFWriterFactory;
+import org.apache.flex.swf.Header;
+import org.apache.flex.swf.ISWF;
+import org.apache.flex.swf.io.ISWFWriter;
+import org.apache.flex.swf.io.SWFReader;
+import org.apache.flex.swf.io.SWFWriterAndSizeReporter;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * Command line optimizer - can read in a swf, apply the optimizations usually done during swf linking,
+ * and write out the swf again.
+ */
+public class Optimizer
+{
+    static final String NEWLINE = System.getProperty("line.separator");
+    private static final String DEFAULT_VAR = "input";
+    private static final String L10N_CONFIG_PREFIX = "org.apache.flex.compiler.internal.config.configuration";
+
+    /**
+     * Entry point for <code>optimizer</code> tool.
+     *
+     * @param args command line arguments
+     */
+    public static void main(final String[] args)
+    {
+        int exitCode = staticMainNoExit(args);
+        System.exit(exitCode);
+    }
+
+
+    public static int staticMainNoExit(final String[] args)
+    {
+        final Optimizer optimizer = new Optimizer();
+        return optimizer.mainNoExit(args);
+    }
+
+
+    public int mainNoExit(final String[] args)
+    {
+        int result = 0;
+        long startTime = System.nanoTime();
+        
+        if( configure(args) )
+        {
+            ITargetSettings targetSettings = getTargetSettings();
+
+            final File inputFile = new File(getInputFilePath());
+            File outputFile = new File(config.getOutput());
+            
+            InputStream in = null;
+            try
+            {
+                if( inputFile.exists() )
+                {
+                    // Read in the SWF
+                    in = new BufferedInputStream(new FileInputStream(inputFile));
+                    SWFReader reader = new SWFReader();
+                    try
+                    {
+                        ISWF swf = reader.readFrom(in, getInputFilePath());
+                        // record any problems encountered reading the swf
+                        problems.addAll(reader.getProblems());
+
+                        List<ICompilerProblem> linkProblems = new ArrayList<ICompilerProblem>();
+    
+                        // do the optimization
+                        OptimizerSWFTarget target = new OptimizerSWFTarget(swf, project, targetSettings, null);
+                        ISWF optimizedSWF = target.build(linkProblems);
+
+                        // record any problems found
+                        problems.addAll(linkProblems);
+
+                        if( !problems.hasErrors() )
+                        {
+                            Header.Compression compression = Header.decideCompression(true, swf.getVersion(), false);
+    
+                            final ISWFWriterFactory writerFactory = SWFWriterAndSizeReporter.getSWFWriterFactory(
+                                    targetSettings.getSizeReport());
+                            final ISWFWriter writer = writerFactory.createSWFWriter(optimizedSWF, compression, targetSettings.isDebugEnabled());
+                            int swfSize = writer.writeTo(outputFile);
+            
+                            long endTime = System.nanoTime();
+                            String seconds = String.format("%5.3f", (endTime - startTime) / 1e9);
+                            Map<String, Object> params = new HashMap<String, Object>();
+                            params.put("byteCount", swfSize);
+                            params.put("path", outputFile.getCanonicalPath());
+                            params.put("seconds", seconds);
+                            System.out.println(Messages.getString(
+                                    "MXMLC.bytes_written_to_file_in_seconds_format",
+                                    params));
+                        }
+                    }
+                    finally
+                    {
+                        IOUtils.closeQuietly(reader);
+                    }   
+                }
+                else
+                {
+                    problems.add(new FileNotFoundProblem(inputFile.getAbsolutePath()));
+                }
+            }
+            catch(IOException ioe)
+            {
+                final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, ioe.getMessage());
+                problems.add(problem);
+            }
+            finally
+            {
+                if ( in != null )
+                {
+                    try
+                    {
+                        in.close();
+                    }
+                    catch(IOException ioe)
+                    {
+                        final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, ioe.getMessage());
+                        problems.add(problem);
+                    }
+                }
+            }
+        }
+        else
+        {
+            result = 1;
+        }
+
+        // Print out any errors we may have encountered
+        ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, null);
+
+        ProblemPrinter printer = new ProblemPrinter(formatter, System.err);
+        printer.printProblems(problems.getFilteredProblems());
+
+        if( problems.hasErrors() )
+            result = 1;
+
+        return result;
+    }
+
+    public Optimizer()
+    {
+        workspace = new Workspace();
+        project = new FlexProject(workspace);
+        problems = new ProblemQuery();
+    }
+
+    protected boolean configure(String[] args)
+    {
+        projectConfigurator = createConfigurator();
+
+        try
+        {
+            // Print brief usage if no arguments provided.
+            if (args.length == 0)
+            {
+                final String usage = CommandLineConfigurator.brief(
+                        getProgramName(),
+                        DEFAULT_VAR,
+                        LocalizationManager.get(),
+                        L10N_CONFIG_PREFIX);
+                System.out.println(getStartMessage());
+                if (usage != null)
+                    System.out.println(usage);
+
+                // Create a default configuration so we can exit gracefully.
+                config = new OptimizerConfiguration();
+                return false;
+            }
+
+            ConfigurationPathResolver resolver = new ConfigurationPathResolver(System.getProperty("user.dir")); 
+            projectConfigurator.setConfigurationPathResolver(resolver);
+            projectConfigurator.setConfiguration(args, getConfigurationDefaultVariable());
+
+            projectConfigurator.applyToProject(project);
+            config = (OptimizerConfiguration)projectConfigurator.getConfiguration();
+            configBuffer = projectConfigurator.getConfigurationBuffer();
+            config.setCompilerOptimize(null, true);
+
+            problems.addAll(projectConfigurator.getConfigurationProblems());
+
+            // Print version if "-version" is present.
+            if (configBuffer.getVar("version") != null)
+            {
+                System.out.println(VersionInfo.buildMessage());
+                return false;
+            }
+
+            // Print help if "-help" is present.
+            final List<ConfigurationValue> helpVar = configBuffer.getVar("help");
+            if (helpVar != null)
+            {
+                processHelp(helpVar);
+                return false;
+            }
+            
+            if( problems.hasErrors() )
+                return false;
+        }
+        catch (Exception e)
+        {
+            final ICompilerProblem problem = new ConfigurationProblem(null, -1, -1, -1, -1, e.getMessage());
+            problems.add(problem);
+            return false;
+        }
+
+        return true;
+
+    }
+
+    /**
+     * Print detailed help information if -help is provided.
+     */
+    private void processHelp(final List<ConfigurationValue> helpVar)
+    {
+        final Set<String> keywords = new LinkedHashSet<String>();
+        for (final ConfigurationValue val : helpVar)
+        {
+            for (final Object element : val.getArgs())
+            {
+                String keyword = (String)element;
+                while (keyword.startsWith("-"))
+                    keyword = keyword.substring(1);
+                keywords.add(keyword);
+            }
+        }
+
+        if (keywords.size() == 0)
+            keywords.add("help");
+
+        final String usages = CommandLineConfigurator.usage(
+                    getProgramName(),
+                    DEFAULT_VAR,
+                    configBuffer,
+                    keywords,
+                    LocalizationManager.get(),
+                    L10N_CONFIG_PREFIX);
+        System.out.println(getStartMessage());
+        System.out.println(usages);
+    }
+    
+    /**
+     * Get the start up message that contains the program name 
+     * with the copyright notice.
+     * 
+     * @return
+     */
+    private String getStartMessage()
+    {
+        // This must not be localized.
+        String message = "Apache ActionScript Compiler (optimizer)" + NEWLINE +
+            VersionInfo.buildMessage() + NEWLINE;
+        return message;
+    }
+
+    /**
+     * @return the input file path
+     */
+    private String getInputFilePath()
+    {
+        return config.getInput();
+    }
+
+    /**
+     * @return  the target settings specified by the configuration
+     */
+    private ITargetSettings getTargetSettings()
+    {
+        if (targetSettings == null)
+            targetSettings = projectConfigurator.getTargetSettings(getTargetType());
+
+        return targetSettings;
+    }
+
+    private Workspace workspace;
+    private FlexProject project;
+    private Configurator projectConfigurator;
+    private OptimizerConfiguration config;
+    private ITargetSettings targetSettings;
+    private ProblemQuery problems;
+    protected ConfigurationBuffer configBuffer;
+
+    protected String getConfigurationDefaultVariable()
+    {
+        return "input";
+    }
+
+    protected Configurator createConfigurator()
+    {
+        return new OptimizerConfigurator(OptimizerConfiguration.class);
+    }
+
+
+    /**
+     * @return always "optimizer"
+     */
+    protected String getProgramName()
+    {
+        return "optimizer";
+    }
+
+    /**
+     */
+    protected Target.TargetType getTargetType()
+    {
+        return Target.TargetType.SWF;
+    }
+
+    /**
+     * Configuration class - adds the input option
+     */
+    public static class OptimizerConfiguration extends Configuration
+    {
+        //
+        // 'input' option
+        //
+
+        private String input;
+
+        public String getInput()
+        {
+            return input;
+        }
+
+        @Config(isRequired=true)
+        @Arguments("filename")
+        public void setInput(ConfigurationValue val, String output) throws ConfigurationException
+        {
+            this.input = getOutputPath(val, output);
+        }
+        
+        //
+        // 'output' option
+        //
+        
+        private String output = "output.swf";
+        
+        @Override
+        public String getOutput()
+        {
+            return output;
+        }
+
+        @Override
+        @Config
+        @Arguments("filename")        
+        public void setOutput(ConfigurationValue val, String output) throws ConfigurationException
+        {
+            this.output = output;
+        }
+        
+    }
+
+    /**
+     * Optimizer configurator - provides overrides to create the right config buffer,
+     * and defaults for the optimizer
+     */
+    public static class OptimizerConfigurator extends Configurator
+    {
+        public OptimizerConfigurator(Class<? extends Configuration> configurationClass)
+        {
+            super(configurationClass);
+        }
+
+        /**
+         * Set of vars that can be set from the command line
+         */
+        private static Set<String> configVars = ImmutableSet.<String>of("help", "version", "load-config", "input", "output",
+                "compiler.keep-as3-metadata", "compiler.debug");
+
+        /**
+         * Create a config filter that will filter out parameters that the optimizer doesn't accept
+         */
+        protected IConfigurationFilter createConfigFilter()
+        {
+            return new IConfigurationFilter()
+            {
+                @Override
+                public boolean select(String name)
+                {
+                    return configVars.contains(name);
+                }
+            };
+        }
+
+        /**
+         * Create the config buffer with the right filter
+         */
+        @Override
+        protected ConfigurationBuffer createConfigurationBuffer(Class<? extends Configuration> configClass)
+        {
+            return new ConfigurationBuffer(configClass, Collections.<String, String>emptyMap(), createConfigFilter());
+        }
+
+        /**
+         * Load default values
+         */
+        @Override
+        protected void loadDefaults (ConfigurationBuffer cfgbuf) throws ConfigurationException
+        {
+        }
+
+        /**
+         * By-pass the configurations that requires Flex SDK.
+         *
+         * This method is needed here because the baseclass version tries to set some config vars
+         * that don't exist in an optimizer config.
+         */
+        @Override
+        protected void byPassConfigurationsRequiringFlexSDK() throws ConfigurationException
+        {
+        }
+    }
+
+    /**
+     * A SWF target that will start with a SWF, and simply link it to build it
+     */
+    public static class OptimizerSWFTarget extends AppSWFTarget
+    {
+        /**
+         * The swf to optimize
+         */
+        private ISWF swf;
+
+        @Override
+        public ISWF build(Collection<ICompilerProblem> problems)
+        {
+            return linkSWF(swf);
+        }
+
+        public OptimizerSWFTarget (ISWF swf, CompilerProject project, ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor)
+        {
+            super(project, targetSettings, progressMonitor);
+            this.swf = swf;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/package.html
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/package.html b/compiler/src/main/java/org/apache/flex/compiler/clients/package.html
new file mode 100644
index 0000000..e1e04c5
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/package.html
@@ -0,0 +1,33 @@
+<!--
+
+  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.
+
+-->
+
+<html>
+<body>
+
+This package contains classes implementing four compiler command-line tools:
+<code>MXMLC</code>, <code>COMPC</code>, <code>ASC</code>, and <code>Optimizer</code>.
+
+<p>
+For example, the <code>mxmlc</code> command-line shell script or batch file
+simply runs the main entry point of <code>MXMLC</code>, namely
+<code>public static void main(String[] args)</code>.
+</p>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/CompilerProblemCategorizer.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/problems/CompilerProblemCategorizer.java b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/CompilerProblemCategorizer.java
new file mode 100644
index 0000000..0e45379
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/CompilerProblemCategorizer.java
@@ -0,0 +1,187 @@
+/*
+ *
+ *  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.clients.problems;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.flex.compiler.config.ICompilerProblemSettings;
+import org.apache.flex.compiler.problems.CompilerProblemClassification;
+import org.apache.flex.compiler.problems.CompilerProblemSeverity;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.compiler.problems.annotations.DefaultSeverity;
+import org.apache.flex.compiler.problems.annotations.ProblemClassification;
+
+/**
+ * Class used to determine if an {@link ICompilerProblem} should be reported
+ * as an error or a warning.
+ * 
+ * A user can override the default severity of a problem by using the following
+ * compiler options:
+ *     -error-problems
+ *     -warning-problems
+ *     -ignore-problems
+ */
+public class CompilerProblemCategorizer
+{
+
+    /**
+     * Default constructor.
+     * 
+     * This categorizer can only consider the default severity of problems.
+     */
+    public CompilerProblemCategorizer()
+    {
+        this(null);
+    }
+    
+    /**
+     * This categorizer can look at user configured overrides of problem
+     * severity, falling back to the default severity.
+     * 
+     * @param problemSettings configuration settings that effect how problems
+     * are categorized. If null, only the default severity of problems will
+     * be used.
+     */
+    public CompilerProblemCategorizer(ICompilerProblemSettings problemSettings)
+    {
+        super();
+        
+        if (problemSettings != null)
+        {
+            addToUserSeverity(problemSettings.getErrorProblems(), CompilerProblemSeverity.ERROR);
+            addToUserSeverity(problemSettings.getWarningProblems(), CompilerProblemSeverity.WARNING);
+            addToUserSeverity(problemSettings.getIgnoreProblems(), CompilerProblemSeverity.IGNORE);
+            showWarnings = problemSettings.showWarnings();
+        }
+        else
+        {
+            showWarnings = true;
+        }
+    }
+    
+    /**
+     * User defined severity to override the default severity.
+     */
+    private Map<Class<? extends ICompilerProblem>, CompilerProblemSeverity> userSeverity;
+    
+    /**
+     * If false, all problems with a severity of 'warning' will be changed to
+     * 'ignore'. See ignoreWarningsIfRequired().
+     */
+    private final boolean showWarnings;
+    
+    /**
+     * Get the severity of the compiler problem. Check if the user overrode
+     * any of the problem severities. If not, return that severity. Otherwise
+     * return the default severity.
+     * 
+     * @param problem the compiler problem. May not be null.
+     * @return the severity of the problem.
+     * 
+     * @throws NullPointerException if problem is null.
+     */
+    public CompilerProblemSeverity getProblemSeverity(ICompilerProblem problem)
+    {
+        if (problem == null)
+            throw new NullPointerException("problem may not be null");
+        
+        CompilerProblemSeverity severity = null;
+        Class<?> problemClass = problem.getClass();
+        
+        if (userSeverity != null)
+        {
+            while (problemClass != null)
+            {
+                severity = userSeverity.get(problemClass);
+                if (severity != null)
+                    return ignoreWarningsIfRequired(severity);
+
+                problemClass = problemClass.getSuperclass();
+            }
+        }
+
+        // get the default severity
+        DefaultSeverity defaultSeverity = problem.getClass().getAnnotation(DefaultSeverity.class);
+        assert defaultSeverity != null;
+        
+        if (defaultSeverity != null)
+            return ignoreWarningsIfRequired(defaultSeverity.value());
+        
+        return CompilerProblemSeverity.ERROR;
+    }
+    
+    
+    public CompilerProblemClassification getProblemClassification(ICompilerProblem problem)
+    {
+        if (problem == null)
+            throw new NullPointerException("problem may not be null");
+        
+        // get the classification from the problem annotation
+        ProblemClassification classification  = problem.getClass().getAnnotation(ProblemClassification.class);
+        assert classification != null;
+        
+        CompilerProblemClassification ret = CompilerProblemClassification.DEFAULT;
+        if (classification != null)
+        {
+           ret = classification.value();
+        }
+        
+        return ret;
+    }
+    
+    /**
+     * Ignore a warning if we are not supposed to show warnings.
+     * 
+     * @param severity
+     * @return if showWarnings is true return input severity. 
+     * If showWarnings is false then if the severity is 'warning' 
+     * then 'ignore' is returned.
+     */
+    private CompilerProblemSeverity ignoreWarningsIfRequired(CompilerProblemSeverity severity)
+    {
+        if (!showWarnings && severity == CompilerProblemSeverity.WARNING)
+        {
+            return CompilerProblemSeverity.IGNORE;
+        }
+        
+        return severity;
+    }
+    
+    /**
+     * Add user defined severity to a collection that overrides the default 
+     * severity.
+     * 
+     * @param problems
+     * @param severity
+     */
+    private void addToUserSeverity(Collection<Class<ICompilerProblem>> problems, 
+            CompilerProblemSeverity severity)
+    {
+        if (userSeverity == null)
+            userSeverity = new HashMap<Class<? extends ICompilerProblem>, CompilerProblemSeverity>();
+
+        for (Class<ICompilerProblem> problem : problems)
+        {
+            userSeverity.put(problem, severity);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/IProblemFilter.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/problems/IProblemFilter.java b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/IProblemFilter.java
new file mode 100644
index 0000000..ede4135
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/IProblemFilter.java
@@ -0,0 +1,40 @@
+/*
+ *
+ *  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.clients.problems;
+
+import org.apache.flex.compiler.problems.ICompilerProblem;
+ 
+/**
+ *  IProblemFilter defines the interface used by problem filtering
+ *  objects; a problem filter must be able to accept (or, by returning
+ *  false, reject) a candidate ICompilerProblem.
+ */
+public interface IProblemFilter
+{
+    /**
+     * Determines if the specified {@link ICompilerProblem} passes the
+     * filter.
+     * 
+     * @param p {@link ICompilerProblem} to check
+     * @return true if specified {@link ICompilerProblem} passes the filter,
+     * false otherwise.
+     */
+    boolean accept(ICompilerProblem p);
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFilterClassCriteria.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFilterClassCriteria.java b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFilterClassCriteria.java
new file mode 100644
index 0000000..c8865c1
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFilterClassCriteria.java
@@ -0,0 +1,113 @@
+/*
+ *
+ *  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.clients.problems;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.flex.compiler.problems.ICompilerProblem;
+
+/**
+ *  IFilter problems based on their class; the filter supports
+ *  a negative set (don't accept any instances of a given class)
+ *  and a positive set (only accept instances of a given class).
+ */
+public class ProblemFilterClassCriteria implements IProblemFilter
+{
+    /**
+     *  Construct a purely negative filter.
+     *  @param classesToFilterOut - the classes (which may be superclasses)
+     *    of problems that should be excluded from the filtered set.
+     */
+    public ProblemFilterClassCriteria(Class<?>... classesToFilterOut)
+    {
+        this.classesToFilterOut = new HashSet<Class<?>>(Arrays.asList(classesToFilterOut));
+        this.classesToRequire = new HashSet<Class<?>>();
+    }
+
+    /**
+     *  Construct a filter with positive and negative requirements.
+     *  @param classesToFilterOut - the classes (which may be superclasses)
+     *    of problems that should be excluded from the filtered set.
+     *  @param classesToRequire - the classes (which may be superclasses)
+     *    that problems in the filtered set must extend/instantiate.
+     */
+    public ProblemFilterClassCriteria(Set<Class<?>> classesToFilterOut, Set<Class<?>> classesToRequire)
+    {
+        this.classesToFilterOut = classesToFilterOut;
+        this.classesToRequire = classesToRequire;
+    }
+
+    /**
+     *  Add a (super)class to be rejected.
+     *  @param clazz - the class to be rejected.
+     */
+    public void addRejectedClass(Class<?> clazz)
+    {
+        assert(!this.classesToRequire.contains(clazz));
+        this.classesToFilterOut.add(clazz);
+    }
+
+    /**
+     *  Remove a (super)class to was have been rejected.
+     *  @param clazz - the class that is now allowed.
+     */
+    public void removeRejectedClass(Class<?> clazz)
+    {
+        this.classesToFilterOut.remove(clazz);
+    }
+
+
+    /**
+     *  Add a (super)class to be required.
+     *  @param clazz - the class to be required.
+     */
+    public void addRequiredClass(Class<?> clazz)
+    {
+        assert(!this.classesToFilterOut.contains(clazz));
+        this.classesToRequire.add(clazz); 
+    }
+
+    @Override
+    public boolean accept(ICompilerProblem p)
+    {
+        for ( Class<?> filter: this.classesToFilterOut )
+        {
+            //  This is equivalent to (p instanceof <filter class>)
+            if ( filter.isInstance(p) )
+                return false;
+        }
+
+        for ( Class<?> filter: this.classesToRequire )
+        {
+            if ( ! (filter.isInstance(p) ) )
+                return false;
+        }
+
+        //  Passed all checks, accept it.
+        return true;
+    }
+
+    /** Classes members of the result set may not be instances of. */
+    private Set<Class<?>> classesToFilterOut;
+    /** Classes members of the result set must be instances of. */
+    private Set<Class<?>> classesToRequire;
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFormatter.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFormatter.java b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFormatter.java
new file mode 100644
index 0000000..c4395f1
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemFormatter.java
@@ -0,0 +1,142 @@
+/*
+ *
+ *  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.clients.problems;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+import org.apache.flex.compiler.Messages;
+import org.apache.flex.compiler.problems.CompilerProblem;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+
+/**
+ * Abstract class that provide useful functionality for
+ * formatting and retrieving the localized message of {@link CompilerProblem}'s.
+ */
+public class ProblemFormatter
+{       
+    public static ProblemFormatter DEFAULT_FORMATTER = new ProblemFormatter();
+    
+    /**
+     * Returns a readable description of the problem, by substituting field
+     * values for named placeholders such as ${name} in the message specified 
+     * for this compiler problem.
+     * 
+     * @param problem problem whose message to find 
+     * @return A localized readable description of the problem.
+     */
+    public String format(ICompilerProblem problem)
+    {
+        Class<?> c = problem.getClass();
+        
+        String problemMessage = getMessage(c.getSimpleName());
+        
+        if(problemMessage == null)
+        {          
+            // Use reflection to look up the value of the static field
+            // named 'description' on the problem subclass.
+            // For example, if this problem is a SyntaxError, this will be
+            // "Syntax error: '${tokenText}' is not allowed here"
+            try
+            {
+                Field descriptionField = c.getDeclaredField("DESCRIPTION");
+                problemMessage = (String)descriptionField.get(null);
+            }
+            catch (Exception e)
+            {
+                //No message found for this problem so just returned 
+                //the name of the class which will also be a bit helpful.
+                return '!' + c.getSimpleName() + '!';
+            }
+        }
+               
+        // Use reflection on this class and its superclasses
+        // to build a name/value map from the public non-static fields.
+        final Map<String, Object> map = new HashMap<String, Object>();   
+        while (c != Object.class)
+        {
+            for (final Field field : c.getDeclaredFields())
+            {
+                int modifiers = field.getModifiers();
+                if (Modifier.isPublic(modifiers) &&
+                    !Modifier.isStatic(modifiers))
+                {
+                    try
+                    {
+                        // Normally there should not be null values in a problem field, but
+                        // It has happened once or twice... Better to not attempt the substitution,
+                        // since it will just NPE and no problems we be correctly reported.
+                        Object value = field.get(problem);
+                        if (value != null)
+                            map.put(field.getName(), value);
+                    }
+                    catch (Exception e)
+                    {
+                        // ignore
+                    }
+                }
+            }
+            c = c.getSuperclass();
+        }
+        
+        //Replace all the tokens in the description using this key/value map
+        return substitute(problemMessage, map);
+    }
+    
+    /**
+     * Resolves the tokens in the text using the key/value pairs in the
+     * specified map. Each token in the message is searched in the map's key
+     * list and if found, the token replaced with the value of found key.
+     * 
+     * @param text compiler message to resolve
+     * @param parameters map that contains key/value pairs used to resolve the
+     * tokens in the specified message.
+     * @return resolved, token-free compiler message
+     */
+    public static String substitute(String text, final Map<String, Object> parameters)
+    {
+        for (String key : parameters.keySet())
+        {
+            String value = parameters.get(key).toString();
+            if(value != null)
+            {
+                // Need to escape the value in case it contains special characters, like '$'
+                value = Matcher.quoteReplacement(value);
+                text = text.replaceAll("[$][{]" + key + "[}]", value);
+            }
+        }
+        return text;
+    }
+    
+    /**
+     * Returns message for the specified key using the resource 
+     * bundle generated for the specified locale.
+     * 
+     * @param key key to find (should be the class name of the problem class)
+     * @return the message associated with the specified key
+     */
+    protected String getMessage(String key)
+    {
+        return Messages.getString(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemPrinter.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemPrinter.java b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemPrinter.java
new file mode 100644
index 0000000..e534e56
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/compiler/clients/problems/ProblemPrinter.java
@@ -0,0 +1,98 @@
+/*
+ *
+ *  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.clients.problems;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.flex.compiler.problems.ICompilerProblem;
+
+/**
+ * A printer utility class for {@link ICompilerProblem}s.
+ */
+public final class ProblemPrinter
+{
+    public ProblemPrinter(ProblemFormatter formatter, OutputStream outputStream)
+    {
+        this.formatter = formatter;
+        this.outputStream = outputStream;
+    }
+    
+    public ProblemPrinter(ProblemFormatter formatter)
+    {
+        this(formatter, System.err);
+    }
+    
+    private ProblemFormatter formatter;
+
+    private OutputStream outputStream;
+
+    /**
+     * Print problems and return number of problems printed after applying the
+     * filter.
+     * 
+     * @param problems compiler problems
+     * @param filter problem filter
+     * @return count of printed problems
+     */
+    public int printProblems(final Collection<ICompilerProblem> problems, IProblemFilter filter)
+    {
+        if (problems == null || problems.isEmpty())
+            return 0;
+
+        List<ICompilerProblem> filteredProblems = new ArrayList<ICompilerProblem>();
+        for (final ICompilerProblem problem : problems)
+        {
+            if ((filter == null) || (filter.accept(problem)))
+            {
+                filteredProblems.add(problem);
+            }
+        }
+        
+        return printProblems(filteredProblems);
+    }
+
+    /**
+     *  Print a pre-filtered sequence of problems.
+     *  
+     *  @param problems compiler problems
+     *  @return count of printed problems
+     */
+    public int printProblems(final Iterable<ICompilerProblem> problems)
+    {
+        int problemCount = 0;
+
+        PrintWriter writer = new PrintWriter(new PrintStream(outputStream));
+        
+        for (final ICompilerProblem problem : problems)
+        {
+            writer.println(formatter.format(problem));
+            problemCount++;
+        }
+
+        writer.flush();
+        return problemCount;
+    }
+
+}