You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by jo...@apache.org on 2021/09/20 21:24:55 UTC

[royale-compiler] branch develop updated (d2358fd -> 9195440)

This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a change to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git.


    from d2358fd  formatter: detect when + and - are used as unary operators instead of binary
     new 1a9333b  formatter: command line configuration
     new 661630a  formatter: fix bad logic for printing formatting text to stdout
     new da1c4e5  formatter: fix new lines at end of file not being preserved
     new 8ca8306  formatter: help and version args
     new 9195440  build: add formatter

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 build.xml                                          |  29 +-
 compiler/src/assembly/scripts/{fdb => asformat}    |   2 +-
 .../src/assembly/scripts/{fdb.bat => asformat.bat} |   2 +-
 formatter/build.xml                                |  11 +-
 .../org/apache/royale/formatter/FORMATTER.java     | 226 +++++--
 .../formatter}/config/CommandLineConfigurator.java |   2 +-
 .../royale/formatter/config/Configuration.java     | 438 +++++++++++++
 .../formatter}/config/ConfigurationBuffer.java     |   2 +-
 .../formatter}/config/ConfigurationInfo.java       |   2 +-
 .../formatter}/config/ConfigurationValue.java      |   2 +-
 .../royale/formatter/config/Configurator.java      | 683 +++++++++++++++++++++
 .../config/IFormatterSettingsConstants.java        |  10 +-
 .../apache/royale/formatter/config/Semicolons.java |  25 +-
 .../config/SystemPropertyConfigurator.java         |   3 +-
 pom.xml                                            |   1 +
 releasesteps.xml                                   |   6 +
 16 files changed, 1362 insertions(+), 82 deletions(-)
 copy compiler/src/assembly/scripts/{fdb => asformat} (98%)
 mode change 100755 => 100644
 copy compiler/src/assembly/scripts/{fdb.bat => asformat.bat} (95%)
 copy {compiler-common/src/main/java/org/apache/royale/compiler => formatter/src/main/java/org/apache/royale/formatter}/config/CommandLineConfigurator.java (99%)
 create mode 100644 formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java
 copy {compiler-common/src/main/java/org/apache/royale/compiler => formatter/src/main/java/org/apache/royale/formatter}/config/ConfigurationBuffer.java (99%)
 copy {compiler-common/src/main/java/org/apache/royale/compiler => formatter/src/main/java/org/apache/royale/formatter}/config/ConfigurationInfo.java (99%)
 copy {compiler-common/src/main/java/org/apache/royale/compiler => formatter/src/main/java/org/apache/royale/formatter}/config/ConfigurationValue.java (98%)
 create mode 100644 formatter/src/main/java/org/apache/royale/formatter/config/Configurator.java
 copy compiler-common/src/main/java/org/apache/royale/swf/types/IDataType.java => formatter/src/main/java/org/apache/royale/formatter/config/IFormatterSettingsConstants.java (82%)
 copy compiler-jx/src/test/resources/royale/projects/circular_proto/A.as => formatter/src/main/java/org/apache/royale/formatter/config/Semicolons.java (83%)
 copy {compiler-common/src/main/java/org/apache/royale/compiler/internal => formatter/src/main/java/org/apache/royale/formatter}/config/SystemPropertyConfigurator.java (96%)

[royale-compiler] 03/05: formatter: fix new lines at end of file not being preserved

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git

commit da1c4e56554cee487b9c57cfc6a1a831b6620839
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Mon Sep 20 11:43:42 2021 -0700

    formatter: fix new lines at end of file not being preserved
---
 .../org/apache/royale/formatter/FORMATTER.java     | 34 ++++++++++++++++++----
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
index d010430..d3239dd 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
@@ -408,6 +408,18 @@ class FORMATTER {
 			tokens.add(token);
 			prevToken = token;
 		}
+		if(prevToken != null) {
+			int start = prevToken.getAbsoluteEnd();
+			int end = text.length();
+			if (end > start) {
+				String tokenText = text.substring(start, end);
+				ASToken extraToken = new ASToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getEndLine(),
+						prevToken.getEndColumn(), tokenText);
+				extraToken.setEndLine(prevToken.getLine());
+				extraToken.setEndLine(prevToken.getColumn());
+				tokens.add(extraToken);
+			}
+		}
 		try {
 			return parseTokens(filePath, tokens, node);
 		} catch (Exception e) {
@@ -471,6 +483,12 @@ class FORMATTER {
 		for (int i = 0; i < tokens.size(); i++) {
 			token = tokens.get(i);
 			if (token.getType() == TOKEN_TYPE_EXTRA) {
+				if(i == (tokens.size() - 1)) {
+					//if the last token is whitespace, include new lines
+					numRequiredNewLines = Math.max(0, countNewLinesInExtra(token));
+					appendNewLines(builder, numRequiredNewLines);
+					break;
+				}
 				if (!blockOpenPending) {
 					numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
 					if (!indentedStatement && numRequiredNewLines > 0 && prevTokenNotComment != null
@@ -701,12 +719,7 @@ class FORMATTER {
 			}
 			if (prevToken != null) {
 				if (numRequiredNewLines > 0) {
-					if (maxPreserveNewLines != 0) {
-						numRequiredNewLines = Math.min(maxPreserveNewLines, numRequiredNewLines);
-					}
-					for (int j = 0; j < numRequiredNewLines; j++) {
-						builder.append('\n');
-					}
+					appendNewLines(builder, numRequiredNewLines);
 					appendIndent(builder, indent);
 				} else if (requiredSpace) {
 					builder.append(' ');
@@ -1283,6 +1296,15 @@ class FORMATTER {
 		}
 	}
 
+	private void appendNewLines(StringBuilder builder, int numRequiredNewLines) {
+		if (maxPreserveNewLines != 0) {
+			numRequiredNewLines = Math.min(maxPreserveNewLines, numRequiredNewLines);
+		}
+		for (int j = 0; j < numRequiredNewLines; j++) {
+			builder.append('\n');
+		}
+	}
+
 	private static class BlockStackItem {
 		public BlockStackItem(IASToken token) {
 			this.token = token;

[royale-compiler] 05/05: build: add formatter

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git

commit 9195440be0eed53f208d5b6ca8952c00b3b81774
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Mon Sep 20 14:09:59 2021 -0700

    build: add formatter
---
 build.xml                                  | 29 +++++++++++++-
 compiler/src/assembly/scripts/asformat     | 62 ++++++++++++++++++++++++++++++
 compiler/src/assembly/scripts/asformat.bat | 26 +++++++++++++
 pom.xml                                    |  1 +
 releasesteps.xml                           |  6 +++
 5 files changed, 123 insertions(+), 1 deletion(-)

diff --git a/build.xml b/build.xml
index 4f9086f..82af803 100644
--- a/build.xml
+++ b/build.xml
@@ -71,7 +71,7 @@
 
     <target name="swf" depends="sdk, javadoc, tests" description="Builds SWF compiler, builds Royale Javadoc, and runs SWF compiler tests."/>
 
-    <target name="main" depends="swf, jx, oem, debugger, anttasks, royaleunit.anttasks" description="Builds Royale SWF compiler, then Royale JS Transpiler"/>
+    <target name="main" depends="swf, jx, oem, debugger, formatter, anttasks, royaleunit.anttasks" description="Builds Royale SWF compiler, then Royale JS Transpiler"/>
 
     <target name="jx" depends="compiler.jx, compiler.jx.tests" description="Builds Royale JS Transpiler" />
     
@@ -100,6 +100,10 @@
     <target name="debugger" depends="swfutils" description="Builds FDB JAR">
         <ant dir="debugger" target="jar"/>
     </target>
+
+    <target name="formatter" description="Builds formatter JAR">
+        <ant dir="formatter" target="main"/>
+    </target>
     
     <target name="compiler.jx" description="Builds Royale JS Transpiler.">
         <ant dir="compiler-jx" target="main"/>
@@ -142,6 +146,7 @@
         <ant dir="royale-ant-tasks" target="clean"/>
         <ant dir="royaleunit-ant-tasks" target="clean"/>
         <ant dir="debugger" target="clean"/>
+        <ant dir="formatter" target="clean"/>
     </target>
 
     <target name="wipe" description="Wipes out everything that didn't come from Git.">
@@ -171,6 +176,7 @@
         <ant dir="royale-ant-tasks" target="wipe"/>
         <ant dir="royaleunit-ant-tasks" target="wipe"/>
         <ant dir="debugger" target="clean"/>
+        <ant dir="formatter" target="clean"/>
         <delete dir="${basedir}/out" failonerror="false" includeEmptyDirs="true"/>
         <delete dir="${basedir}/temp" failonerror="false" includeEmptyDirs="true"/>
         <!-- remove legacy folders if they exist -->
@@ -288,6 +294,9 @@
         <!-- compiler -->
         <antcall target="stage-compiler"/>
         
+        <!-- formatter -->
+        <antcall target="stage-formatter"/>
+        
         <!-- flex-compiler-oem and fdb -->
         <antcall target="stage-fb-integration"/>
         
@@ -443,6 +452,24 @@
         <!--<antcall target="version-update"/>-->
     </target>
 
+    <target name="stage-formatter">
+        <copy todir="${staging-dir}/formatter" includeEmptyDirs="false">
+            <fileset dir="${basedir}/formatter">
+                <include name="**"/>
+                <exclude name=".classpath" />
+                <exclude name=".project" />
+                <exclude name=".settings/**" />
+                <exclude name="in/**"/>
+                <exclude name="lib/**"/>
+                <exclude name="target/classes/**"/>
+                <exclude name="target/test-classes/**"/>
+                <exclude name="target/junit-temp/**"/>
+                <exclude name="target/junit-reports/**"/>
+                <exclude name="**/unittest.properties" />
+            </fileset>
+        </copy>
+    </target>
+
     <target name="stage-fb-integration">
         <copy todir="${staging-dir}/debugger" includeEmptyDirs="false">
             <fileset dir="${basedir}/debugger">
diff --git a/compiler/src/assembly/scripts/asformat b/compiler/src/assembly/scripts/asformat
new file mode 100644
index 0000000..8020357
--- /dev/null
+++ b/compiler/src/assembly/scripts/asformat
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+################################################################################
+##
+##  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.
+##
+################################################################################
+
+#
+# fasc shell script to launch compiler-asc.jar on OSX, Unix, or Cygwin.
+# In Windows Command Prompt, use fasc.bat instead.
+#
+
+if [ "x${ROYALE_COMPILER_HOME}" = "x" ]
+then
+    SCRIPT_HOME=`dirname "$0"`
+    ROYALE_COMPILER_HOME=${SCRIPT_HOME}/..
+else
+    echo Using Royale codebase: $ROYALE_COMPILER_HOME
+fi
+
+case `uname` in
+		CYGWIN*)
+			OS="Windows"
+		;;
+		*)
+			OS=Unix
+esac
+
+D32=''
+
+if [ $OS = "Windows" ]; then
+
+	ROYALE_COMPILER_HOME=`cygpath -m $ROYALE_COMPILER_HOME`
+
+elif [ $OS = "Unix" ]; then
+
+    check64="`java -version 2>&1 | grep -i 64-Bit`"
+    isOSX="`uname | grep -i Darwin`"
+    javaVersion="`java -version 2>&1 | awk -F '[ ".]+' 'NR==1 {print $3 "." $4}'`"
+    
+    if [ "$isOSX" != "" -a "$HOSTTYPE" = "x86_64" -a "$check64" != "" -a "$javaVersion" = "1.6" ]; then
+        D32='-d32'
+    fi
+fi
+
+VMARGS="-Xmx384m -Dsun.io.useCanonCaches=false "
+
+java $VMARGS $D32 $SETUP_SH_VMARGS -jar "${ROYALE_COMPILER_HOME}/lib/formatter.jar" "$@"
diff --git a/compiler/src/assembly/scripts/asformat.bat b/compiler/src/assembly/scripts/asformat.bat
new file mode 100644
index 0000000..7c1f5c9
--- /dev/null
+++ b/compiler/src/assembly/scripts/asformat.bat
@@ -0,0 +1,26 @@
+@echo off
+
+rem
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements.  See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License.  You may obtain a copy of the License at
+rem
+rem     http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+rem
+
+rem
+rem fasc.bat script to launch compiler-asc.jar in Windows Command Prompt.
+rem On OSX, Unix, or Cygwin, use the fasc shell script instead.
+rem
+
+@java -Dsun.io.useCanonCaches=false -Dapplication.home="%~dp0.." -Xms32m -Xmx512m -jar "%~dp0..\lib\formatter.jar" %*
+
diff --git a/pom.xml b/pom.xml
index 0d79b14..e62d398 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,6 +106,7 @@
     <module>compiler-test-utils</module>
     <module>swfutils</module>
     <module>debugger</module>
+    <module>formatter</module>
     <module>flex-compiler-oem</module>
     <module>royale-ant-tasks</module>
     <module>royaleunit-ant-tasks</module>
diff --git a/releasesteps.xml b/releasesteps.xml
index b4752ba..f45f8cb 100644
--- a/releasesteps.xml
+++ b/releasesteps.xml
@@ -323,6 +323,12 @@
             <param name="jarname" value="debugger" />
         </antcall>
         <antcall target="sign-jar-artifacts" >
+            <param name="jarname" value="formatter" />
+        </antcall>
+        <antcall target="sign-file" >
+            <param name="file" value="${artifactfolder}/artifacts/archive/target/local-release-dir/org/apache/royale/compiler/formatter/${release.version}/formatter-${release.version}-tests.jar" />
+        </antcall>
+        <antcall target="sign-jar-artifacts" >
             <param name="jarname" value="flex-compiler-oem" />
         </antcall>
         <antcall target="sign-jar-artifacts" >

[royale-compiler] 04/05: formatter: help and version args

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git

commit 8ca8306b0a9778e780ec48e8a5a928d6cb0dfc78
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Mon Sep 20 13:38:03 2021 -0700

    formatter: help and version args
---
 .../org/apache/royale/formatter/FORMATTER.java     | 112 ++++++++--
 .../royale/formatter/config/Configuration.java     | 234 +++++++++++++++++++++
 .../apache/royale/formatter/config/Semicolons.java |  30 +++
 3 files changed, 360 insertions(+), 16 deletions(-)

diff --git a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
index d3239dd..ad1aa94 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
@@ -25,8 +25,10 @@ import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Scanner;
+import java.util.Set;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.royale.compiler.clients.problems.ProblemFormatter;
@@ -34,6 +36,7 @@ import org.apache.royale.compiler.clients.problems.ProblemPrinter;
 import org.apache.royale.compiler.clients.problems.ProblemQuery;
 import org.apache.royale.compiler.common.VersionInfo;
 import org.apache.royale.compiler.exceptions.ConfigurationException;
+import org.apache.royale.compiler.internal.config.localization.LocalizationManager;
 import org.apache.royale.compiler.internal.parsing.as.ASParser;
 import org.apache.royale.compiler.internal.parsing.as.ASToken;
 import org.apache.royale.compiler.internal.parsing.as.ASTokenTypes;
@@ -49,9 +52,12 @@ import org.apache.royale.compiler.parsing.IASToken;
 import org.apache.royale.compiler.problems.ConfigurationProblem;
 import org.apache.royale.compiler.problems.ICompilerProblem;
 import org.apache.royale.compiler.problems.UnexpectedExceptionProblem;
+import org.apache.royale.formatter.config.CommandLineConfigurator;
 import org.apache.royale.formatter.config.Configuration;
 import org.apache.royale.formatter.config.ConfigurationBuffer;
+import org.apache.royale.formatter.config.ConfigurationValue;
 import org.apache.royale.formatter.config.Configurator;
+import org.apache.royale.formatter.config.Semicolons;
 import org.apache.royale.utils.FilenameNormalization;
 
 /**
@@ -59,6 +65,10 @@ import org.apache.royale.utils.FilenameNormalization;
  */
 class FORMATTER {
 	private static final int TOKEN_TYPE_EXTRA = 999999;
+    
+    private static final String NEWLINE = System.getProperty("line.separator");
+    private static final String DEFAULT_VAR = "files";
+    private static final String L10N_CONFIG_PREFIX = "org.apache.royale.compiler.internal.config.configuration";
 
 	static enum ExitCode {
 		SUCCESS(0), PRINT_HELP(1), FAILED_WITH_PROBLEMS(2), FAILED_WITH_EXCEPTIONS(3), FAILED_WITH_CONFIG_PROBLEMS(4);
@@ -70,16 +80,6 @@ class FORMATTER {
 		final int code;
 	}
 
-	public static enum Semicolons {
-		IGNORE("ignore"), INSERT("insert"), REMOVE("remove");
-
-		Semicolons(String value) {
-			this.value = value;
-		}
-
-		final String value;
-	}
-
 	/**
 	 * Java program entry point.
 	 * 
@@ -113,6 +113,8 @@ class FORMATTER {
 	private List<File> inputFiles = new ArrayList<File>();
 	private boolean writeBackToInputFiles = false;
 	private boolean listChangedFiles = false;
+	private Configuration configuration;
+	private ConfigurationBuffer configBuffer;
 
 	public int execute(String[] args) {
 		ExitCode exitCode = ExitCode.SUCCESS;
@@ -207,6 +209,61 @@ class FORMATTER {
 	public String formatText(String text) {
 		return formatText(text, null);
 	}
+    
+    /**
+     * 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 Royale ActionScript Formatter (asformat)" + NEWLINE +
+            VersionInfo.buildMessage() + NEWLINE;
+        return message;
+    }
+
+    /**
+     * Get my program name.
+     * 
+     * @return always "mxmlc".
+     */
+    protected String getProgramName()
+    {
+        return "asformat";
+    }
+
+    /**
+     * 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);
+    }
 
 	private boolean configure(String[] args) {
 		if (args.length == 0) {
@@ -218,20 +275,43 @@ class FORMATTER {
 
 			Configurator configurator = new Configurator();
 			configurator.setConfiguration(args, "files");
-			Configuration config = configurator.getConfiguration();
-			ConfigurationBuffer configBuffer = configurator.getConfigurationBuffer();
+			configuration = configurator.getConfiguration();
+			configBuffer = configurator.getConfigurationBuffer();
 
 			problems.addAll(configurator.getConfigurationProblems());
 
-			if (configBuffer.getVar("version") != null)
+			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;
 
-			writeBackToInputFiles = config.getWriteFiles();
-			listChangedFiles = config.getListFiles();
-			for (String filePath : config.getFiles()) {
+			collapseEmptyBlocks = configuration.getCollapseEmptyBlocks();
+			ignoreProblems = configuration.getIgnoreParsingProblems();
+			insertFinalNewLine = configuration.getInsertFinalNewLine();
+			insertSpaceAfterCommaDelimiter = configuration.getInsertSpaceAfterCommaDelimiter();
+			insertSpaceAfterFunctionKeywordForAnonymousFunctions = configuration.getInsertSpaceAfterFunctionKeywordForAnonymousFunctions();
+			insertSpaceAfterKeywordsInControlFlowStatements = configuration.getInsertSpaceAfterKeywordsInControlFlowStatements();
+			insertSpaceAfterSemicolonInForStatements = configuration.getInsertSpaceAfterSemicolonInForStatements();
+			insertSpaceBeforeAndAfterBinaryOperators = configuration.getInsertSpaceBeforeAndAfterBinaryOperators();
+			insertSpaces = configuration.getInsertSpaces();
+			listChangedFiles = configuration.getListFiles();
+			maxPreserveNewLines = configuration.getMaxPreserveNewLines();
+			placeOpenBraceOnNewLine = configuration.getPlaceOpenBraceOnNewLine();
+			semicolons = Semicolons.valueOf(configuration.getSemicolons().toUpperCase());
+			tabSize = configuration.getTabSize();
+			writeBackToInputFiles = configuration.getWriteFiles();
+			for (String filePath : configuration.getFiles()) {
 				File inputFile = new File(filePath);
 				if (!inputFile.exists()) {
 					throw new ConfigurationException("Input file does not exist: " + filePath, null, -1);
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java b/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java
index 3f33aec..2a7938a 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java
@@ -201,4 +201,238 @@ public class Configuration {
     {
         this.listFiles = b;
     }
+
+    //
+    // 'insert-spaces' option
+    //
+
+    private boolean insertSpaces = false;
+
+    public boolean getInsertSpaces()
+    {
+        return insertSpaces;
+    }
+
+    @Config
+    @Mapping("insert-spaces")
+    public void setInsertSpaces(ConfigurationValue cv, boolean b)
+    {
+        this.insertSpaces = b;
+    }
+
+    //
+    // 'insert-final-new-line' option
+    //
+
+    private boolean insertFinalNewLine = false;
+
+    public boolean getInsertFinalNewLine()
+    {
+        return insertFinalNewLine;
+    }
+
+    @Config
+    @Mapping("insert-final-new-line")
+    public void setInsertFinalNewLine(ConfigurationValue cv, boolean b)
+    {
+        this.insertFinalNewLine = b;
+    }
+
+    //
+    // 'open-brace-new-line' option
+    //
+
+    private boolean placeOpenBraceOnNewLine = false;
+
+    public boolean getPlaceOpenBraceOnNewLine()
+    {
+        return placeOpenBraceOnNewLine;
+    }
+
+    @Config
+    @Mapping("open-brace-new-line")
+    public void setPlaceOpenBraceOnNewLine(ConfigurationValue cv, boolean b)
+    {
+        this.placeOpenBraceOnNewLine = b;
+    }
+
+    //
+    // 'insert-space-for-loop-semicolon' option
+    //
+
+    private boolean insertSpaceAfterSemicolonInForStatements = false;
+
+    public boolean getInsertSpaceAfterSemicolonInForStatements()
+    {
+        return insertSpaceAfterSemicolonInForStatements;
+    }
+
+    @Config
+    @Mapping("insert-space-for-loop-semicolon")
+    public void setInsertSpaceAfterSemicolonInForStatements(ConfigurationValue cv, boolean b)
+    {
+        this.insertSpaceAfterSemicolonInForStatements = b;
+    }
+
+    //
+    // 'insert-space-control-flow-keywords' option
+    //
+
+    private boolean insertSpaceAfterKeywordsInControlFlowStatements = false;
+
+    public boolean getInsertSpaceAfterKeywordsInControlFlowStatements()
+    {
+        return insertSpaceAfterKeywordsInControlFlowStatements;
+    }
+
+    @Config
+    @Mapping("insert-space-control-flow-keywords")
+    public void setInsertSpaceAfterKeywordsInControlFlowStatements(ConfigurationValue cv, boolean b)
+    {
+        this.insertSpaceAfterKeywordsInControlFlowStatements = b;
+    }
+
+    //
+    // 'insert-space-anonymous-function-keyword' option
+    //
+
+    private boolean insertSpaceAfterFunctionKeywordForAnonymousFunctions = false;
+
+    public boolean getInsertSpaceAfterFunctionKeywordForAnonymousFunctions()
+    {
+        return insertSpaceAfterFunctionKeywordForAnonymousFunctions;
+    }
+
+    @Config
+    @Mapping("insert-space-control-flow-keywords")
+    public void setInsertSpaceAfterFunctionKeywordForAnonymousFunctions(ConfigurationValue cv, boolean b)
+    {
+        this.insertSpaceAfterFunctionKeywordForAnonymousFunctions = b;
+    }
+
+    //
+    // 'insert-space-binary-operators' option
+    //
+
+    private boolean insertSpaceBeforeAndAfterBinaryOperators = false;
+
+    public boolean getInsertSpaceBeforeAndAfterBinaryOperators()
+    {
+        return insertSpaceBeforeAndAfterBinaryOperators;
+    }
+
+    @Config
+    @Mapping("insert-space-binary-operators")
+    public void setInsertSpaceBeforeAndAfterBinaryOperators(ConfigurationValue cv, boolean b)
+    {
+        this.insertSpaceBeforeAndAfterBinaryOperators = b;
+    }
+
+    //
+    // 'insert-space-comma-delimiter' option
+    //
+
+    private boolean insertSpaceAfterCommaDelimiter = false;
+
+    public boolean getInsertSpaceAfterCommaDelimiter()
+    {
+        return insertSpaceAfterCommaDelimiter;
+    }
+
+    @Config
+    @Mapping("insert-space-comma-delimiter")
+    public void setInsertSpaceAfterCommaDelimiter(ConfigurationValue cv, boolean b)
+    {
+        this.insertSpaceAfterCommaDelimiter = b;
+    }
+
+    //
+    // 'collapse-empty-blocks' option
+    //
+
+    private boolean collapseEmptyBlocks = false;
+
+    public boolean getCollapseEmptyBlocks()
+    {
+        return collapseEmptyBlocks;
+    }
+
+    @Config
+    @Mapping("collapse-empty-blocks")
+    public void setCollapseEmptyBlocks(ConfigurationValue cv, boolean b)
+    {
+        this.collapseEmptyBlocks = b;
+    }
+
+    //
+    // 'tab-size' option
+    //
+
+    private int tabSize = 4;
+
+    public int getTabSize()
+    {
+        return tabSize;
+    }
+
+    @Config
+    @Mapping("tab-size")
+    public void setTabSize(ConfigurationValue cv, int b)
+    {
+        this.tabSize = b;
+    }
+
+    //
+    // 'max-preserve-new-lines' option
+    //
+
+    private int maxPreserveNewLines = 2;
+
+    public int getMaxPreserveNewLines()
+    {
+        return maxPreserveNewLines;
+    }
+
+    @Config
+    @Mapping("max-preserve-new-lines")
+    public void setMaxPreserveNewLines(ConfigurationValue cv, int b)
+    {
+        this.maxPreserveNewLines = b;
+    }
+
+    //
+    // 'semicolons' option
+    //
+
+    private Semicolons semicolons = Semicolons.INSERT;
+
+    public String getSemicolons()
+    {
+        return semicolons.value;
+    }
+
+    @Config
+    @Mapping("semicolons")
+    public void setSemicolons(ConfigurationValue cv, String b)
+    {
+        this.semicolons = Semicolons.valueOf(b.toUpperCase());
+    }
+
+    //
+    // 'ignore-parsing-problems' option
+    //
+
+    private boolean ignoreParsingProblems = false;
+
+    public boolean getIgnoreParsingProblems()
+    {
+        return ignoreParsingProblems;
+    }
+
+    @Config(advanced = true)
+    @Mapping("ignore-parsing-problems")
+    public void setIgnoreParsingProblems(ConfigurationValue cv, boolean b)
+    {
+        this.ignoreParsingProblems = b;
+    }
 }
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/Semicolons.java b/formatter/src/main/java/org/apache/royale/formatter/config/Semicolons.java
new file mode 100644
index 0000000..7abee7a
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/Semicolons.java
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.royale.formatter.config;
+
+public enum Semicolons {
+	IGNORE("ignore"), INSERT("insert"), REMOVE("remove");
+
+	Semicolons(String value) {
+		this.value = value;
+	}
+
+	final String value;
+}
\ No newline at end of file

[royale-compiler] 01/05: formatter: command line configuration

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git

commit 1a9333bca06dc74bdf79fbb906c47850fc0999f5
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Mon Sep 20 11:25:38 2021 -0700

    formatter: command line configuration
---
 formatter/build.xml                                |   11 +-
 .../org/apache/royale/formatter/FORMATTER.java     |   90 +-
 .../formatter/config/CommandLineConfigurator.java  |  607 +++++++++
 .../royale/formatter/config/Configuration.java     |  204 +++
 .../formatter/config/ConfigurationBuffer.java      | 1346 ++++++++++++++++++++
 .../royale/formatter/config/ConfigurationInfo.java |  473 +++++++
 .../formatter/config/ConfigurationValue.java       |  109 ++
 .../royale/formatter/config/Configurator.java      |  683 ++++++++++
 .../config/IFormatterSettingsConstants.java        |   24 +
 .../config/SystemPropertyConfigurator.java         |   82 ++
 10 files changed, 3595 insertions(+), 34 deletions(-)

diff --git a/formatter/build.xml b/formatter/build.xml
index 1243567..93e6bc8 100644
--- a/formatter/build.xml
+++ b/formatter/build.xml
@@ -115,9 +115,14 @@
     
     <target name="jar-test" >
         <echo>using formatter.jar from ${sdk}</echo>
-        <java jar="${sdk}/formatter.jar" fork="true"
-            failonerror="true">
-        </java>
+        <java jar="${sdk}/formatter.jar" fork="true" resultproperty="formatter.result"/>
+        <fail message="Starting Failed">
+            <condition>
+                <not>
+                    <equals arg1="${formatter.result}" arg2="1"/>
+                </not>
+            </condition>
+        </fail>
     </target>
     
     <!--
diff --git a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
index ae80ce7..cb33693 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
@@ -29,6 +29,9 @@ import java.util.List;
 import java.util.Scanner;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.royale.compiler.clients.problems.ProblemFormatter;
+import org.apache.royale.compiler.clients.problems.ProblemPrinter;
+import org.apache.royale.compiler.clients.problems.ProblemQuery;
 import org.apache.royale.compiler.common.VersionInfo;
 import org.apache.royale.compiler.exceptions.ConfigurationException;
 import org.apache.royale.compiler.internal.parsing.as.ASParser;
@@ -43,8 +46,12 @@ import org.apache.royale.compiler.internal.parsing.as.StreamingASTokenizer;
 import org.apache.royale.compiler.internal.tree.as.FileNode;
 import org.apache.royale.compiler.internal.workspaces.Workspace;
 import org.apache.royale.compiler.parsing.IASToken;
+import org.apache.royale.compiler.problems.ConfigurationProblem;
 import org.apache.royale.compiler.problems.ICompilerProblem;
 import org.apache.royale.compiler.problems.UnexpectedExceptionProblem;
+import org.apache.royale.formatter.config.Configuration;
+import org.apache.royale.formatter.config.ConfigurationBuffer;
+import org.apache.royale.formatter.config.Configurator;
 import org.apache.royale.utils.FilenameNormalization;
 
 /**
@@ -102,12 +109,14 @@ class FORMATTER {
 	public boolean ignoreProblems = false;
 	public boolean collapseEmptyBlocks = false;
 
+	private ProblemQuery problems;
 	private List<File> inputFiles = new ArrayList<File>();
 	private boolean writeBackToInputFiles = false;
 	private boolean listChangedFiles = false;
 
 	public int execute(String[] args) {
 		ExitCode exitCode = ExitCode.SUCCESS;
+		problems = new ProblemQuery();
 
 		try {
 			boolean continueFormatting = configure(args);
@@ -151,13 +160,19 @@ class FORMATTER {
 						}
 					}
 				}
+			} else if (problems.hasFilteredProblems()) {
+				exitCode = ExitCode.FAILED_WITH_CONFIG_PROBLEMS;
+			} else {
+				exitCode = ExitCode.PRINT_HELP;
 			}
-		} catch (ConfigurationException e) {
-			System.err.println(e.getMessage());
-			exitCode = ExitCode.FAILED_WITH_CONFIG_PROBLEMS;
 		} catch (Exception e) {
 			System.err.println(e.getMessage());
 			exitCode = ExitCode.FAILED_WITH_EXCEPTIONS;
+		} finally {
+			if (problems.hasFilteredProblems()) {
+				final ProblemPrinter printer = new ProblemPrinter(ProblemFormatter.DEFAULT_FORMATTER);
+				printer.printProblems(problems.getFilteredProblems());
+			}
 		}
 		return exitCode.code;
 	}
@@ -193,28 +208,33 @@ class FORMATTER {
 		return formatText(text, null);
 	}
 
-	private boolean configure(String[] args) throws ConfigurationException {
+	private boolean configure(String[] args) {
 		if (args.length == 0) {
 			printHelp();
 			return false;
 		}
-		for (int i = 0; i < args.length; i++) {
-			String arg = args[i];
-			if (arg.charAt(0) == '-') {
-				if (arg.equals("-write") || arg.equals("-w")) {
-					writeBackToInputFiles = true;
-				} else if (arg.equals("-list") || arg.equals("-l")) {
-					listChangedFiles = true;
-				} else if (arg.equals("-help") || arg.equals("-h")) {
-					printHelp();
-					return false;
-				} else {
-					throw new ConfigurationException("Unknown command-line argument: " + arg, null, -1);
-				}
-			} else {
-				File inputFile = new File(arg);
+		try {
+			problems = new ProblemQuery();
+
+			Configurator configurator = new Configurator();
+			configurator.setConfiguration(args, "files");
+			Configuration config = configurator.getConfiguration();
+			ConfigurationBuffer configBuffer = configurator.getConfigurationBuffer();
+
+			problems.addAll(configurator.getConfigurationProblems());
+
+			if (configBuffer.getVar("version") != null)
+				return false;
+
+			if (problems.hasErrors())
+				return false;
+
+			writeBackToInputFiles = config.getWriteFiles();
+			listChangedFiles = config.getListFiles();
+			for (String filePath : config.getFiles()) {
+				File inputFile = new File(filePath);
 				if (!inputFile.exists()) {
-					throw new ConfigurationException("Input file does not exist: " + arg, null, -1);
+					throw new ConfigurationException("Input file does not exist: " + filePath, null, -1);
 				}
 				if (inputFile.isDirectory()) {
 					addDirectory(inputFile);
@@ -222,21 +242,29 @@ class FORMATTER {
 					inputFiles.add(inputFile);
 				}
 			}
-		}
-		if (inputFiles.size() == 0 && writeBackToInputFiles) {
-			throw new ConfigurationException("Cannot use -w with standard input", null, -1);
-		}
-		if (writeBackToInputFiles) {
-			if (inputFiles.size() == 0) {
-				throw new ConfigurationException("Cannot use -w with standard input", null, -1);
+			if (inputFiles.size() == 0 && listChangedFiles) {
+				throw new ConfigurationException("Cannot use -list-files with standard input", null, -1);
 			}
-			for (File inputFile : inputFiles) {
-				if (!inputFile.canWrite()) {
-					throw new ConfigurationException("File is read-only: " + inputFile.getPath(), null, -1);
+			if (writeBackToInputFiles) {
+				if (inputFiles.size() == 0) {
+					throw new ConfigurationException("Cannot use -write-files with standard input", null, -1);
+				}
+				for (File inputFile : inputFiles) {
+					if (!inputFile.canWrite()) {
+						throw new ConfigurationException("File is read-only: " + inputFile.getPath(), null, -1);
+					}
 				}
 			}
+			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;
 		}
-		return true;
 	}
 
 	private void addDirectory(File inputFile) {
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/CommandLineConfigurator.java b/formatter/src/main/java/org/apache/royale/formatter/config/CommandLineConfigurator.java
new file mode 100644
index 0000000..a42960f
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/CommandLineConfigurator.java
@@ -0,0 +1,607 @@
+/*
+ *
+ *  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.royale.formatter.config;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.File;
+
+import com.google.common.base.Joiner;
+
+import org.apache.royale.compiler.Messages;
+import org.apache.royale.compiler.exceptions.ConfigurationException;
+import org.apache.royale.compiler.internal.config.localization.LocalizationManager;
+
+/**
+ * A utility class, which is used to parse an array of command line args and
+ * populate a ConfigurationBuffer. It also contains some associated methods like
+ * brief() and usage(). A counterpart of FileConfigurator and
+ * SystemPropertyConfigurator.
+ */
+public class CommandLineConfigurator
+{
+    public static final String SOURCE_COMMAND_LINE = "command line";
+
+    /**
+     * parse - buffer up configuration vals from the command line
+     * 
+     * @param buffer the configuration buffer to hold the results
+     * @param defaultvar the variable name where the trailing loose args go
+     * @param args the command line
+     */
+    public static void parse(final ConfigurationBuffer buffer,
+                              final String defaultvar,
+                              final String[] args)
+            throws ConfigurationException
+    {
+        // "no-default-arg" means the application does not have a default var.
+        assert defaultvar == null || buffer.isValidVar(defaultvar) || "no-default-arg".equals(defaultvar) : "coding error: config must provide default var " + defaultvar;
+
+        Map<String, String> aliases = getAliases(buffer);
+        final int START = 1;
+        final int ARGS = 2;
+        final int EXEC = 3;
+        final int DONE = 4;
+
+        int i = 0, iStart = 0, iEnd = 0;
+        String var = null;
+        int varArgCount = -2;
+        List<String> argList = new LinkedList<String>();
+        Set<String> vars = new HashSet<String>();
+        boolean append = false;
+        boolean dash = true;
+
+        int mode = START;
+
+        while (mode != DONE)
+        {
+            switch (mode)
+            {
+                case START:
+                {
+                    iStart = i;
+
+                    if (args.length == i)
+                    {
+                        mode = DONE;
+                        break;
+                    }
+                    // expect -var, --, or the beginning of default args
+
+                    mode = ARGS;
+                    varArgCount = -2;
+
+                    if (args[i].equals("--"))
+                    {
+                        dash = false;
+                        if (defaultvar != null)
+                            var = defaultvar;
+                        else
+                            mode = START;
+                        ++i;
+                    }
+                    else if (dash && args[i].startsWith("+"))
+                    {
+                        String token = null;
+                        int c = (args[i].length() > 1 && args[i].charAt(1) == '+') ? 2 : 1; // gnu-style?
+
+                        int equals = args[i].indexOf('=');
+                        String rest = null;
+                        if (equals != -1)
+                        {
+                            rest = args[i].substring(equals + 1);
+                            token = args[i++].substring(c, equals);
+                        }
+                        else
+                        {
+                            token = args[i++].substring(c);
+                        }
+                        if (equals != -1)
+                        {
+                            iEnd = i;
+                            buffer.setToken(token, rest);
+                            buffer.addPosition(token, iStart, iEnd);
+                        }
+                        else
+                        {
+                            if (i == args.length)
+                            {
+                                throw new ConfigurationException.Token(ConfigurationException.Token.INSUFFICIENT_ARGS,
+                                                                        token, var, source, -1);
+                            }
+                            rest = args[i++];
+                            iEnd = i;
+                            buffer.setToken(token, rest);
+                            buffer.addPosition(token, iStart, iEnd);
+                        }
+                        mode = START;
+                        break;
+                    }
+                    else if (dash && isAnArgument(args[i]))
+                    {
+                        int c = (args[i].length() > 1 && args[i].charAt(1) == '-') ? 2 : 1; // gnu-style?
+
+                        int plusequals = args[i].indexOf("+=");
+                        int equals = args[i].indexOf('=');
+                        String rest = null;
+                        if (plusequals != -1)
+                        {
+                            rest = args[i].substring(plusequals + 2);
+                            var = args[i++].substring(c, plusequals);
+                            append = true;
+                        }
+                        else if (equals != -1)
+                        {
+                            rest = args[i].substring(equals + 1);
+                            var = args[i++].substring(c, equals);
+                        }
+                        else
+                        {
+                            var = args[i++].substring(c);
+                        }
+
+                        if (aliases.containsKey(var))
+                            var = aliases.get(var);
+
+                        if (!buffer.isValidVar(var))
+                        {
+                            throw new ConfigurationException.UnknownVariable(var, source, -1);
+                        }
+
+                        if (equals != -1)
+                        {
+                            if ((rest == null) || (rest.length() == 0))
+                            {
+                                varArgCount = -1;
+                                mode = EXEC;
+                            }
+                            else
+                            {
+                                String seps = null;
+                                if (buffer.getInfo(var).isPath())
+                                {
+                                    seps = "[," + File.pathSeparatorChar + "]";
+                                }
+                                else
+                                {
+                                    seps = ",";
+                                }
+
+                                String[] tokens = rest.split(seps);
+                                argList.addAll(Arrays.asList(tokens));
+                                varArgCount = buffer.getVarArgCount(var);
+                                mode = EXEC;
+                            }
+                        }
+
+                    }
+                    else
+                    {
+                        // asdoc sets default var as no-default-arg - it has no default vars
+                        if (defaultvar != null && !defaultvar.equals("no-default-arg"))
+                        {
+                            // don't increment i, let ARGS pick it up.
+                            var = defaultvar;
+                        }
+                        else
+                        {
+                            throw new ConfigurationException.UnexpectedDefaults(null, null, -1);
+                        }
+                    }
+                    break;
+                }
+                case ARGS:
+                {
+                    if (varArgCount == -2)
+                    {
+                        if (isBoolean(buffer, var))
+                        {
+                            varArgCount = 0;
+                            mode = EXEC;
+                            break;
+                        }
+                        else
+                        {
+                            varArgCount = buffer.getVarArgCount(var);
+                        }
+                    }
+                    assert varArgCount >= -1; // just in case the getVarArgCount author was insane.
+
+                    if (args.length == i)
+                    {
+                        mode = EXEC;
+                        break;
+                    }
+
+                    boolean greedy = buffer.getInfo(var).isGreedy();
+
+                    // accumulating non-command arguments...
+
+                    // check for a terminator on our accumulated parameter list
+                    if (!greedy && dash && isAnArgument(args[i]))
+                    {
+                        if (varArgCount == -1)
+                        {
+                            // we were accumulating an unlimited set of args, a new var terminates that.
+                            mode = EXEC;
+                            break;
+                        }
+                        throw new ConfigurationException.IncorrectArgumentCount(varArgCount, argList.size(), var, source, -1);
+                    }
+
+                    argList.add(args[i++]);
+                    if (argList.size() == varArgCount)
+                    {
+                        mode = EXEC;
+                    }
+
+                    break;
+                }
+                case EXEC:
+                {
+                    if ((varArgCount != -1) && (argList.size() != varArgCount))
+                    {
+                        throw new ConfigurationException.IncorrectArgumentCount(varArgCount, argList.size(), var, source, -1);
+                    }
+                    if (varArgCount == 0) // boolean flag fakery...
+                        argList.add("true");
+
+                    if (vars.contains(var))
+                    {
+                        if ((defaultvar != null) && var.equals(defaultvar))
+                        {
+                            // we could perhaps accumulate the defaults spread out through
+                            // the rest of the flags, but for now we'll call this illegal.
+                            throw new ConfigurationException.InterspersedDefaults(var, source, -1);
+                        }
+                    }
+                    iEnd = i;
+                    buffer.setVar(var, new LinkedList<String>(argList), source, -1, null, append);
+                    buffer.addPosition(var, iStart, iEnd);
+                    append = false;
+                    vars.add(var);
+                    argList.clear();
+                    mode = START;
+                    break;
+                }
+                case DONE:
+                {
+                    assert false;
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Given a string like "-foo" or "-5" or "-123.mxml", this determines
+     * whether the string is an argument or... not an argument (e.g. numeral)
+     */
+    private static boolean isAnArgument(final String arg)
+    {
+        return (arg.startsWith("-") &&
+                // if the first character after a dash is numeric, this is not
+                // an argument, it is a parameter (and therefore non-terminating)
+                (arg.length() > 1) && !Character.isDigit(arg.charAt(1)));
+    }
+
+    private static Map<String, String> getAliases(ConfigurationBuffer buffer)
+    {
+        Map<String, String> aliases = new HashMap<String, String>();
+        aliases.putAll(buffer.getAliases());
+        for (final String varname : buffer.getVars())
+        {
+            if (varname.indexOf('.') == -1)
+                continue;
+
+            String leafname = varname.substring(varname.lastIndexOf('.') + 1);
+            if (aliases.containsKey(leafname))
+                continue;
+            aliases.put(leafname, varname);
+        }
+
+        return aliases;
+    }
+
+    private static boolean isBoolean(ConfigurationBuffer buffer, String var)
+    {
+        ConfigurationInfo info = buffer.getInfo(var);
+
+        if (info.getArgCount() > 1)
+            return false;
+
+        Class<?> c = info.getArgType(0);
+
+        return ((c == boolean.class) || (c == Boolean.class));
+    }
+
+    public static String brief(String program, String defaultvar, LocalizationManager l10n, String l10nPrefix)
+    {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("defaultVar", defaultvar);
+        params.put("program", program);
+        return l10n.getLocalizedTextString(l10nPrefix + ".Brief", params);
+    }
+
+    public static String usage(String program, String defaultVar, ConfigurationBuffer cfgbuf, Set<String> keywords, LocalizationManager lmgr, String l10nPrefix)
+    {
+        boolean isCompc = program.contains("compc");
+        Map<String, String> aliases = getAliases(cfgbuf);
+        Map<String, String> sesaila = new HashMap<String, String>();
+        for (Iterator<Map.Entry<String, String>> it = aliases.entrySet().iterator(); it.hasNext();)
+        {
+            Map.Entry<String, String> e = it.next();
+            sesaila.put(e.getValue(), e.getKey());
+        }
+
+        TreeSet<String> printSet = new TreeSet<String>();
+
+        boolean all = false;
+        boolean advanced = false;
+        boolean details = false;
+        boolean syntax = false;
+        boolean printaliases = false;
+
+        // figure out behavior..
+        Set<String> newSet = new HashSet<String>();
+        for (Iterator<String> kit = keywords.iterator(); kit.hasNext();)
+        {
+            String keyword = kit.next();
+
+            if (keyword.equals("list"))
+            {
+                all = true;
+                newSet.add("*");
+            }
+            else if (keyword.equals("advanced"))
+            {
+                advanced = true;
+                if (keywords.size() == 1)
+                {
+                    all = true;
+                    newSet.add("*");
+                }
+            }
+            else if (keyword.equals("details"))
+            {
+                details = true;
+            }
+            else if (keyword.equals("syntax"))
+            {
+                syntax = true;
+            }
+            else if (keyword.equals("aliases"))
+            {
+                printaliases = true;
+            }
+            else
+            {
+                details = true;
+                newSet.add(keyword);
+            }
+        }
+        if (syntax)
+        {
+            final List<String> lines = ConfigurationBuffer.formatText(getSyntaxDescription(program, defaultVar, advanced, lmgr, l10nPrefix), 78);
+            return Joiner.on("\n").join(lines);
+        }
+        keywords = newSet;
+
+        // accumulate set to print
+        for (Iterator<String> kit = keywords.iterator(); kit.hasNext();)
+        {
+            String keyword = kit.next().toLowerCase();
+
+            for (final String var : cfgbuf.getVars())
+            {
+                ConfigurationInfo info = cfgbuf.getInfo(var);
+                
+                // If the client is not "compc", skip "compc-only" options.
+                if (info.isCompcOnly && !isCompc)
+                    continue;
+                
+                String description = getDescription(cfgbuf, var, lmgr, l10nPrefix);
+
+                if ((all
+                        || (var.indexOf(keyword) != -1)
+                        || ((description != null) && (description.toLowerCase().indexOf(keyword) != -1))
+                        || (keyword.matches(var))
+                        || ((sesaila.get(var) != null) && (sesaila.get(var)).indexOf(keyword) != -1))
+                     && (!info.isHidden())
+                     && (!info.isRemoved())
+                     && (advanced || !info.isAdvanced()))
+                {
+                    if (printaliases && sesaila.containsKey(var))
+                        printSet.add(sesaila.get(var));
+                    else
+                        printSet.add(var);
+                }
+                else
+                {
+                    /*
+                     * for (int i = 0; i < info.getAliases().length; ++i) {
+                     * String alias = info.getAliases()[i]; if (alias.indexOf(
+                     * keyword ) != -1) { printSet.add( var ); } }
+                     */
+                }
+            }
+        }
+
+        StringBuilder output = new StringBuilder(1024);
+
+        if (printSet.size() == 0)
+        {
+            String nkm = lmgr.getLocalizedTextString(l10nPrefix + ".NoKeywordsMatched");
+            output.append(nkm);
+            output.append("\n");
+        }
+        else
+            for (Iterator<String> it = printSet.iterator(); it.hasNext();)
+            {
+                String avar = it.next();
+                String var = avar;
+                if (aliases.containsKey(avar))
+                    var = aliases.get(avar);
+
+                ConfigurationInfo info = cfgbuf.getInfo(var);
+                assert info != null;
+
+                output.append("-");
+                output.append(avar);
+
+                int count = cfgbuf.getVarArgCount(var);
+                if ((count >= 1) && (!isBoolean(cfgbuf, var)))
+                {
+                    for (int i = 0; i < count; ++i)
+                    {
+                        output.append(" <");
+                        output.append(cfgbuf.getVarArgName(var, i));
+                        output.append(">");
+                    }
+                }
+                else if (count == -1)
+                {
+                    String last = "";
+                    for (int i = 0; i < 5; ++i)
+                    {
+                        String argname = cfgbuf.getVarArgName(var, i);
+                        if (!argname.equals(last))
+                        {
+                            output.append(" [");
+                            output.append(argname);
+                            output.append("]");
+                            last = argname;
+                        }
+                        else
+                        {
+                            output.append(" [...]");
+                            break;
+                        }
+                    }
+                }
+
+                output.append("\n");
+
+                if (details)
+                {
+                    StringBuilder description = new StringBuilder(160);
+                    if (printaliases)
+                    {
+                        if (aliases.containsKey(avar))
+                        {
+                            String fullname = lmgr.getLocalizedTextString(l10nPrefix + ".FullName");
+                            description.append(fullname);
+                            description.append(" -");
+                            description.append(aliases.get(avar));
+                            description.append("\n");
+                        }
+                    }
+                    else if (sesaila.containsKey(var))
+                    {
+                        String alias = lmgr.getLocalizedTextString(l10nPrefix + ".Alias");
+                        description.append(alias);
+                        description.append(" -");
+                        description.append(sesaila.get(var));
+                        description.append("\n");
+                    }
+
+                    String d = getDescription(cfgbuf, var, lmgr, l10nPrefix);
+                    if (var.equals("help") && (printSet.size() > 2))
+                    {
+                        String helpKeywords = lmgr.getLocalizedTextString(l10nPrefix + ".HelpKeywords");
+                        description.append(helpKeywords);
+                    }
+                    else if (d != null)
+                        description.append(d);
+
+                    String flags = "";
+                    if (info.isAdvanced())
+                    {
+                        String advancedString = lmgr.getLocalizedTextString(l10nPrefix + ".Advanced");
+                        flags += (((flags.length() == 0) ? " (" : ", ") + advancedString);
+                    }
+                    if (info.allowMultiple())
+                    {
+                        String repeatableString = lmgr.getLocalizedTextString(l10nPrefix + ".Repeatable");
+                        flags += (((flags.length() == 0) ? " (" : ", ") + repeatableString);
+                    }
+                    if ((defaultVar != null) && var.equals(defaultVar))
+                    {
+                        String defaultString = lmgr.getLocalizedTextString(l10nPrefix + ".Default");
+                        flags += (((flags.length() == 0) ? " (" : ", ") + defaultString);
+                    }
+                    if (info.isRoyaleOnly())
+                    {
+                        String royaleOnlylString = Messages.getString("RoyaleOnly");
+                        flags += (((flags.length() == 0) ? " (" : ", ") + royaleOnlylString);
+                    }
+                    if (flags.length() != 0)
+                    {
+                        flags += ")";
+                    }
+                    description.append(flags);
+
+                    List<String> descriptionLines = ConfigurationBuffer.formatText(description.toString(), 70);
+
+                    for (final String next : descriptionLines)
+                    {
+                        output.append("    ");
+                        output.append(next);
+                        output.append("\n");
+                    }
+                }
+            }
+        return output.toString();
+    }
+
+    public static String getDescription(ConfigurationBuffer buffer, String var, LocalizationManager l10n, String l10nPrefix)
+    {
+        String key = (l10nPrefix == null) ? var : (l10nPrefix + "." + var);
+        String description = l10n.getLocalizedTextString(key, null);
+
+        return description;
+    }
+
+    public static String getSyntaxDescription(String program, String defaultVar, boolean advanced, LocalizationManager l10n, String l10nPrefix)
+    {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("defaultVar", defaultVar);
+        params.put("program", program);
+
+        String key = l10nPrefix + "." + (advanced ? "AdvancedSyntax" : "Syntax");
+        String text = l10n.getLocalizedTextString(key, params);
+
+        if (text == null)
+        {
+            text = "No syntax help available, try '-help list' to list available configuration variables.";
+            assert false : "Localized text for syntax description not found!";
+        }
+        return text;
+    }
+
+    public static final String source = SOURCE_COMMAND_LINE;
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java b/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java
new file mode 100644
index 0000000..3f33aec
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/Configuration.java
@@ -0,0 +1,204 @@
+/*
+ *
+ *  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.royale.formatter.config;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.royale.compiler.exceptions.ConfigurationException;
+import org.apache.royale.compiler.internal.config.annotations.Arguments;
+import org.apache.royale.compiler.internal.config.annotations.Config;
+import org.apache.royale.compiler.internal.config.annotations.InfiniteArguments;
+import org.apache.royale.compiler.internal.config.annotations.Mapping;
+import org.apache.royale.compiler.problems.DeprecatedConfigurationOptionProblem;
+import org.apache.royale.compiler.problems.ICompilerProblem;
+import org.apache.royale.compiler.problems.RemovedConfigurationOptionProblem;
+
+public class Configuration {
+
+    private static Map<String, String> aliases = null;
+
+    public static Map<String, String> getAliases()
+    {
+        if (aliases == null)
+        {
+            aliases = new HashMap<String, String>();
+
+            aliases.put("w", "write-files");
+            aliases.put("l", "list-files");
+        }
+        return aliases;
+    }
+	
+    /**
+     * Collection of fatal and non-fatal configuration problems.
+     */
+    private Collection<ICompilerProblem> configurationProblems = new ArrayList<ICompilerProblem>();
+
+    /**
+     * Get the configuration problems. This should be called after the configuration has been processed.
+     * 
+     * @return a collection of fatal and non-fatal configuration problems.
+     */
+    public Collection<ICompilerProblem> getConfigurationProblems()
+    {
+        return configurationProblems;
+    }
+
+    /**
+     * Validate configuration options values.
+     * 
+     * @param configurationBuffer Configuration buffer.
+     * @throws ConfigurationException Error.
+     */
+    public void validate(ConfigurationBuffer configurationBuffer) throws ConfigurationException
+    {
+        // process the merged configuration buffer. right, can't just process the args.
+        processDeprecatedAndRemovedOptions(configurationBuffer);
+    }
+
+    private void processDeprecatedAndRemovedOptions(ConfigurationBuffer configurationBuffer)
+    {
+        for (final String var : configurationBuffer.getVars())
+        {
+            ConfigurationInfo info = configurationBuffer.getInfo(var);
+            List<ConfigurationValue> values = configurationBuffer.getVar(var);
+            if (values != null)
+            {
+                for (final ConfigurationValue cv : values)
+                {
+                    if (info.isRemoved())
+                    {
+                        addRemovedConfigurationOptionProblem(cv);
+                    }
+                    else if (info.isDeprecated() && configurationBuffer.getVar(var) != null)
+                    {
+                        String replacement = info.getDeprecatedReplacement();
+                        String since = info.getDeprecatedSince();
+                        DeprecatedConfigurationOptionProblem problem = new DeprecatedConfigurationOptionProblem(var,
+                                replacement, since, cv.getSource(), cv.getLine());
+                        configurationProblems.add(problem);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Add a RemovedConfigurationOptionProblem to the list of configuration problems.
+     * 
+     * @param cv
+     */
+    private void addRemovedConfigurationOptionProblem(ConfigurationValue cv)
+    {
+        RemovedConfigurationOptionProblem problem = new RemovedConfigurationOptionProblem(cv.getVar(), cv.getSource(),
+                cv.getLine());
+        configurationProblems.add(problem);
+    }
+
+    //
+    // 'help' option from CommandLineConfiguration
+    //
+
+    /**
+     * dummy, just a trigger for help text
+     */
+    @Config(displayed = false, greedy = true)
+    @Arguments("keyword")
+    @InfiniteArguments
+    public void setHelp(ConfigurationValue cv, String[] keywords)
+    {
+
+    }
+
+    //
+    // 'version' option from CommandLineConfiguration
+    //
+
+    /**
+     * Dummy option. Just a trigger for version info.
+     */
+    @Config
+    public void setVersion(ConfigurationValue cv, boolean value)
+    {
+    }
+
+    //
+    // 'files' option
+    //
+
+    private List<String> files = new ArrayList<String>();
+
+    /**
+     * @return A list of filespecs. It's the default variable for command line.
+     */
+    public List<String> getFiles()
+    {
+        return files;
+    }
+
+    @Config(allowMultiple = true, hidden = true)
+    @Mapping("files")
+    @Arguments(Arguments.PATH_ELEMENT)
+    @InfiniteArguments
+    public void setFiles(ConfigurationValue cv, List<String> args) throws ConfigurationException
+    {
+        this.files.addAll(args);
+    }
+
+    //
+    // 'write-files' option
+    //
+
+    private boolean writeFiles = false;
+
+    public boolean getWriteFiles()
+    {
+        return writeFiles;
+    }
+
+    @Config
+    @Mapping("write-files")
+    public void setWriteFiles(ConfigurationValue cv, boolean b)
+    {
+        this.writeFiles = b;
+    }
+
+    //
+    // 'list-files' option
+    //
+
+    private boolean listFiles = false;
+
+    public boolean getListFiles()
+    {
+        return listFiles;
+    }
+
+    @Config
+    @Mapping("list-files")
+    public void setListFiles(ConfigurationValue cv, boolean b)
+    {
+        this.listFiles = b;
+    }
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationBuffer.java b/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationBuffer.java
new file mode 100644
index 0000000..35a6e37
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationBuffer.java
@@ -0,0 +1,1346 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.royale.formatter.config;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.royale.compiler.exceptions.ConfigurationException;
+import org.apache.royale.compiler.internal.config.IConfigurationFilter;
+import org.apache.royale.compiler.internal.config.annotations.ArgumentNameGenerator;
+import org.apache.royale.compiler.internal.config.annotations.Arguments;
+import org.apache.royale.compiler.internal.config.annotations.Config;
+import org.apache.royale.compiler.internal.config.annotations.RoyaleOnly;
+import org.apache.royale.compiler.internal.config.annotations.InfiniteArguments;
+import org.apache.royale.compiler.internal.config.annotations.Mapping;
+import org.apache.royale.compiler.internal.config.annotations.SoftPrerequisites;
+import org.apache.royale.compiler.problems.ConfigurationProblem;
+import org.apache.royale.compiler.problems.ICompilerProblem;
+import org.apache.royale.utils.Trace;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * The basic idea here is to let you keep all your configuration knowledge in
+ * your configuration object, and to automate as much as possible. Reflection is
+ * used to convert public fields and setters on your configuration object into
+ * settable vars. There are a few key concepts:
+ * <p>
+ * - You should be able to configure absolutely any object.<br>
+ * - Child configuration variables in your config become a dotted hierarchy of
+ * varnames<br>
+ * - All sources of configuration data are buffered and merged (as string
+ * var/vals) before committing to the final configuration. This class acts as
+ * the buffer.<br>
+ * - Hyphenated variables (i.e. "some-var") are automatically configured by
+ * calling your matching setter (i.e. setSomeVar)<br>
+ * - Implementing an getSomeVarInfo() method on your class lets you set up more
+ * complicated config objects<br>
+ * - You can make variables depend on other variables having been set first.
+ * This lets you set a root directory in one var and then use its value in
+ * another.<br>
+ * - Per-variable validation can be performed in setters. Overall validation
+ * should take place as a post-process step.<br>
+ * - You can keep ConfigurationBuffers around and merge multiple buffers
+ * together before committing. Most recent definitions always win.<br>
+ * <p>
+ * The contract with your configuration class:
+ * <p>
+ * - You must provide a method with the signature
+ * "void setYourVar(ConfigurationValue val)" to set your config var. Your setter
+ * method should accept either a single arg of type List or String[], or else an
+ * arglist of simple types. For example
+ * "void myvar(int a, boolean b, String c")".<br>
+ * - You can implement a function with the signature "int yourvar_argcount()" to
+ * require a different number of arguments. This limit will be enforced by
+ * configurators (command line, file, etc.)<br>
+ * - If you provide a setter and explicit parameters (i.e. not List or String[])
+ * the number of arguments will be automatically determined.<br>
+ * - Each argument to your configuration variable is assumed to have a
+ * (potentially non-unique) name. The default is the simple type of the argument
+ * (boolean, int, string). If the var takes an undetermined number of args via
+ * List or String[], the argname defaults to string.<br>
+ * - You can implement a function with the signature
+ * "String yourvar_argnames(int)" to provide names for each of the parameters.
+ * The integer passed in is the argument number. Return the same name (i.e.
+ * "item") for infinite lists.<br>
+ * - You can implement a function with the signature "String[] yourvar_deps()"
+ * to provide a list of other prerequisites for this var. You will be guaranteed
+ * that the deps are committed before your var, or else a configurationexception
+ * will be thrown if a prerequsite was unset. (Note that infinite cycles are not
+ * checked, so be careful.)<br>
+ */
+public final class ConfigurationBuffer
+{
+    public ConfigurationBuffer(Class<? extends Configuration> configClass)
+    {
+        this(configClass, new HashMap<String, String>());
+    }
+
+    public ConfigurationBuffer(Class<? extends Configuration> configClass, Map<String, String> aliases)
+    {
+        this(configClass, aliases, null);
+    }
+
+    /**
+     * Create a configuration buffer with an optional filter. The filter can be
+     * used to remove unwanted options from a super class.
+     * 
+     * @param filter if null there is no filter, otherwise the set of
+     * configuration options is filtered.
+     */
+    public ConfigurationBuffer(Class<? extends Configuration> configClass, Map<String, String> aliases, IConfigurationFilter filter)
+    {
+        this.configClass = configClass;
+        this.varMap = new HashMap<String, List<ConfigurationValue>>();
+        this.committed = new HashSet<String>();
+
+        loadCache(configClass, filter);
+        assert (varCache.size() > 0) : "coding error: nothing was configurable in the provided object!";
+        for (Map.Entry<String, String> e : aliases.entrySet())
+        {
+            addAlias(e.getKey(), e.getValue());
+        }
+    }
+
+    public ConfigurationBuffer(ConfigurationBuffer copyFrom, boolean copyCommitted)
+    {
+        this.configClass = copyFrom.configClass;
+        this.varMap = new HashMap<String, List<ConfigurationValue>>(copyFrom.varMap);
+        this.committed = copyCommitted ? new HashSet<String>(copyFrom.committed) : new HashSet<String>();
+        this.varCache = copyFrom.varCache; // doesn't change after creation
+        this.varList = copyFrom.varList; // doesn't change after creation
+        this.tokens = new HashMap<String, String>(copyFrom.tokens);
+    }
+
+    public final List<String> dump()
+    {
+        final List<String> dump = new ArrayList<String>(varCache.size());
+        for (final Map.Entry<String, ConfigurationInfo> entry : varCache.entrySet())
+        {
+            dump.add(entry.getKey() + "," + entry.getValue().toString());
+        }
+        Collections.sort(dump);
+        return dump;
+    }
+
+    public void setVar(String var, String val, String source, int line) throws ConfigurationException
+    {
+        List<String> list = new LinkedList<String>();
+        list.add(val);
+        setVar(var, list, source, line, null, false);
+    }
+
+    public void setVar(String var, List<String> vals, String source, int line) throws ConfigurationException
+    {
+        setVar(var, vals, source, line, null, false);
+    }
+
+    public void setVar(String avar, List<String> vals, String source, int line, String contextPath, boolean append) throws ConfigurationException
+    {
+        String var = unalias(avar);
+        if (!isValidVar(var))
+            throw new ConfigurationException.UnknownVariable(var, source, line);
+
+        int argCount = getVarArgCount(var);
+
+        // -1 means unspecified length, its up to the receiving setter to validate.
+        if (argCount != -1)
+        {
+            addAnyDefaultArgValues(var, argCount, vals);
+
+            if (vals.size() != argCount)
+            {
+                throw new ConfigurationException.IncorrectArgumentCount(argCount, // expected
+                        vals.size(), //passed
+                var, source, line);
+            }
+        }
+
+        ConfigurationValue val = new ConfigurationValue(this, var,
+                                                         vals, //processValues( var, vals, source, line ),
+                                                         source, line, contextPath);
+        storeValue(var, val, append);
+        committed.remove(var);
+    }
+
+    public void clearVar(String avar, String source, int line) throws ConfigurationException
+    {
+        String var = unalias(avar);
+        if (!isValidVar(var))
+            throw new ConfigurationException.UnknownVariable(var, source, line);
+        varMap.remove(var);
+        committed.remove(var);
+    }
+
+    /**
+     * Remove the configuration values came from the given source.
+     * 
+     * @param source source name
+     * @see CommandLineConfigurator#SOURCE_COMMAND_LINE
+     */
+    public void clearSourceVars(String source)
+    {
+        List<String> remove = new LinkedList<String>();
+        for (Map.Entry<String, List<ConfigurationValue>> e : varMap.entrySet())
+        {
+            String var = e.getKey();
+            List<ConfigurationValue> vals = e.getValue();
+
+            List<ConfigurationValue> newvals = new LinkedList<ConfigurationValue>();
+            for (ConfigurationValue val : vals)
+            {
+                if (!val.getSource().equals(source))
+                {
+                    newvals.add(val);
+                }
+            }
+            if (newvals.size() > 0)
+                varMap.put(var, newvals);
+            else
+                remove.add(var);
+        }
+        for (Iterator<String> it = remove.iterator(); it.hasNext();)
+        {
+            varMap.remove(it.next());
+        }
+    }
+
+    public List<String> processValues(String var, List<String> args, String source, int line) throws ConfigurationException
+    {
+        List<String> newArgs = new LinkedList<String>();
+        for (Iterator<String> it = args.iterator(); it.hasNext();)
+        {
+            String arg = it.next();
+
+            int depth = 100;
+            while (depth-- > 0)
+            {
+                int o = arg.indexOf("${");
+                if (o == -1)
+                    break;
+
+                int c = arg.indexOf("}", o);
+
+                if (c == -1)
+                {
+                    throw new ConfigurationException.Token(ConfigurationException.Token.MISSING_DELIMITER,
+                                                           null, var, source, line);
+                }
+                String token = arg.substring(o + 2, c);
+                String value = getToken(token);
+
+                if (value == null)
+                {
+                    if (value == null)
+
+                    {
+                        throw new ConfigurationException.Token(ConfigurationException.Token.UNKNOWN_TOKEN,
+                                                                token, var, source, line);
+                    }
+
+                }
+                arg = arg.substring(0, o) + value + arg.substring(c + 1);
+
+            }
+            if (depth == 0)
+            {
+                throw new ConfigurationException.Token(ConfigurationException.Token.RECURSION_LIMIT,
+                                                        null, var, source, line);
+            }
+
+            newArgs.add(arg);
+        }
+        return newArgs;
+    }
+
+    public void setToken(String token, String value)
+    {
+        tokens.put(token, value);
+    }
+
+    public String getToken(String token)
+    {
+        if (tokens.containsKey(token))
+            return tokens.get(token);
+        else
+        {
+            try
+            {
+                return System.getProperty(token);
+            }
+            catch (SecurityException se)
+            {
+                return null;
+            }
+        }
+    }
+
+    private void storeValue(String avar, ConfigurationValue val, boolean append) throws ConfigurationException
+    {
+        String var = unalias(avar);
+        ConfigurationInfo info = getInfo(var);
+
+        List<ConfigurationValue> vals;
+        if (varMap.containsKey(var))
+        {
+            vals = varMap.get(var);
+            assert (vals.size() > 0);
+            ConfigurationValue first = vals.get(0);
+            if (!append && !first.getSource().equals(val.getSource()))
+                vals.clear();
+            else if (!info.allowMultiple())
+                throw new ConfigurationException.IllegalMultipleSet(
+                                                  var,
+                                                  val.getSource(), val.getLine());
+        }
+        else
+        {
+            vals = new LinkedList<ConfigurationValue>();
+            varMap.put(var, vals);
+        }
+        vals.add(val);
+    }
+
+    public List<ConfigurationValue> getVar(String avar)
+    {
+        String var = unalias(avar);
+        return varMap.get(var);
+    }
+
+    public Set<String> getVars()
+    {
+        return varCache.keySet();
+    }
+
+    public void merge(ConfigurationBuffer other)
+    {
+        assert (configClass == other.configClass);
+        varMap.putAll(other.varMap);
+        committed.addAll(other.committed);
+    }
+
+    private final Map<String, List<ConfigurationValue>> varMap; // list of vars that have been set
+    private final Set<String> committed; // set of vars committed to backing config
+    private final Class<? extends Configuration> configClass; // configuration class
+    private Map<String, ConfigurationInfo> varCache // info cache
+    = new HashMap<String, ConfigurationInfo>();
+    private List<String> requiredList = new LinkedList<String>(); // required vars
+    private List<String> varList = new LinkedList<String>(); // list of vars in order they should be set
+    private Map<String, String> aliases = new HashMap<String, String>(); // variable name aliases
+    private Map<String, String> tokens = new HashMap<String, String>(); // tokens for replacement
+    private List<Object[]> positions = new ArrayList<Object[]>();
+
+    private static final String SET_PREFIX = "cfg";
+    private static final String GET_PREFIX = "get";
+    private static final String INFO_SUFFIX = "Info";
+
+    //-----------------------------------------------
+    //
+
+    /**
+     * WORKAROUND FOR BUG CMP-396
+     * 
+     * <p>
+     * {@link #c2h(String)} generates option names based on cfgXXX names in
+     * {@code Configuration}. Since we collapsed all the sub-configurations into
+     * one class, there's no longer a "base name" like "compiler.*" or
+     * "compiler.fonts.*". In order to preserve the dotted naming convention, we
+     * need to know which "-" separated names are actually dotted names. The
+     * {@link #CONVERT_FROM} and {@link #CONVERT_TO} is an <b>ordered</b> lookup
+     * table for option group base names. It's order makes sure that the longest
+     * possible replacement is done.
+     */
+    private static final ImmutableList<String> CONVERT_FROM =
+            ImmutableList.of(
+                    "compiler-fonts-languages-",
+                    "compiler-fonts-",
+                    "compiler-namespaces-",
+                    "compiler-mxml-",
+                    "compiler-",
+                    "metadata-",
+                    "licenses-",
+                    "frames-",
+                    "runtime-shared-library-settings-");
+
+    private static final ImmutableList<String> CONVERT_TO =
+            ImmutableList.of(
+                    "compiler.fonts.languages.",
+                    "compiler.fonts.",
+                    "compiler.namespaces.",
+                    "compiler.mxml.",
+                    "compiler.",
+                    "metadata.",
+                    "licenses.",
+                    "frames.",
+                    "runtime-shared-library-settings.");
+
+    /**
+     * convert StudlyCaps or camelCase to hyphenated
+     * 
+     * @param camel someVar or SomeVar
+     * @return hyphen some-var
+     */
+    protected static String c2h(String camel)
+    {
+        StringBuilder b = new StringBuilder(camel.length() + 5);
+        for (int i = 0; i < camel.length(); ++i)
+        {
+            char c = camel.charAt(i);
+            if (Character.isUpperCase(c))
+            {
+                if (i != 0)
+                    b.append('-');
+                b.append(Character.toLowerCase(c));
+            }
+            else
+            {
+                b.append(camel.charAt(i));
+            }
+        }
+        final String combined = b.toString();
+
+        for (int i = 0; i < CONVERT_FROM.size(); i++)
+        {
+            if (combined.startsWith(CONVERT_FROM.get(i)))
+            {
+                return combined.replaceFirst(CONVERT_FROM.get(i), CONVERT_TO.get(i));
+            }
+        }
+        return combined;
+    }
+
+    /**
+     * convert hyphenated to StudlyCaps or camelCase
+     * 
+     * @param hyphenated some-var
+     * @return result
+     */
+    protected static String h2c(String hyphenated, boolean studly)
+    {
+        StringBuilder b = new StringBuilder(hyphenated.length());
+        boolean capNext = studly;
+        for (int i = 0; i < hyphenated.length(); ++i)
+        {
+            char c = hyphenated.charAt(i);
+            if (c == '-')
+                capNext = true;
+            else
+            {
+                b.append(capNext ? Character.toUpperCase(c) : c);
+                capNext = false;
+            }
+        }
+        return b.toString();
+    }
+
+    public static String varname(String membername, String basename)
+    {
+        return ((basename == null) ? membername : (basename + "." + membername));
+    }
+
+    private static ConfigurationInfo createInfo(Method setterMethod)
+    {
+        ConfigurationInfo info = null;
+
+        String infoMethodName = GET_PREFIX + setterMethod.getName().substring(SET_PREFIX.length()) + INFO_SUFFIX;
+        String getterMethodName = GET_PREFIX + setterMethod.getName().substring(SET_PREFIX.length());
+        @SuppressWarnings("unchecked")
+        Class<? extends Configuration> cfgClass = (Class<? extends Configuration>)setterMethod.getDeclaringClass();
+
+        Method infoMethod = null, getterMethod = null;
+        if (!setterMethod.isAnnotationPresent(Config.class))
+        {
+            try
+            {
+                infoMethod = cfgClass.getMethod(infoMethodName);
+    
+                if (!Modifier.isStatic(infoMethod.getModifiers()))
+                {
+                    assert false : ("coding error: " + cfgClass.getName() + "." + infoMethodName + " needs to be static!");
+                    infoMethod = null;
+                }
+    
+                info = (ConfigurationInfo)infoMethod.invoke(null, (Object[])null);
+    
+            }
+            catch (SecurityException e)
+            {
+                e.printStackTrace();
+            }
+            catch (NoSuchMethodException e)
+            {
+            }
+            catch (IllegalArgumentException e)
+            {
+                e.printStackTrace();
+            }
+            catch (IllegalAccessException e)
+            {
+                e.printStackTrace();
+            }
+            catch (InvocationTargetException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        if (info == null)
+            info = new ConfigurationInfo();
+
+        try
+        {
+            getterMethod = cfgClass.getMethod(getterMethodName, (Class[])null);
+        }
+        catch (SecurityException e)
+        {
+            e.printStackTrace();
+        }
+        catch (NoSuchMethodException e)
+        {
+        }
+        info.setSetterMethod(setterMethod);
+        info.setGetterMethod(getterMethod);
+
+        return info;
+    }
+
+    /**
+     * load - prefetch all the interesting names into a dictionary so that we
+     * can find them again more easily. At the end of this call, we will have a
+     * list of every variable and their associated method.
+     * 
+     * @param filter if null there is no filter, otherwise the set of
+     * configuration options is filtered.
+     */
+    private boolean loadCache(Class<? extends Configuration> cfg, IConfigurationFilter filter)
+    {
+        int count = 0;
+
+        // First, find all vars at this level.
+        for (final Method method : cfg.getMethods())
+        {
+            if (method.getName().startsWith(SET_PREFIX) ||
+                method.isAnnotationPresent(Config.class))
+            {
+                String configName = null;
+
+                final Class<?>[] pt = method.getParameterTypes();
+                assert pt.length > 1 : "Expected at least one parameters on setter.";
+
+                // Collect configuration info from getXXXInfo() static methods.
+                final ConfigurationInfo info = createInfo(method);
+
+                // Collect configuration info from annotations.
+                final Config config = method.getAnnotation(Config.class);
+                if (config != null)
+                {
+                    info.isAdvanced = config.advanced();
+                    info.isHidden = config.hidden();
+                    info.isRemoved = config.removed();
+                    info.allowMultiple = config.allowMultiple();
+                    info.isPath = config.isPath();
+                    info.isDisplayed = config.displayed();
+                    info.isCompcOnly = config.compcOnly();
+                    info.isRequired = config.isRequired();
+                    
+                    // Argument name generator class
+                    final ArgumentNameGenerator argumentNameGeneratorClass = 
+                        method.getAnnotation(ArgumentNameGenerator.class);
+                    if (argumentNameGeneratorClass != null)
+                    {
+                        info.argNameGeneratorClass = argumentNameGeneratorClass.value();
+                    }
+                    else
+                    {
+                        // Argument names
+                        final Arguments arguments = method.getAnnotation(Arguments.class);
+                        if (arguments != null)
+                            info.argnames = arguments.value();
+                    }
+                    
+                    // Argument count
+                    final InfiniteArguments infinite = method.getAnnotation(InfiniteArguments.class);
+                    if (infinite != null)
+                        info.argcount = ConfigurationInfo.INFINITE_ARGS;
+
+                    // Soft Prerequisites
+                    final SoftPrerequisites softPre = method.getAnnotation(SoftPrerequisites.class);
+                    if (softPre != null)
+                        info.softPrerequisites = softPre.value();
+                    
+                    // XML element name for configuration
+                    final Mapping mapping = method.getAnnotation(Mapping.class);
+                    if (mapping != null)
+                        configName = Joiner.on(".").skipNulls().join(mapping.value());
+                    
+                    // Is this a Flex only option?
+                    final RoyaleOnly royaleOnly = method.getAnnotation(RoyaleOnly.class);
+                    if (royaleOnly != null)
+                        info.isRoyaleOnly = true;
+                }
+
+                // Fall back to naming convention for configuration names.
+                if (configName == null)
+                    configName = c2h(method.getName().substring(SET_PREFIX.length()));
+
+                if( filter == null || filter.select(configName) )
+                {
+                    varCache.put(configName, info);
+                    varList.add(configName);
+                    if (info.isRequired())
+                    {
+                        requiredList.add(configName);
+                    }
+                    ++count;
+                }
+            }
+        }
+
+        assert (count > 0 || filter != null) : "coding error: config class " + cfg.getName() + " did not define any setters or child configs";
+        return (count > 0);
+    }
+
+    String classToArgName(Class<?> c)
+    {
+        // we only support builtin classnames!
+
+        String className = c.getName();
+        if (className.startsWith("java.lang."))
+            className = className.substring("java.lang.".length());
+
+        return className.toLowerCase();
+    }
+
+    public ConfigurationInfo getInfo(String avar)
+    {
+        String var = unalias(avar);
+        return varCache.get(var);
+    }
+
+    public String getVarArgName(String avar, int argnum)
+    {
+        String var = unalias(avar);
+        ConfigurationInfo info = getInfo(var);
+
+        if (info == null)
+        {
+            assert false : ("must call isValid to check vars!");
+        }
+
+        return info.getArgName(argnum);
+    }
+
+    public boolean isValidVar(String avar)
+    {
+        String var = unalias(avar);
+        ConfigurationInfo info = getInfo(var);
+        return (info != null);
+    }
+
+    public int getVarArgCount(String avar)
+    {
+        ConfigurationInfo info = getInfo(avar);
+        assert (info != null);
+        return info.getArgCount();
+    }
+
+    /**
+     * Add any default values to an argument, if the user did not specify them
+     * on the command line.
+     * 
+     * @param avar the argument variable
+     * @param argCount the number of argument values specified
+     * @param vals Values to add any default values to
+     */
+    private void addAnyDefaultArgValues(String avar, int argCount, List<String> vals)
+    {
+        ConfigurationInfo info = getInfo(avar);
+        final int missingArgsCount = argCount - vals.size();
+        if (missingArgsCount == 0 || info.getDefaultArgValues() == null)
+            return;
+
+        final String[] defaultArgValues = info.getDefaultArgValues();
+        final int defaultArgsCount = defaultArgValues.length;
+        final int defaultArgsStart = defaultArgsCount - missingArgsCount;
+        for (int i = defaultArgsStart; i < defaultArgsCount; i++)
+        {
+            vals.add(defaultArgValues[i]);
+        }
+    }
+
+    /**
+     * commit - bake the resolved map to the configuration
+     * 
+     * @param config The configuration to set the buffer variables into.
+     * @param problems A collection where configuration problems are reported.
+     * 
+     * @return true if successful, false otherwise.
+     */
+    public boolean commit(Object config, Collection<ICompilerProblem> problems)
+    {
+        assert (config.getClass() == configClass) : 
+            ("coding error: configuration " + config.getClass() + " != template " + configClass);
+        Set<String> done = new HashSet<String>();
+        boolean success = true;
+        
+        for (Iterator<String> vars = varList.iterator(); vars.hasNext();)
+        {
+            String var = vars.next();
+            if (varMap.containsKey(var))
+            {
+                try
+                {
+                    commitVariable(config, var, done);
+                }
+                catch (ConfigurationException e)
+                {
+                    problems.add(new ConfigurationProblem(e));
+                    success = false;
+                }
+            }
+        }
+
+        for (Iterator<String> reqs = requiredList.iterator(); reqs.hasNext();)
+        {
+            String req = reqs.next();
+
+            if (!committed.contains(req))
+            {
+                ConfigurationException e = new ConfigurationException.MissingRequirement(req, null, null, -1);
+                problems.add(new ConfigurationProblem(
+                        null,
+                        -1,
+                        -1,
+                        -1,
+                        -1,
+                        e.getMessage()));
+                success = false;
+            }
+        }
+        
+        return success;
+    }
+
+    /**
+     * commitVariable - copy a variable out of a state into the final config.
+     * This should only be called on variables that are known to exist in the
+     * state!
+     * 
+     * @param var variable name to lookup
+     * @param done set of variable names that have been completed so far (for
+     * recursion)
+     */
+    private void commitVariable(Object config, String var, Set<String> done) throws ConfigurationException
+    {
+        ConfigurationInfo info = getInfo(var);
+
+        setPrerequisites(info.getPrerequisites(), var, done, config, true);
+        setPrerequisites(info.getSoftPrerequisites(), var, done, config, false);
+
+        if (committed.contains(var))
+            return;
+
+        committed.add(var);
+        done.add(var);
+
+        assert (varMap.containsKey(var));
+        List<ConfigurationValue> vals = varMap.get(var);
+
+        if (vals.size() > 1)
+        {
+            assert (info.allowMultiple()); // assumed to have been previously checked
+        }
+        for (ConfigurationValue val : vals)
+        {
+            try
+            {
+                Object[] args = buildArgList(info, val);
+
+                info.getSetterMethod().invoke(config, args);
+            }
+            catch (Exception e)
+            {
+                Throwable t = e;
+
+                if (e instanceof InvocationTargetException)
+                {
+                    t = ((InvocationTargetException)e).getTargetException();
+                }
+
+                if (Trace.error)
+                    t.printStackTrace();
+
+                if (t instanceof ConfigurationException)
+                {
+                    throw (ConfigurationException)t;
+                }
+                else
+                {
+                    throw new ConfigurationException.OtherThrowable(t, var, val.getSource(), val.getLine());
+                }
+            }
+        }
+
+    }
+
+    private void setPrerequisites(String[] prerequisites, String var, Set<String> done, Object config, boolean required)
+            throws ConfigurationException
+    {
+        if (prerequisites != null)
+        {
+            for (int p = 0; p < prerequisites.length; ++p)
+            {
+                String depvar = prerequisites[p];
+
+                // Dependencies can only go downward.
+                int dot = var.lastIndexOf('.');
+
+                if (dot >= 0)
+                {
+                    String car = var.substring(0, dot);
+                    //String cdr = var.substring( dot + 1 );
+
+                    String newDepvar = car + "." + depvar;
+
+                    // Since in royale we have collapsed sub-configurations into one
+                    // configuration, some options that were in sub-configurations now
+                    // have prerequisites on options in the same configuration. We
+                    // need to keep the old configuration mappings so old configurations
+                    // options will still work. So a simple thing we can do is if the 
+                    // dependency variable is invalid (presumably because the 
+                    // dependency is really on a parent configuration option), 
+                    // then use the dependency as is depvar) and see if it is 
+                    // valid. If depvar ends up not being valid then set depvar
+                    // to newDepvar so error reporting isn't changed by the new
+                    // fall-back behavior.
+                    if (isValidVar(newDepvar) || !isValidVar(depvar))
+                        depvar = newDepvar;
+
+                }
+
+                if (!done.contains(depvar))
+                {
+                    if (!isValidVar(depvar))
+                    {
+                        assert false : ("invalid " + var + " dependency " + depvar);
+                        continue;
+                    }
+                    if (varMap.containsKey(depvar))
+                    {
+                        commitVariable(config, depvar, done);
+                    }
+                    else if (required && !committed.contains(depvar))
+                    {
+                        // TODO - can we get source/line for this?
+                        throw new ConfigurationException.MissingRequirement(depvar, var, null, -1);
+                    }
+                }
+            }
+        }
+    }
+
+    private String[] constructStringArray(List<String> args)
+    {
+        String[] sa = new String[args.size()];
+
+        int i = 0;
+        for (Iterator<String> it = args.iterator(); it.hasNext();)
+            sa[i++] = it.next();
+
+        return sa;
+    }
+
+    private Object constructValueObject(ConfigurationInfo info, ConfigurationValue cv) throws ConfigurationException
+    {
+        try
+        {
+            Class<?>[] pt = info.getSetterMethod().getParameterTypes();
+            assert (pt.length == 2); // assumed to be checked upstream
+
+            Object o = pt[1].newInstance();
+
+            Field[] fields = pt[1].getFields();
+
+            assert (fields.length == cv.getArgs().size()); // assumed to be checked upstream
+
+            Iterator<String> argsit = cv.getArgs().iterator();
+            for (int f = 0; f < fields.length; ++f)
+            {
+                String val = (String)argsit.next();
+                Object valobj = null;
+                Class<?> fc = fields[f].getType();
+
+                assert (info.getArgType(f) == fc);
+                assert (info.getArgName(f).equals(ConfigurationBuffer.c2h(fields[f].getName())));
+
+                if (fc == String.class)
+                {
+                    valobj = val;
+                }
+                else if ((fc == Boolean.class) || (fc == boolean.class))
+                {
+                    // TODO - Boolean.valueOf is pretty lax.  Maybe we should restrict to true/false?
+                    valobj = Boolean.valueOf(val);
+                }
+                else if ((fc == Integer.class) || (fc == int.class))
+                {
+                    valobj = Integer.decode(val);
+                }
+                else if ((fc == Long.class) || (fc == long.class))
+                {
+                    valobj = Long.decode(val);
+                }
+                else
+                {
+                    assert false; // should have checked any other condition upstream!
+                }
+                fields[f].set(o, valobj);
+            }
+
+            return o;
+        }
+        catch (InstantiationException e)
+        {
+            assert false : ("coding error: unable to instantiate value object when trying to set var " + cv.getVar());
+            throw new ConfigurationException.OtherThrowable(e, cv.getVar(), cv.getSource(), cv.getLine());
+
+        }
+        catch (IllegalAccessException e)
+        {
+            assert false : ("coding error: " + e + " when trying to set var " + cv.getVar());
+            throw new ConfigurationException.OtherThrowable(e, cv.getVar(), cv.getSource(), cv.getLine());
+        }
+    }
+
+    protected static boolean isSupportedSimpleType(Class<?> c)
+    {
+        return ((c == String.class)
+                || (c == Integer.class) || (c == int.class)
+                || (c == Long.class) || (c == long.class)
+                || (c == Boolean.class) || (c == boolean.class));
+    }
+
+    protected static boolean isSupportedListType(Class<?> c)
+    {
+        return ((c == List.class) || (c == String[].class));
+    }
+
+    protected static boolean isSupportedValueType(Class<?> c)
+    {
+        if (isSupportedSimpleType(c))
+            return false;
+
+        Field[] fields = c.getFields();
+
+        for (int f = 0; f < fields.length; ++f)
+        {
+            if (!isSupportedSimpleType(fields[f].getType()))
+                return false;
+        }
+        return true;
+    }
+
+    private Object[] buildArgList(ConfigurationInfo info, ConfigurationValue val) throws ConfigurationException
+    {
+        Method setter = info.getSetterMethod();
+
+        Class<?>[] pt = setter.getParameterTypes();
+
+        List<String> args = processValues(val.getVar(), val.getArgs(), val.getSource(), val.getLine());
+
+        if (info.getArgCount() == -1)
+        {
+            if (pt.length != 2)
+            {
+                assert false : ("coding error: unlimited length setter " + val.getVar() + " must take a single argument of type List or String[]");
+                return null;
+            }
+            else if (List.class.isAssignableFrom(pt[1]))
+            {
+                return new Object[] {val, args};
+            }
+            else if (String[].class.isAssignableFrom(pt[1]))
+            {
+                return new Object[] {val, constructStringArray(args)};
+            }
+            else
+            {
+                assert false : ("coding error: unlimited length setter " + val.getVar() + " must take a single argument of type List or String[]");
+                return null;
+            }
+        }
+        else
+        {
+            assert (pt.length > 1) : ("coding error: config setter " + val.getVar() + " must accept at least one argument");
+            // ok, we first check to see if the signature of their setter accepts a list.
+
+            if (pt.length == 2)
+            {
+                // a variety of specialty setters here...
+
+                if (List.class.isAssignableFrom(pt[1]))
+                {
+                    return new Object[] {val, args};
+                }
+                else if (String[].class == pt[1])
+                {
+                    return new Object[] {val, constructStringArray(args)};
+                }
+                else if (isSupportedValueType(pt[1]))
+                {
+                    return new Object[] {val, constructValueObject(info, val)};
+                }
+            }
+
+            // otherwise, they must have a matching size parm list as the number of args passed in.
+
+            assert (pt.length == (args.size() + 1)) : ("coding error: config setter " + val.getVar() + " does not have " + args.size() + " parameters!");
+
+            Object[] pa = new Object[pt.length];
+
+            pa[0] = val;
+
+            for (int p = 1; p < pt.length; ++p)
+            {
+                String arg = args.get(p - 1);
+                if (pt[p].isAssignableFrom(String.class))
+                {
+                    pa[p] = arg;
+                }
+                else if ((pt[p] == int.class) || (pt[p] == Integer.class))
+                {
+                    try
+                    {
+                        pa[p] = Integer.decode(arg);
+
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ConfigurationException.TypeMismatch(ConfigurationException.TypeMismatch.INTEGER,
+                                                                       arg, val.getVar(), val.getSource(), val.getLine());
+                    }
+                }
+                else if ((pt[p] == long.class) || (pt[p] == Long.class))
+                {
+                    try
+                    {
+                        pa[p] = Long.decode(arg);
+
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ConfigurationException.TypeMismatch(
+                                ConfigurationException.TypeMismatch.LONG,
+                                arg, val.getVar(), val.getSource(), val.getLine());
+                    }
+                }
+                else if ((pt[p] == boolean.class) || (pt[p] == Boolean.class))
+                {
+                    try
+                    {
+                        arg = arg.trim().toLowerCase();
+                        if (arg.equals("true") || arg.equals("false"))
+                        {
+                            pa[p] = Boolean.valueOf(arg);
+                        }
+                        else
+                        {
+                            throw new ConfigurationException.TypeMismatch(
+                                    ConfigurationException.TypeMismatch.BOOLEAN, arg, val.getVar(), val.getSource(), val.getLine());
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ConfigurationException.TypeMismatch(
+                                ConfigurationException.TypeMismatch.BOOLEAN, arg, val.getVar(), val.getSource(), val.getLine());
+                    }
+                }
+                else
+                {
+                    assert false : ("coding error: " + val.getVar() + " setter argument " + p + " is not a supported type");
+                }
+            }
+
+            return pa;
+        }
+    }
+
+    public void addAlias(String alias, String var)
+    {
+        if (!isValidVar(var))
+        {
+            assert false : ("coding error: can't bind alias " + alias + " to nonexistent var " + var);
+            return;
+        }
+        if (aliases.containsKey(alias))
+        {
+            assert false : ("coding error: alias " + alias + " already defined as " + aliases.get(alias));
+            return;
+        }
+        if (varCache.containsKey(alias))
+        {
+            assert false : ("coding error: can't define alias " + alias + ", it already exists as a var");
+            return;
+        }
+
+        aliases.put(alias, var);
+    }
+
+    public Map<String, String> getAliases()
+    {
+        return aliases;
+    }
+
+    public String unalias(String var)
+    {
+        String realvar = aliases.get(var);
+        return (realvar == null) ? var : realvar;
+    }
+
+    public String peekSimpleConfigurationVar(String avar) throws ConfigurationException
+    {
+        String val = null;
+        List<ConfigurationValue> valList = getVar(avar);
+        if (valList != null)
+        {
+            ConfigurationValue cv = (ConfigurationValue)valList.get(0);
+            List<String> args = processValues(avar, cv.getArgs(), cv.getSource(), cv.getLine());
+            val = args.get(0);
+        }
+        return val;
+    }
+
+    public List<ConfigurationValue> peekConfigurationVar(String avar) throws ConfigurationException
+    {
+        List<ConfigurationValue> srcList = getVar(avar);
+        if (srcList == null)
+            return null;
+
+        List<ConfigurationValue> dstList = new LinkedList<ConfigurationValue>();
+        for (ConfigurationValue srcVal : srcList)
+        {
+            List<String> args = processValues(avar, srcVal.getArgs(), srcVal.getSource(), srcVal.getLine());
+
+            ConfigurationValue dstVal = new ConfigurationValue(srcVal.getBuffer(), avar, args, srcVal.getSource(), srcVal.getLine(), srcVal.getContext());
+            dstList.add(dstVal);
+        }
+        return dstList;
+    }
+
+    public void addPosition(String var, int iStart, int iEnd)
+    {
+        positions.add(new Object[] {var, new Integer(iStart), new Integer(iEnd)});
+    }
+
+    public List<Object[]> getPositions()
+    {
+        return positions;
+    }
+
+    public static List<String> formatText(String input, int columns)
+    {
+        ArrayList<String> lines = new ArrayList<String>();
+
+        if ((input == null) || (input.length() == 0))
+            return lines;
+
+        int current = 0;
+        int lineStart = -1;
+        int lineEnd = -1;
+        int wordStart = -1;
+        int wordEnd = -1;
+        boolean start = true;
+        boolean preserve = true;
+
+        while (true)
+        {
+            if (current < input.length())
+            {
+                boolean newline = input.charAt(current) == '\n';
+                boolean printable = (preserve && !newline) || !Character.isWhitespace(input.charAt(current));
+
+                if (start) // find a word
+                {
+                    if (printable)
+                    {
+                        if (lineStart == -1)
+                        {
+                            lineStart = current;
+                        }
+                        wordStart = current;
+                        start = false;
+                    }
+                    else
+                    {
+                        if (newline && lineStart != -1)
+                        {
+                            lines.add(input.substring(lineStart, current));
+                            lineStart = -1;
+                        }
+                        else if (newline)
+                        {
+                            lines.add("");
+                        }
+                        ++current;
+                    }
+                }
+                else
+                // have a word
+                {
+                    preserve = false;
+                    if (printable)
+                    {
+                        ++current;
+                    }
+                    else
+                    {
+                        wordEnd = current;
+                        if (lineEnd == -1)
+                        {
+                            lineEnd = current;
+                        }
+
+                        // two possibilities; if the new word fits in the current line length
+                        // without being too many columns, leave on current line.
+                        // otherwise, set it as the start of a new line.
+
+                        if (wordEnd - lineStart < columns)
+                        {
+                            if (newline)
+                            {
+                                lines.add(input.substring(lineStart, current));
+                                lineStart = -1;
+                                lineEnd = -1;
+                                wordStart = -1;
+                                start = true;
+                                preserve = true;
+                                ++current;
+                            }
+                            else
+                            {
+                                // we have room to add the current word to this line, find new word
+                                start = true;
+                                lineEnd = current;
+                            }
+                        }
+                        else
+                        {
+                            // current word pushes things beyond the requested column limit,
+                            // dump current text
+                            lines.add(input.substring(lineStart, lineEnd));
+                            lineStart = wordStart;
+                            lineEnd = -1;
+                            wordStart = -1;
+                            start = true;
+                            if (newline)
+                                preserve = true;
+                        }
+                    }
+                }
+            }
+            else
+            // we're done
+            {
+                // a) no line yet, so don't do anything
+                // b) have line and new word would push over edge, need two lines
+                // c) have line and current word fits, need one line
+                // d) only one word and its too long anyway, need one line
+
+                if (lineStart != -1) // we have a line in progress
+                {
+                    wordEnd = current;
+                    if (lineEnd == -1)
+                        lineEnd = current;
+
+                    if (((wordEnd - lineStart) < columns) // current word fits
+                        || (wordEnd == lineEnd)) // or one long word
+                    {
+                        lineEnd = wordEnd;
+                        lines.add(input.substring(lineStart, wordEnd));
+                    }
+                    else
+                    // didn't fit, multiple words
+                    {
+                        lines.add(input.substring(lineStart, lineEnd));
+                        lines.add(input.substring(wordStart, wordEnd));
+                    }
+                }
+                break;
+            }
+        }
+        return lines;
+    }
+
+    /**
+     * For debugging only.
+     * <p>
+     * Produces an alphabetized list of this buffer's configuration options and their values.
+     * An option such as
+     * <pre>
+     * -foo=aaa,bbb -foo+=ccc
+     * </pre>
+     * will appear as
+     * <pre>
+     * foo=aaa,bbb;ccc
+     * </pre>
+     */
+    @Override
+    public String toString()
+    {
+        StringBuffer sb = new StringBuffer();
+        
+        String[] variables = varMap.keySet().toArray(new String[0]);
+        Arrays.sort(variables);
+        
+        for (String var : variables)
+        {
+            sb.append(var);
+            sb.append("=");
+
+            ArrayList<String> commaSeparatedValues = new ArrayList<String>();
+            for (ConfigurationValue cv : varMap.get(var))
+            {
+                List<String> args = cv.getArgs();
+                String joinedArgs = Joiner.on(',').join(args);
+                commaSeparatedValues.add(joinedArgs);
+            }
+            String rhs = Joiner.on(';').join(commaSeparatedValues);
+            sb.append(rhs);
+            
+            sb.append('\n');
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationInfo.java b/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationInfo.java
new file mode 100644
index 0000000..53836a7
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationInfo.java
@@ -0,0 +1,473 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.royale.formatter.config;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.apache.royale.compiler.internal.config.annotations.DefaultArgumentValue;
+import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
+
+/**
+ * Meta information for each configuration options. It is created by
+ * {@link ConfigurationBuffer#loadCache} from either annotations or
+ * {@code public static ConfigurationInfo getFooInfo();} methods in
+ * {@link Configuration} class.
+ */
+public class ConfigurationInfo
+{
+    public static final int NOT_SET = -2;
+    public static final int INFINITE_ARGS = -1;
+
+    /**
+     * This ctor is used when everything can be introspected off the setter
+     * method, or else when the names/types are provided by method overrides
+     * rather than ctor arguments
+     */
+    public ConfigurationInfo()
+    {
+        this.argcount = NOT_SET;
+        this.argnames = null;
+    }
+
+    /**
+     * Simple ctor for restricting the number of arguments.
+     * 
+     * @param argcount number of args, -1 for an infinite list
+     */
+    public ConfigurationInfo(int argcount)
+    {
+        this.argcount = argcount;
+        this.argnames = null;
+    }
+
+    /**
+     * Simple ctor for naming the arguments.
+     * 
+     * @param argnames list of argnames, argcount will default to # of elements
+     */
+    public ConfigurationInfo(String argnames[])
+    {
+        this.argcount = argnames.length;
+        this.argnames = argnames;
+    }
+
+    /**
+     * Use this ctor when you want to set a single list of some number of
+     * identically named args
+     * 
+     * @param argcount number of arguments (-1 for infinite)
+     * @param argname name of each argument
+     */
+    public ConfigurationInfo(int argcount, String argname)
+    {
+        this.argcount = argcount;
+        this.argnames = new String[] {argname};
+    }
+
+    /**
+     * More unusual ctor, this would let you have the first few args named one
+     * thing, the rest named something else. It is far more likely that you want
+     * a constrained list of names or else an arbitrary list of identical names.
+     * 
+     * @param argcount number of arguments
+     * @param argnames array of argument names
+     */
+    public ConfigurationInfo(int argcount, String argnames[])
+    {
+        this.argcount = argcount;
+        this.argnames = argnames;
+    }
+
+    public final int getArgCount()
+    {
+        return argcount;
+    }
+
+    protected int argcount = NOT_SET;
+
+    protected String[] defaultArgValues = null;
+
+    /**
+     * Get any default values for an argument
+     * 
+     * @return an array of default argument values.  May be null
+     */
+    public final String[] getDefaultArgValues()
+    {
+        return defaultArgValues;
+    }
+
+    private static String classToArgName(Class<?> c)
+    {
+        // we only support builtin classnames!
+
+        String className = c.getName();
+        if (className.startsWith("java.lang."))
+            className = className.substring("java.lang.".length());
+
+        return className.toLowerCase();
+    }
+
+    /**
+     * Return the name of each parameter. The default implementation is usually
+     * sufficient for simple cases, but one could do wacky things here like
+     * support an infinite list of alternating arg names.
+     * 
+     * @param argnum The argument number.
+     * @return name of argument
+     */
+    public String getArgName(int argnum)
+    {
+        if (argNameGeneratorClass != null)
+        {
+            Method getArgNameMethod;
+            try
+            {
+                getArgNameMethod = argNameGeneratorClass.getMethod("getArgumentName", int.class);
+                return (String)getArgNameMethod.invoke(null, argnum);
+            }
+            catch (Exception e)
+            {
+                // TODO: connect these exception to our problem logging subsystem.
+                e.printStackTrace();
+            }
+
+            return "";
+        }
+
+        if ((argnames == null) || (argnames.length == 0))
+        {
+            return classToArgName(getArgType(argnum));
+        }
+        else if (argnum >= argnames.length)
+        {
+            return argnames[argnames.length - 1];
+        }
+        else
+        {
+            return argnames[argnum];
+        }
+    }
+
+    /**
+     * Return the type of each parameter. This is computed based on your setter,
+     * and cannot be overridden
+     * 
+     * @param argnum The argument number.
+     */
+    public final Class<?> getArgType(int argnum)
+    {
+        if (argnum >= argtypes.length)
+        {
+            return argtypes[argtypes.length - 1];
+        }
+        else
+        {
+            return argtypes[argnum];
+        }
+    }
+
+    protected Class<?> argNameGeneratorClass;
+    protected String[] argnames;
+    protected Class<?>[] argtypes;
+
+    protected String[] prerequisites = null;
+
+    /**
+     * Return variable names that should be set before this one. The buffer is
+     * always set such that it tries to set all variables at a given level
+     * before setting child values, but you could override by using this. Its
+     * probably a bad idea to depend on children, though. It is unnecessary to
+     * set parent vars as prerequisites, since they are implicitly set first
+     */
+    public String[] getPrerequisites()
+    {
+        return prerequisites;
+    }
+
+    protected String[] softPrerequisites = null;
+
+    /**
+     * Prerequisites which should be set before this one if they exist
+     */
+    public String[] getSoftPrerequisites()
+    {
+        return softPrerequisites;
+    }
+
+    protected boolean allowMultiple = false;
+
+    /**
+     * Variables are generally only allowed to be set once in a given
+     * file/cmdline. It is sometimes useful to allow the same set multiple times
+     * in order to aggregate values.
+     * 
+     * @return true if the setter can be called multiple times
+     */
+    public boolean allowMultiple()
+    {
+        return allowMultiple;
+    }
+
+    protected String[] aliases = null;
+
+    /**
+     * Return an array of other names for this variable.
+     */
+    public String[] getAliases()
+    {
+        return aliases;
+    }
+
+    protected boolean isAdvanced = false;
+
+    /**
+     * Override to make a variable hidden by default (i.e. you need -advanced on
+     * the cmdline)
+     */
+    public boolean isAdvanced()
+    {
+        return isAdvanced;
+    }
+
+    protected boolean isHidden = false;
+
+    /**
+     * Override to make a variable completely hidden
+     */
+    public boolean isHidden()
+    {
+        return isHidden;
+    }
+
+    protected boolean isDisplayed = true;
+
+    /**
+     * Override to prevent printing when dumping configuration
+     */
+    public boolean isDisplayed()
+    {
+        return isDisplayed;
+    }
+
+    /**
+     * If a variable -must- be set, override this
+     */
+    public boolean isRequired()
+    {
+        return isRequired;
+    }
+
+    protected boolean isRequired = false;
+
+    /**
+     * Magic used by the command line configurator only at the moment to decide
+     * whether this variable should eat all subsequent arguments. Useful for
+     * -help...
+     */
+    public boolean isGreedy()
+    {
+        return isGreedy;
+    }
+
+    protected boolean isGreedy = false;
+
+    public boolean isPath()
+    {
+        return isPath;
+    }
+
+    protected boolean isPath = false;
+
+    public boolean doChecksum()
+    {
+        return true;
+    }
+
+    public String getDeprecatedMessage()
+    {
+        return deprecatedMessage;
+    }
+
+    protected String deprecatedMessage = null;
+
+    public boolean isDeprecated()
+    {
+        return isDeprecated;
+    }
+
+    protected boolean isDeprecated = false;
+
+    public String getDeprecatedReplacement()
+    {
+        return deprecatedReplacement;
+    }
+
+    protected String deprecatedReplacement;
+
+    public String getDeprecatedSince()
+    {
+        return deprecatedSince;
+    }
+
+    protected String deprecatedSince;
+    
+    /**
+     * @return True indicates that the option is no longer 
+     * supported and will not have any affect.
+     */
+    public boolean isRemoved() 
+    {
+        return isRemoved;
+    }
+
+    protected boolean isRemoved = false;
+    
+    /**
+     * @return True the option requires Flex in order to be useful. 
+     */
+    public boolean isRoyaleOnly() 
+    {
+        return isRoyaleOnly;
+    }
+
+    protected boolean isRoyaleOnly = false;
+    
+    protected final void setSetterMethod(Method setter)
+    {
+        Class<?>[] pt = setter.getParameterTypes();
+
+        assert (pt.length >= 2) : ("coding error: config setter must take at least 2 args!");
+
+        this.setter = setter;
+
+        if (pt.length == 2)
+        {
+            Class<?> c = pt[1];
+
+            if (ConfigurationBuffer.isSupportedListType(c))
+            {
+                if (argcount == NOT_SET)
+                    argcount = -1; // infinite list
+
+                argtypes = new Class[] {String.class};
+                return;
+            }
+            else if (ConfigurationBuffer.isSupportedValueType(c))
+            {
+                assert (argcount == NOT_SET) : ("coding error: value object setter cannot override argcount");
+                assert (argnames == null) : ("coding error: value object setter cannot override argnames");
+
+                Field[] fields = c.getFields();
+
+                argcount = fields.length;
+
+                assert (argcount > 0) : ("coding error: " + setter + " value object " + c.getName() + " must contain at least one public field");
+
+                argnames = new String[fields.length];
+                argtypes = new Class[fields.length];
+
+                for (int f = 0; f < fields.length; ++f)
+                {
+                    argnames[f] = ConfigurationBuffer.c2h(fields[f].getName());
+                    argtypes[f] = fields[f].getType();
+                }
+                return;
+            }
+        }
+
+        assert ((argcount == NOT_SET) || (argcount == pt.length - 1)) : ("coding error: the argument count must match the number of setter arguments");
+        // We've taken care of lists and value objects, from here on out, it must match the parameter list.
+
+        argcount = pt.length - 1;
+
+        DefaultArgumentValue defaultArgValuesAnno = setter.getAnnotation(DefaultArgumentValue.class);
+        if (defaultArgValuesAnno != null)
+            defaultArgValues = defaultArgValuesAnno.value();
+
+        argtypes = new Class[pt.length - 1];
+        for (int i = 1; i < pt.length; ++i)
+        {
+            assert (ConfigurationBuffer.isSupportedSimpleType(pt[i])) : ("coding error: " + setter.getClass().getName() + "." + setter.getName() + " parameter " + i + " is not a supported type!");
+            argtypes[i - 1] = pt[i];
+        }
+    }
+
+    protected final Method getSetterMethod()
+    {
+        return setter;
+    }
+
+    private Method setter;
+    private Method getter;
+
+    protected final void setGetterMethod(Method getter)
+    {
+        this.getter = getter;
+    }
+
+    protected final Method getGetterMethod()
+    {
+        return getter;
+    }
+
+    @Override
+    public String toString()
+    {
+        return MoreObjects.toStringHelper("")
+                .add("alias", arrayAsString(getAliases()))
+                .add("argcount", getArgCount())
+                .add("argnames", arrayAsString(argnames))
+                .add("argtypes", arrayAsString(argtypes))
+                .add("deprecated", isDeprecated())
+                .add("deprecatedMessage", getDeprecatedMessage())
+                .add("deprecatedReplacement", getDeprecatedReplacement())
+                .add("deprecatedSince", getDeprecatedSince())
+                .add("getter", getGetterMethod() == null ? "null" : getGetterMethod().getName())
+                .add("setter", getSetterMethod() == null ? "null" : getSetterMethod().getName())
+                .add("required", isRequired())
+                .add("Prerequisites", arrayAsString(getPrerequisites()))
+                .add("softPrerequisites", arrayAsString(getSoftPrerequisites()))
+                .add("advanced", isAdvanced())
+                .add("allow multiple", allowMultiple())
+                //.add("doChecksum", doChecksum())
+                .add("displayed", isDisplayed())
+                .add("greedy", isGreedy())
+                .add("hidden", isHidden())
+                .add("removed", isRemoved())
+                .add("path", isPath())
+                .toString();
+    }
+
+    private String arrayAsString(Object[] array)
+    {
+        if (array == null)
+            return "";
+        else
+            return "[" + Joiner.on(",").join(array) + "]";
+    }
+
+    /**
+     * True if only {@code compc} client can use this option.
+     */
+    public boolean isCompcOnly = false;
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationValue.java b/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationValue.java
new file mode 100644
index 0000000..a768560
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/ConfigurationValue.java
@@ -0,0 +1,109 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.royale.formatter.config;
+
+import java.util.List;
+import java.util.LinkedList;
+
+
+/**
+ * This class represents an instance of a configuration option.  For
+ * example, "-debug=true".
+ */
+public class ConfigurationValue
+{
+    protected ConfigurationValue( ConfigurationBuffer buffer, String var, List<String> args, String source, int line, String context )
+    {
+        this.buffer = buffer;
+        this.var = var;
+        this.args = new LinkedList<String>( args );
+        this.source = source;
+        this.line = line;
+        this.context = context;
+    }
+
+    /**
+     * getArgs
+     *
+     * @return list of values provided, in schema order
+     */
+    public final List<String> getArgs()
+    {
+        return args;
+    }
+
+    /**
+     * getBuffer
+     *
+     * @return a handle to the associated buffer holding this value
+     */
+    public final ConfigurationBuffer getBuffer()
+    {
+        return buffer;
+    }
+
+    /**
+     * getSource
+     *
+     * @return a string representing the origin of this value, or null if unknown
+     */
+    public final String getSource()
+    {
+        return source;
+    }
+
+    /**
+     * getLine
+     *
+     * @return the line number of the origin of this value, or -1 if unknown
+     */
+    public final int getLine()
+    {
+        return line;
+    }
+
+    /**
+     * getVar
+     *
+     * @return the full name of this configuration variable in the hierarchy
+     */
+    public final String getVar()
+    {
+        return var;
+    }
+
+    /**
+     * getContext
+     *
+     * @return the path of the enclosing context where the variable was set
+     * (i.e. the directory where the config file was found)
+     */
+    public final String getContext()
+    {
+        return context;
+    }
+
+    private final ConfigurationBuffer buffer;
+    private final String var;
+    private final List<String> args;
+    private final String source;
+    private final int line;
+    private final String context;
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/Configurator.java b/formatter/src/main/java/org/apache/royale/formatter/config/Configurator.java
new file mode 100644
index 0000000..c2abb48
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/Configurator.java
@@ -0,0 +1,683 @@
+/*
+ *
+ *  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.royale.formatter.config;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.royale.compiler.exceptions.ConfigurationException;
+import org.apache.royale.compiler.internal.config.localization.LocalizationManager;
+import org.apache.royale.compiler.internal.config.localization.ResourceBundleLocalizer;
+import org.apache.royale.compiler.problems.ConfigurationProblem;
+import org.apache.royale.compiler.problems.ICompilerProblem;
+import org.apache.royale.utils.FilenameNormalization;
+import org.apache.royale.utils.Trace;
+
+/**
+ * A class that allows a client change compiler settings and to 
+ * configure projects and targets from those settings.
+ */
+public class Configurator implements Cloneable
+{
+    /**
+     * Convert file path strings to {@code File} objects. Null values are
+     * discarded.
+     * 
+     * @param paths List of file 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;
+    }
+
+    /**
+     * Convert file path strings to {@code File} objects. Null values are
+     * discarded.
+     * 
+     * @param paths List of file paths
+     * @return Array of File objects. No null values will be returned.
+     */
+    public static List<File> toFileList(final List<String> paths)
+    {
+        final List<File> result = new ArrayList<File>();
+        for (final String path : paths)
+        {
+            if (path != null)
+                result.add(FilenameNormalization.normalize(new File(path)));
+        }
+        return result;
+    }
+
+    /**
+     * Convert {@code File} objects to {@code String}, where each {@code String} is 
+     * the absolute file path of the file. Null values are discarded.
+     * 
+     * @param files file specifications
+     * @return Array of File objects. No null values will be returned.
+     */
+    public static String[] toPaths(File[] files)
+    {
+        final List<String> result = new ArrayList<String>();
+        for (final File file : files)
+        {
+            if (file != null)
+                result.add(file.getAbsolutePath());
+        }
+        return result.toArray(new String[0]);
+    }
+
+    // Used to generate the command line
+    private static final String EQUALS_STRING = "=";
+    private static final String PLUS_EQUALS_STRING = "+=";
+    private static final String COMMA_STRING =  ",";
+    private static final String PLUS_STRING =  "+";
+    /**
+     * Constructor
+     */
+    public Configurator()
+    {
+        this(Configuration.class);
+    }
+    
+    /**
+     * Constructor
+     */
+    public Configurator(Class<? extends Configuration> configurationClass)
+    {
+        this.configurationClass = configurationClass;
+        args = new LinkedHashMap<String, Object>();
+        more = new LinkedHashMap<String, Object>();
+        tokens = new TreeMap<String, String>();
+        
+        isConfigurationDirty = true;
+        configurationDefaultVariable = IFormatterSettingsConstants.FILES; // the default variable of the configuration.
+        configurationProblems = new ArrayList<ICompilerProblem>();
+
+        // initialize the localization manager.
+        LocalizationManager.get().addLocalizer(new ResourceBundleLocalizer());
+    }
+
+    private ConfigurationBuffer cfgbuf;
+    protected Configuration configuration;
+    private Class<? extends Configuration> configurationClass;
+    
+    private Map<String, Object> args, more;
+    private String[] extras;
+    private String configurationDefaultVariable;
+    
+    private Map<String, String> tokens;
+    
+    private boolean isConfigurationDirty;
+    private boolean configurationSuccess;
+    protected Collection<ICompilerProblem> configurationProblems;
+
+    // 
+    // IConfigurator related methods
+    //
+
+    public Collection<ICompilerProblem> validateConfiguration(String[] args)
+    {
+        if (args == null)
+            throw new NullPointerException("args may not be null");
+        
+        List<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
+        ConfigurationBuffer configurationBuffer = createConfigurationBuffer(configurationClass);
+        
+        try
+        {
+            CommandLineConfigurator.parse(configurationBuffer, null, args);
+        }
+        catch (ConfigurationException e)
+        {
+            final ICompilerProblem problem = new ConfigurationProblem(e);
+            problems.add(problem);
+        }
+        
+        return problems;
+    }
+    
+    public Collection<ICompilerProblem> getConfigurationProblems()
+    {
+        assert configuration != null : 
+            "Get the configuration problems after calling getConfiguration()";
+        
+        List<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(configurationProblems.size() +
+                                            configuration.getConfigurationProblems().size());
+        problems.addAll(configurationProblems);
+        problems.addAll(configuration.getConfigurationProblems()); 
+        return problems;
+    }
+
+    public Configuration getConfiguration()
+    {
+        processConfiguration();
+        return configuration;
+    }
+    
+    public ConfigurationBuffer getConfigurationBuffer()
+    {
+        return cfgbuf;
+    }
+
+    /**
+     * Create a new configuration instance. The Configurator will need to 
+     * create a new configuration for each new configuration. For example,
+     * creating a new Configurator and getting the target settings will create
+     * a new configuration. If later on, the configuration is modified by calling
+     * any of the setter methods on the Configurator, then a new configuration 
+     * will be created the next time applyToProject() or getTargetSettings() is called.
+     * 
+     * The method may be overriden to allow for greater control when creating a 
+     * custom configuration that extends the built-in configuration.
+     * 
+     * @return a new configuration instance. If the custom configuration class
+     * cannot be created, the default configuration class will be created instead.
+     */
+    protected Configuration createConfiguration()
+    {
+        try
+        {
+            return configurationClass.newInstance();
+        }
+        catch (Exception e)
+        {
+            // If there is a problem initializing the configuration, then
+            // throw a ConfigurationException.
+            reportConfigurationException(new ConfigurationException.CouldNotInstantiate(configurationClass.getName()));
+
+            // Create the default configuration so we can report configuration
+            // problems.
+            try
+            {
+                return Configuration.class.newInstance();                
+            }
+            catch (Exception e2)
+            {
+                // this should never fail
+                assert(false);
+                return null;
+            }
+        }
+    }
+    
+    /**
+     * Initialize the configuration and the configuration buffer.
+     */
+    protected void initializeConfiguration()
+    {
+        // Create a clean configuration and configuration buffer
+        configuration = createConfiguration();
+        cfgbuf = createConfigurationBuffer(configuration.getClass());
+    }
+
+    /**
+     * Create a configuration buffer.
+     * @param configurationClass    The Configuration object
+     * @return          the configuration buffer to use
+     */
+    protected ConfigurationBuffer createConfigurationBuffer(
+            Class<? extends Configuration> configurationClass)
+    {
+        return new ConfigurationBuffer(
+                configurationClass, Configuration.getAliases());
+    }
+
+    /**
+     * Wrapper around the real processConfiguration.
+     * 
+     * @return true if success, false otherwise.
+     */
+    protected boolean processConfiguration()
+    {
+        boolean success = true;
+        
+        if (isConfigurationDirty)
+        {
+            configurationProblems.clear();
+            
+            try
+            {
+                success = processConfiguration(getOptions(args, more, processExtras(extras)));
+            }
+            catch (ConfigurationException e)
+            {
+                reportConfigurationException(e);
+                success = false;
+            }
+            catch (Exception e)
+            {
+            	e.printStackTrace();
+            }
+        }
+        else
+        {
+            success = configurationSuccess;
+        }
+        
+        isConfigurationDirty = false;
+        configurationSuccess = success;
+        return success;
+    }
+
+    /**
+     * Does all the work to set the command line arguments info the 
+     * configuration object.
+     *  
+     * @param argsArray - command line arguments
+     * 
+     * @return true if successful, false otherwise.
+     */
+    protected boolean  processConfiguration(String[] argsArray)
+    {
+        initializeConfiguration();
+        
+        boolean success = true;
+
+        try
+        {   
+            SystemPropertyConfigurator.load(cfgbuf, "royale");
+
+            // Parse the command line a first time, to peak at stuff like
+            // "royalelib" and "load-config".  The first parse is thrown
+            // away after that and we intentionally parse a second time
+            // below.  See note below.
+            CommandLineConfigurator.parse(cfgbuf, configurationDefaultVariable, argsArray);
+
+            overrideDefaults();
+
+            // Return if "-version" is present so the command line can print the 
+            // version.
+            if (cfgbuf.getVar("version") != null)
+                return false;
+
+            // Return so the command line can print help if "-help" is present.
+            final List<ConfigurationValue> helpVar = cfgbuf.getVar("help");
+            if (helpVar != null)
+                return false;
+            
+            // The command line needs to take precedence over all defaults and config files.
+            // By simply re-merging the command line back on top,
+            // we will get the behavior we want.
+            cfgbuf.clearSourceVars(CommandLineConfigurator.source);
+            CommandLineConfigurator.parse(cfgbuf, configurationDefaultVariable, argsArray);
+
+            // commit() reports problems instead of throwing an exception. This 
+            // allows us to process all the options in a configuration that
+            // are correct in the hopes that it will be enough to configure a
+            // project.
+            if (!cfgbuf.commit(configuration, configurationProblems))
+                success = false;
+            
+            configuration.validate(cfgbuf);
+        }
+        catch (ConfigurationException e)
+        {
+            reportConfigurationException(e);
+            success = false;
+        }
+        
+        return success;
+    }
+
+    /**
+     * Override default values.
+     */
+    protected void overrideDefaults() throws ConfigurationException
+    {
+    }
+
+    /**
+     * Convert conifguration exceptions to problems and collect them for 
+     * reporting.
+     * 
+     * @param e
+     */
+    protected void reportConfigurationException(ConfigurationException e)
+    {
+        final ICompilerProblem problem = new ConfigurationProblem(e);
+        configurationProblems.add(problem);
+    }
+    
+    //
+    // Configuration related methods
+    //
+    protected String[] getOptions(Map<String, Object> args, Map<String, Object> more, 
+		String[] extras)
+    {
+        ArrayList<String> buffer = new ArrayList<String>();
+        
+        for (Map.Entry<String, String> tokenEntry : tokens.entrySet())
+        {
+            buffer.add(PLUS_STRING + tokenEntry.getKey() + EQUALS_STRING + tokenEntry.getValue());
+        }
+        
+        for (Map.Entry<String, Object> arg : args.entrySet())
+        {
+            String key = arg.getKey();
+            Object value = arg.getValue();
+
+            if (value instanceof Boolean)
+            {
+                buffer.add(key + EQUALS_STRING + value);
+            }
+            else if (value instanceof Number)
+            {
+                buffer.add(key);
+                buffer.add(value.toString());
+            }
+            else if (value instanceof String)
+            {               
+                if (!"".equals(value))
+                {
+                    buffer.add(key);
+                    buffer.add((String)value);
+                }
+                else
+                {
+                    buffer.add(key + EQUALS_STRING);
+                }
+            }
+            else if (value instanceof File)
+            {
+                String p = ((File) value).getPath();
+                if (!"".equals(p))
+                {
+                    buffer.add(key);
+                    buffer.add(p);
+                }
+                else
+                {
+                    buffer.add(key + EQUALS_STRING);
+                }
+            }
+            else if (value instanceof java.util.Date)
+            {
+                buffer.add(key);
+                buffer.add(value.toString());
+            }
+            else if (value instanceof Map)
+            {
+                @SuppressWarnings("unchecked")
+                Map<String, ?> m = (Map<String, ?>) value;
+                for (Map.Entry<String, ?>entry : m.entrySet())
+                {
+                    String k = entry.getKey();
+                    Object v = entry.getValue();
+                    
+                    if (v instanceof String)
+                    {
+                        buffer.add(key);
+                        buffer.add(k);
+                        buffer.add((String)v);
+                    }
+                    else if (v instanceof File)
+                    {
+                        buffer.add(key);
+                        buffer.add(k);
+                        buffer.add(((File) v).getPath());
+                    }
+                    else if (v instanceof Collection)
+                    {
+                        buffer.add(key);
+                        buffer.add(k);
+                        Collection<?> list = (Collection<?>)v;
+                        for (Object next : list)
+                        {
+                            if (next != null)
+                                buffer.add(next.toString());
+                        }
+                    }
+                    else if (v != null)
+                    {
+                        assert false;
+                    }
+                }
+            }
+            else if (value instanceof int[])
+            {
+                int[] a = (int[]) value;
+                buffer.add(key);
+                buffer.add(String.valueOf(a[0]));
+                buffer.add(String.valueOf(a[1]));
+            }
+            else if (value instanceof Collection)
+            {
+                Collection<Object> list = new LinkedList<Object>((Collection<?>)args.get(key));
+                
+                int length = list.size();
+                if (length > 0)
+                {
+                    buffer.add(key);
+                }
+                for (Object obj : list)
+                {
+                    if (obj instanceof String)
+                    {
+                        buffer.add((String)obj);
+                    }
+                    else if (obj instanceof File)
+                    {
+                        buffer.add(((File)obj).getPath());
+                    }
+                }
+            }
+            else if (value != null)
+            {
+                assert false;
+            }
+            else
+            {
+                // System.err.println("unprocessed compiler options: " + key + EQUALS_STRING + value);
+            }
+        }
+        
+        for (Map.Entry<String, Object> moreEntry : more.entrySet())
+        {
+            String key = moreEntry.getKey();
+            Object value = moreEntry.getValue();
+
+            if (value instanceof Collection)
+            {
+                buffer.add(key + PLUS_EQUALS_STRING + toCommaSeparatedString((Collection<?>)value));
+            }
+            else if (value instanceof Map)
+            {
+                @SuppressWarnings("unchecked")
+                Map<String, ?> m = (Map<String, ?>) value;
+                for (Map.Entry<String, ?>entry : m.entrySet())
+                {
+                    String k = entry.getKey();
+                    Object v = entry.getValue();
+                    
+                    if (v instanceof Collection)
+                    {
+                        buffer.add(key + PLUS_EQUALS_STRING + k + COMMA_STRING +
+                                toCommaSeparatedString((Collection<?>)v));
+                    }
+                    else if (v != null)
+                    {
+                        assert false;
+                    }
+                }
+            }
+            else if (value != null)
+            {
+                assert false;
+            }
+            else
+            {
+                // System.err.println("unprocessed compiler options: " + key + EQUALS_STRING + value);
+            }
+        }
+
+        // Append extra command line args to the buffer.
+        if (extras != null && extras.length > 0)
+        {
+            for (int i = 0, length = extras == null ? 0 : extras.length; i < length; i++)
+            {
+                if (extras[i] != null)
+                {
+                    buffer.add(extras[i]);
+                }
+            }            
+        }
+        
+        String[] options = new String[buffer.size()];
+        buffer.toArray(options);
+    
+        if (Trace.config)
+            Trace.trace("Configurator: options = " + buffer.toString());
+        
+        return options;
+    }
+
+	protected String[] processExtras(String[] extraOptions) throws ConfigurationException
+    {
+		return extraOptions;
+	}
+
+    /**
+     * Sets the configuration parameters. The input should be valid <code>mxmlc/compc</code> command-line arguments.<p>
+     * 
+     * @param args <code>mxmlc/compc</code> command-line arguments
+     * @param defaultVariable the default variable of the configuration.
+     */
+    public void setConfiguration(String[] args, String defaultVariable)
+    {
+        extras = args;
+        configurationDefaultVariable = defaultVariable;
+        isConfigurationDirty = true;
+    }
+    
+    /**
+     * Defines a token. mxmlc and compc support token substitutions. For example,
+     * 
+     * <pre>
+     * mxmlc +royalelib=path1 +foo=bar --var=${foo}
+     * </pre>
+     * 
+     * <code>var=bar</code> after the substitution of <code>${foo}</code>.
+     * 
+     * @param name The name of the token.
+     * @param value The value of the token.
+     */
+    public void setToken(String name, String value)
+    {
+        tokens.put(name, value);
+        
+        isConfigurationDirty = true;
+    }
+
+    /**
+     * 
+     */
+    @Override
+    public String toString()
+    {
+        String[] options;
+        try
+        {
+            options = getOptions(args, more, processExtras(extras));
+        }
+        catch (ConfigurationException e)
+        {
+            options = new String[0];
+        }
+        
+        StringBuilder b = new StringBuilder();
+        for (int i = 0; i < options.length; i++)
+        {
+            b.append(options[i]);
+            b.append(' ');
+        }
+        return b.toString();
+    }
+    
+    private String toCommaSeparatedString(Collection<?> values)
+    {
+        StringBuilder b = new StringBuilder();
+        int length = values.size();
+        int i = 0;
+        for (Object value : values)
+        {
+            String valueString = null;
+            
+            if (value instanceof String)
+            {
+                valueString = (String)value;
+            }
+            else if (value instanceof File)
+            {
+                valueString = ((File)value).getPath();
+            }
+            
+            if (valueString != null)
+                b.append(valueString);
+            
+            if (i++ < length - 1)
+            {
+                b.append(COMMA_STRING);
+            }
+        }
+        return b.toString();
+    }
+    
+    @Override
+    public Configurator clone()
+    {
+        Configurator cloneConfig;
+        
+        try
+        {
+            cloneConfig = (Configurator) super.clone();
+        }
+        catch ( CloneNotSupportedException e )
+        {
+            throw new RuntimeException(e);//wont happen
+        }
+
+        cloneConfig.args = new LinkedHashMap<String, Object>(args);
+        cloneConfig.more = new LinkedHashMap<String, Object>(more);
+        cloneConfig.tokens = new LinkedHashMap<String, String>(tokens);
+        cloneConfig.configurationClass = configurationClass;
+        cloneConfig.isConfigurationDirty = true;
+        
+        if (extras != null)
+        {
+            cloneConfig.extras = new String[extras.length];
+            System.arraycopy(extras, 0, cloneConfig.extras, 0, extras.length);
+        }
+
+        return cloneConfig;
+    }
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/IFormatterSettingsConstants.java b/formatter/src/main/java/org/apache/royale/formatter/config/IFormatterSettingsConstants.java
new file mode 100644
index 0000000..6650a90
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/IFormatterSettingsConstants.java
@@ -0,0 +1,24 @@
+/*
+ *
+ *  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.royale.formatter.config;
+
+public interface IFormatterSettingsConstants {
+    static final String FILES                                       = "files";
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/config/SystemPropertyConfigurator.java b/formatter/src/main/java/org/apache/royale/formatter/config/SystemPropertyConfigurator.java
new file mode 100644
index 0000000..b74fae0
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/config/SystemPropertyConfigurator.java
@@ -0,0 +1,82 @@
+/*
+ *
+ *  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.royale.formatter.config;
+
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+
+import org.apache.royale.compiler.exceptions.ConfigurationException;
+
+/**
+ * A utility class, which is used to load configuration options via
+ * system properties and populate a ConfigurationBuffer.  A
+ * counterpart of CommandLineConfigurator and FileConfigurator.
+ */
+public class SystemPropertyConfigurator
+{
+    /**
+     * Opportunistically find some configuration settings in system properties.
+     * @param buffer the intermediate config buffer
+     * @param prefix an optional prefix to add to the variable, pass null if no prefix
+     */
+    public static void load( final ConfigurationBuffer buffer, String prefix ) throws ConfigurationException
+    {
+        try
+        {
+            Properties props = System.getProperties();
+
+            for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();)
+            {
+                String propname = (String) e.nextElement();
+
+                if (!propname.startsWith( prefix + "."))
+                {
+                    String value = System.getProperty( propname );
+                    buffer.setToken( propname, value );
+                    continue;
+                }
+
+                String varname = propname.substring( prefix.length() + 1 );
+
+                if (!buffer.isValidVar( varname ))
+                    continue;
+
+                String value = System.getProperty( propname );
+
+                List<String> args = new LinkedList<String>();
+                StringTokenizer t = new StringTokenizer( value, "," );
+
+                while (t.hasMoreTokens())
+                {
+                    String token = t.nextToken();
+                    args.add( token );
+                }
+                buffer.setVar( varname, args, "system properties", -1 );
+            }
+        }
+        catch (SecurityException se)
+        {
+            // just ignore, this is an optional for loading configuration   
+        }
+    }
+}

[royale-compiler] 02/05: formatter: fix bad logic for printing formatting text to stdout

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git

commit 661630a67fa1758333e52b0074effa5515c0b8e3
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Mon Sep 20 11:26:48 2021 -0700

    formatter: fix bad logic for printing formatting text to stdout
---
 formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
index cb33693..d010430 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
@@ -155,7 +155,7 @@ class FORMATTER {
 								FileUtils.write(inputFile, formattedText, "utf8");
 							}
 						}
-						if (!listChangedFiles || !writeBackToInputFiles) {
+						if (!listChangedFiles && !writeBackToInputFiles) {
 							System.out.println(formattedText);
 						}
 					}