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;
+ }
+
+}