force order of JFlex switch statements to help with reproducible binaries
diff --git a/compiler-build-tools/build.xml b/compiler-build-tools/build.xml
index b548f01..4ea1a61 100644
--- a/compiler-build-tools/build.xml
+++ b/compiler-build-tools/build.xml
@@ -81,6 +81,22 @@
         </javac>
 	</target>
 	
+    <!-- The OrderSwitches tool is used to order a switch statement from
+         JFlex for reproducible binaries  -->
+    <target name="order.switches"
+            description="Compiles the OrderSwitches build tool" >
+        <mkdir dir="${compiler-build-tools}/target/classes"/>
+        <javac debug="${javac.debug}" deprecation="${javac.deprecation}" destdir="${compiler-build-tools}/target/classes" includeAntRuntime="true"
+            source="${javac.src}" target="${javac.src}">
+            <compilerarg value="-Xlint:all,-path,-fallthrough"/>
+            <src path="${compiler-build-tools}/src/main/java"/>
+            <include name="org/apache/royale/compiler/tools/annotate/OrderSwitches.java"/>
+            <classpath>
+                <pathelement location="${compiler-build-tools}/target/classes"/>
+            </classpath>
+        </javac>
+    </target>
+    
 	<!-- The UnknownTreePatternInputOutput tool is used to compile an XML file containing unknown AST patterns to a Java class -->
     <target name="unknown.tree.pattern.input.output"
             description="Compiles the UnknownTreePatternInputOutput tool">
@@ -108,7 +124,7 @@
 	    </javac>
 	</target>
 
-    <target name="main" depends="annotate.class, unknown.tree.pattern.input.output" />
+    <target name="main" depends="annotate.class,order.switches, unknown.tree.pattern.input.output" />
     
     <!--
 
diff --git a/compiler-build-tools/src/main/java/org/apache/royale/compiler/tools/annotate/OrderSwitches.java b/compiler-build-tools/src/main/java/org/apache/royale/compiler/tools/annotate/OrderSwitches.java
new file mode 100644
index 0000000..f99c287
--- /dev/null
+++ b/compiler-build-tools/src/main/java/org/apache/royale/compiler/tools/annotate/OrderSwitches.java
@@ -0,0 +1,260 @@
+/*
+ * 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.compiler.tools.annotate;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+public class OrderSwitches
+{
+    public static void processFile(File file) throws AnnotateClass.AnnotateClassDeleteException, AnnotateClass.AnnotateClassRenameException {
+        if(!file.exists()) {
+            System.out.println("Missing file: " + file.getPath());
+            return;
+        }
+        try
+        {
+            // Prepare to read the file.
+            FileInputStream fileInputStream = new FileInputStream(file);
+            InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
+            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+
+            File tmpOutputFile = new File(file.getParentFile(), file.getName() + ".tmp");
+            FileOutputStream fileOutputStream = new FileOutputStream(tmpOutputFile);
+            PrintStream outputStream = new PrintStream(fileOutputStream);
+            try
+            {
+                // Read it line-by-line.
+                String line;
+                ArrayList<String> lines = new ArrayList<String>();
+                boolean inSwitch = false;
+                while ((line = bufferedReader.readLine()) != null)
+                {
+                    if (line.contains("The following code was generated by JFlex "))
+                    {
+                    	int c = line.indexOf(" on ");
+                    	if (c != -1)
+                    		line = line.substring(0, c) + " */";
+                    }
+                    if (line.contains("* on ") && line.contains("from the specification file"))
+                    	line = " * from the specification file";
+                    if (line.contains("<tt>") && line.contains("compiler/src/main/jflex"))
+                    {
+                    	int t = line.indexOf("<tt>");
+                    	int rc = line.indexOf("compiler/src/main/jflex");
+                    	line = line.substring(0, t + 4) + line.substring(rc);
+                    }
+                    // If the line starts with "switch", and the cases are numbers, make sure they are in order.
+                    if (line.contains("      switch (zzAction")) {
+                    	inSwitch = true;
+                        System.out.println("Ordering Switch in file: " + file.getPath());
+                    }
+                    if (inSwitch)
+                    {
+                    	lines.add(line);
+                        if (line.startsWith("      }")) {
+                        	orderSwitch(lines);
+                        	for (String orderedLine : lines)
+                        	{
+                        		outputStream.println(orderedLine);
+                        	}
+                        	inSwitch = false;
+                        }
+                    }
+                    else
+                    	outputStream.println(line);
+                }
+            }
+            finally
+            {
+                try {
+                    bufferedReader.close();
+                } catch(Exception e) {
+                    // Ignore.
+                }
+                try {
+                    outputStream.close();
+                } catch(Exception e) {
+                    // Ignore.
+                }
+                try {
+                    fileOutputStream.close();
+                } catch(Exception e) {
+                    // Ignore.
+                }
+                try {
+                    inputStreamReader.close();
+                } catch(Exception e) {
+                    // Ignore.
+                }
+                try {
+                    fileInputStream.close();
+                } catch(Exception e) {
+                    // Ignore.
+                }
+            }
+
+            // Remove the original file.
+            if(!file.delete()) {
+                // wait a bit then retry on Windows
+                if (file.exists())
+                {
+                    for (int i = 0; i < 6; i++)
+                    {
+                        Thread.sleep(500);
+                        System.gc();
+                        if (file.delete())
+                           break;
+                    }
+                    if (file.exists())
+                        throw new AnnotateClass.AnnotateClassDeleteException("Error deleting original file at: " + file.getPath());
+                }
+            }
+
+            // Rename the temp file to the name of the original file.
+            if(!tmpOutputFile.renameTo(file)) {
+                throw new AnnotateClass.AnnotateClassRenameException("Error renaming the temp file from: " + tmpOutputFile.getPath() +
+                        " to: " + file.getPath());
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+    
+    private static void orderSwitch(ArrayList<String> lines) {
+    	HashMap<String,List<String>> switchMap = new HashMap<String,List<String>>();
+    	ArrayList<String> defaultCase = new ArrayList<String>();
+    	ArrayList<String> breaks = new ArrayList<String>();
+    	String line0 = lines.get(0); // first line should be switch statement
+    	boolean inDefault = false;
+    	int n = lines.size();
+    	for (int i = 1; i < n; i++)
+    	{
+    		String line = lines.get(i);
+    		int c = line.indexOf(" case ");
+    		if (!inDefault && (c != -1))
+    		{
+    			String cayse = line.substring(c);
+    			if (line.contains("break;"))
+    			{
+    				// some cases just contain a break statement.
+    				// the are used to make sure the prior case
+    				// ends with a break without having to check
+    				// if the break statement would be reachable
+    				// due to return statements in the case.
+    				breaks.add(line);
+    				continue;
+    			}
+    			else
+    			{
+    				ArrayList<String> caseLines = new ArrayList<String>();
+    				for (int j = i + 1; j < n; j++)
+    				{
+    					line = lines.get(j);
+    					if (line.contains(" case "))
+    					{
+    						switchMap.put(cayse, caseLines);
+    						i = j - 1;
+    						break;
+    					}
+    					else if (line.contains("default:"))
+    					{
+    						switchMap.put(cayse, caseLines);
+    						inDefault = true;
+    						i = j;
+    						// assumes no break at end of default case
+    						defaultCase.add(line);
+    						break;    						
+    					}
+    					caseLines.add(line);
+    				}
+    			}
+    		}
+			else if (line.contains("default:"))
+			{
+				inDefault = true;
+				// assumes no break at end of default case
+				defaultCase.add(line);
+			}
+    		else if (inDefault)
+    			defaultCase.add(line);
+    	}
+    	Set<String> keys = switchMap.keySet();
+    	ArrayList<String> keyList = new ArrayList<String>();
+    	keyList.addAll(keys);
+    	Collections.sort(keyList);
+    	lines.clear();
+    	lines.add(line0);
+    	int breakIndex = 0;
+    	for (String key : keyList)
+    	{
+    		lines.add("       " + key);
+    		List<String> caseLines = switchMap.get(key);
+    		lines.addAll(caseLines);
+    		lines.add(breaks.get(breakIndex++));    		
+    	}
+    	boolean inSwitch = false;
+    	boolean sawDefault = false;
+    	ArrayList<String> switchLines = new ArrayList<String>();
+    	for (String defaultLine : defaultCase)
+    	{
+            // If the line starts with "switch", and the cases are numbers, make sure they are in order.
+            if (defaultLine.contains("switch (zzLexicalState)")) {
+            	inSwitch = true;
+                System.out.println("Ordering Switch in default: ");
+            }
+            if (inSwitch)
+            {
+            	switchLines.add(defaultLine);
+            	if (defaultLine.contains("default:"))
+            		sawDefault = true;
+                if (sawDefault && defaultLine.startsWith("            }")) {
+                	orderSwitch(switchLines);
+                	for (String orderedLine : switchLines)
+                	{
+                		lines.add(orderedLine);
+                	}
+                	inSwitch = false;
+                }
+            }
+            else
+            	lines.add(defaultLine);
+
+    	}
+    	
+    }
+    
+    public static void main(String[] args)
+    {
+        File f = new File(args[0]);
+        try {
+            processFile(f);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        
+    }
+}
diff --git a/compiler/build.xml b/compiler/build.xml
index bca3b89..3348d3e 100644
--- a/compiler/build.xml
+++ b/compiler/build.xml
@@ -202,6 +202,25 @@
     </macrodef>
 
     <!--
+        Defines an <order.switches file="..."> macro
+        used for ordering the switch statement generated by
+        JFlex in order to get reproducible builds.
+    -->
+    <macrodef name="order.switches">
+        <attribute name="file"/>
+        <sequential>
+            <java classname="org.apache.royale.compiler.tools.annotate.OrderSwitches" fork="false">
+                <classpath>
+                    <path refid="classpath"/>
+                    <pathelement location="${compiler}/../compiler-build-tools/target/classes"/>
+                    <pathelement location="${compiler}/target/classes"/>
+                </classpath>
+                <arg value="@{file}"/>
+            </java>
+        </sequential>
+    </macrodef>
+
+    <!--
 
         SETUP
 
@@ -237,6 +256,7 @@
         <jflex input="${compiler}/src/main/jflex/org/apache/royale/compiler/internal/parsing/as/RawASTokenizer.lex"
                skeleton="${compiler}/src/main/jflex/org/apache/royale/compiler/internal/parsing/as/skeleton.royale"
                output="${compiler}/target/generated-sources/jflex/org/apache/royale/compiler/internal/parsing/as"/>
+        <order.switches file="${compiler}/target/generated-sources/jflex/org/apache/royale/compiler/internal/parsing/as/RawASTokenizer.java" />
 	</target>
 	
     <target name="set.raw.asdoc.tokenizer.uptodate">
@@ -254,6 +274,7 @@
         <echo message="Generating RawASDocTokenizer"/>
         <jflex input="${compiler}/src/main/jflex/org/apache/royale/compiler/internal/parsing/as/RawASDocTokenizer.lex"
                output="${compiler}/target/generated-sources/jflex/org/apache/royale/compiler/internal/parsing/as"/>
+        <order.switches file="${compiler}/target/generated-sources/jflex/org/apache/royale/compiler/internal/parsing/as/RawASDocTokenizer.java" />
     </target>
 
     <target name="set.raw.mxml.tokenizer.uptodate">
@@ -271,6 +292,7 @@
         <echo message="Generating RawMXMLTokenizer"/>
         <jflex input="${compiler}/src/main/jflex/org/apache/royale/compiler/internal/parsing/mxml/RawMXMLTokenizer.lex"
                output="${compiler}/target/generated-sources/jflex/org/apache/royale/compiler/internal/parsing/mxml"/>
+        <order.switches file="${compiler}/target/generated-sources/jflex/org/apache/royale/compiler/internal/parsing/mxml/RawMXMLTokenizer.java" />
     </target>
 
 	<target name="jflex" depends="raw.as.tokenizer, raw.asdoc.tokenizer, raw.mxml.tokenizer"