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 2022/07/28 23:08:12 UTC

[royale-compiler] branch develop updated: formatter: refactor .as and .mxml formatters into separate classes for easier maintenance

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


The following commit(s) were added to refs/heads/develop by this push:
     new 71626012b formatter: refactor .as and .mxml formatters into separate classes for easier maintenance
71626012b is described below

commit 71626012bc3b6ba8dcc3d83f50333f02ea8b72f8
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Thu Jul 28 16:07:56 2022 -0700

    formatter: refactor .as and .mxml formatters into separate classes for easier maintenance
---
 .../org/apache/royale/formatter/FORMATTER.java     | 1766 +-------------------
 .../ASTokenFormatter.java}                         | 1358 ++++-----------
 .../formatter/internal/BaseTokenFormatter.java     |   81 +
 .../formatter/internal/MXMLTokenFormatter.java     |  506 ++++++
 4 files changed, 879 insertions(+), 2832 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 a0f71dcb2..469b08b61 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
@@ -21,16 +21,12 @@ package org.apache.royale.formatter;
 
 import java.io.File;
 import java.io.IOException;
-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 java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
@@ -43,23 +39,7 @@ import org.apache.royale.compiler.common.VersionInfo;
 import org.apache.royale.compiler.exceptions.ConfigurationException;
 import org.apache.royale.compiler.filespecs.FileSpecification;
 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;
-import org.apache.royale.compiler.internal.parsing.as.IncludeHandler;
-import org.apache.royale.compiler.internal.parsing.as.MetaDataPayloadToken;
-import org.apache.royale.compiler.internal.parsing.as.MetadataToken;
-import org.apache.royale.compiler.internal.parsing.as.MetadataTokenTypes;
-import org.apache.royale.compiler.internal.parsing.as.RepairingTokenBuffer;
-import org.apache.royale.compiler.internal.parsing.as.StreamingASTokenizer;
-import org.apache.royale.compiler.internal.parsing.mxml.MXMLToken;
-import org.apache.royale.compiler.internal.parsing.mxml.MXMLTokenizer;
-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.parsing.IMXMLToken;
-import org.apache.royale.compiler.parsing.MXMLTokenTypes;
-import org.apache.royale.compiler.problems.CompilerProblemSeverity;
 import org.apache.royale.compiler.problems.ConfigurationProblem;
 import org.apache.royale.compiler.problems.ICompilerProblem;
 import org.apache.royale.compiler.problems.UnexpectedExceptionProblem;
@@ -69,20 +49,17 @@ 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.formatter.internal.ASTokenFormatter;
+import org.apache.royale.formatter.internal.MXMLTokenFormatter;
 import org.apache.royale.utils.FilenameNormalization;
 
 /**
- * Formats .as source files.
+ * Formats .as and .mxml source files.
  */
 public 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";
-	private static final Pattern ASDOC_START_LINE_PATTERN = Pattern.compile("^\\*(\\s*)");
-	private static final String FORMATTER_TAG_OFF = "@formatter:off";
-	private static final String FORMATTER_TAG_ON = "@formatter:on";
 
 	static enum ExitCode {
 		SUCCESS(0), PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), FAILED_WITH_CONFIG_PROBLEMS(4);
@@ -222,9 +199,11 @@ public class FORMATTER {
 		filePath = FilenameNormalization.normalize(filePath);
 		String result = null;
 		if (filePath.endsWith(".mxml")) {
-			result = formatMXMLTextInternal(filePath, text, problems);
+			MXMLTokenFormatter mxmlFormatter = new MXMLTokenFormatter(this);
+			result = mxmlFormatter.format(filePath, text, problems);
 		} else {
-			result = formatAS3TextInternal(filePath, text, problems);
+			ASTokenFormatter asFormatter = new ASTokenFormatter(this);
+			result = asFormatter.format(filePath, text, problems);
 		}
 		if (insertFinalNewLine && result.charAt(result.length() - 1) != '\n') {
 			return result + '\n';
@@ -238,7 +217,8 @@ public class FORMATTER {
 
 	public String formatActionScriptText(String text, Collection<ICompilerProblem> problems) {
 		String filePath = FilenameNormalization.normalize("stdin.as");
-		return formatAS3TextInternal(filePath, text, problems);
+		ASTokenFormatter asFormatter = new ASTokenFormatter(this);
+		return asFormatter.format(filePath, text, problems);
 	}
 
 	public String formatActionScriptText(String text) {
@@ -247,7 +227,8 @@ public class FORMATTER {
 
 	public String formatMXMLText(String text, Collection<ICompilerProblem> problems) {
 		String filePath = FilenameNormalization.normalize("stdin.mxml");
-		return formatMXMLTextInternal(filePath, text, problems);
+		MXMLTokenFormatter mxmlFormatter = new MXMLTokenFormatter(this);
+		return mxmlFormatter.format(filePath, text, problems);
 	}
 
 	public String formatMXMLText(String text) {
@@ -396,1729 +377,4 @@ public class FORMATTER {
 			}
 		}
 	}
-
-	private String formatMXMLScriptElement(String filePath, int line, String text,
-			Collection<ICompilerProblem> problems) {
-		String indent = "\t";
-		if (insertSpaces) {
-			indent = "";
-			for (int i = 0; i < tabSize; i++) {
-				indent += " ";
-			}
-		}
-		StringBuilder builder = new StringBuilder();
-		Pattern scriptPattern = Pattern.compile(
-				"^<((?:mx|fx):(\\w+))>\\s*(<!\\[CDATA\\[)?((?:.|(?:\\r?\\n))*?)(?:\\]\\]>)?\\s*<\\/(?:mx|fx):(?:\\w+)>$");
-		Matcher scriptMatcher = scriptPattern.matcher(text);
-		if (!scriptMatcher.matches()) {
-			return text;
-		}
-		if (problems == null) {
-			// we need to know if there were problems because it means that we
-			// need to return the original, unformatted text
-			problems = new ArrayList<ICompilerProblem>();
-		}
-		String scriptTagText = scriptMatcher.group(1);
-		String scriptTagName = scriptMatcher.group(2);
-		String cdataText = scriptMatcher.group(3);
-		String scriptText = scriptMatcher.group(4);
-		boolean requireCdata = cdataText != null || "Script".equals(scriptTagName);
-		String formattedScriptText = formatAS3TextInternal(filePath + "@Script[" + line + "]", scriptText, problems);
-		if (!ignoreProblems && hasErrors(problems)) {
-			return text;
-		}
-		if (formattedScriptText.length() > 0) {
-			String[] formattedLines = formattedScriptText.split("\n");
-			String lineIndent = requireCdata ? (indent + indent + indent) : (indent + indent);
-			for (int i = 0; i < formattedLines.length; i++) {
-				formattedLines[i] = lineIndent + formattedLines[i];
-			}
-			formattedScriptText = String.join("\n", formattedLines);
-		}
-		builder.append(indent);
-		builder.append("<");
-		builder.append(scriptTagText);
-		builder.append(">\n");
-		if (requireCdata) {
-			builder.append(indent);
-			builder.append(indent);
-			builder.append("<![CDATA[\n");
-		}
-		if (formattedScriptText.length() > 0) {
-			builder.append(formattedScriptText);
-			builder.append("\n");
-		}
-		if (requireCdata) {
-			builder.append(indent);
-			builder.append(indent);
-			builder.append("]]>\n");
-		}
-		builder.append(indent);
-		builder.append("</");
-		builder.append(scriptTagText);
-		builder.append(">");
-
-		return builder.toString();
-	}
-
-	private String formatAS3TextInternal(String filePath, String text, Collection<ICompilerProblem> problems) {
-		if (problems == null) {
-			problems = new ArrayList<ICompilerProblem>();
-		}
-
-		StringReader textReader = new StringReader(text);
-		StreamingASTokenizer tokenizer = null;
-		ASToken[] streamingTokens = null;
-		try {
-			tokenizer = StreamingASTokenizer.createForRepairingASTokenizer(textReader, filePath,
-					IncludeHandler.creatDefaultIncludeHandler());
-			tokenizer.setCollectComments(true);
-			tokenizer.setFollowIncludes(false);
-			streamingTokens = tokenizer.getTokens(textReader);
-		} finally {
-			IOUtils.closeQuietly(textReader);
-			IOUtils.closeQuietly(tokenizer);
-		}
-
-		if (tokenizer.hasTokenizationProblems()) {
-			problems.addAll(tokenizer.getTokenizationProblems());
-		}
-
-		if (!ignoreProblems && hasErrors(problems)) {
-			return text;
-		}
-
-		// temporarily remove the comments from the token list because ASParser
-		// doesn't know how to deal with them properly.
-		// we'll add them back at the same locations after the parser is done.
-		List<ASToken> comments = new ArrayList<ASToken>();
-		List<ASToken> streamingTokensList = new ArrayList<ASToken>();
-		for (ASToken token : streamingTokens) {
-			if (token.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-					|| token.getType() == ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT) {
-				comments.add(token);
-			} else {
-				streamingTokensList.add(token);
-			}
-		}
-
-		Workspace workspace = new Workspace();
-		RepairingTokenBuffer buffer = new RepairingTokenBuffer(streamingTokensList.toArray(new ASToken[0]));
-		ASParser parser = new ASParser(workspace, buffer);
-		FileNode node = new FileNode(workspace);
-		try {
-			parser.file(node);
-		} catch (Exception e) {
-			parser = null;
-			problems.add(new UnexpectedExceptionProblem(e));
-			return text;
-		}
-
-		if (tokenizer.hasTokenizationProblems()) {
-			problems.addAll(tokenizer.getTokenizationProblems());
-		}
-
-		if (parser.getSyntaxProblems().size() > 0) {
-			problems.addAll(parser.getSyntaxProblems());
-		}
-
-		if (!ignoreProblems && hasErrors(problems)) {
-			return text;
-		}
-
-		List<IASToken> repairedTokensList = new ArrayList<IASToken>(Arrays.asList(buffer.getTokens(true)));
-		// restore the comments that were removed before parsing
-		IASToken nextComment = null;
-		for (int i = 0; i < repairedTokensList.size(); i++) {
-			if (nextComment == null) {
-				if (comments.size() == 0) {
-					// no more comments to add
-					break;
-				}
-				nextComment = comments.get(0);
-			}
-			IASToken currentToken = repairedTokensList.get(i);
-			if (nextComment.getAbsoluteStart() <= currentToken.getAbsoluteStart()) {
-				repairedTokensList.add(i, nextComment);
-				nextComment = null;
-				comments.remove(0);
-			}
-		}
-		// there may be some comments left that didn't appear before any
-		// of the repaired tokens, so add them all at the end
-		repairedTokensList.addAll(comments);
-
-		List<IASToken> tokens = insertExtraAS3Tokens(repairedTokensList, text);
-		try {
-			return parseTokens(filePath, tokens, node);
-		} catch (Exception e) {
-			if (problems != null) {
-				System.err.println(e);
-				e.printStackTrace(System.err);
-				problems.add(new UnexpectedExceptionProblem(e));
-			}
-			return text;
-		}
-
-	}
-
-	private List<IASToken> insertExtraAS3Tokens(List<IASToken> originalTokens, String text) {
-		ArrayList<IASToken> tokens = new ArrayList<IASToken>();
-		IASToken prevToken = null;
-		for (IASToken token : originalTokens) {
-			if (prevToken != null) {
-
-				boolean skipSemicolon = token.getType() == ASTokenTypes.TOKEN_SEMICOLON && token.isImplicit()
-						&& prevToken != null && (prevToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-								|| prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN);
-				if (skipSemicolon) {
-					continue;
-				}
-
-				int start = prevToken.getAbsoluteEnd();
-				int end = token.getAbsoluteStart();
-				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(token.getLine());
-					extraToken.setEndLine(token.getColumn());
-					tokens.add(extraToken);
-				}
-			}
-			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);
-			}
-		}
-		return tokens;
-	}
-
-	private List<IMXMLToken> insertExtraMXMLTokens(IMXMLToken[] originalTokens, String text) {
-		ArrayList<IMXMLToken> tokens = new ArrayList<IMXMLToken>();
-		IMXMLToken prevToken = null;
-		for (IMXMLToken token : originalTokens) {
-			if (prevToken != null) {
-				int start = prevToken.getEnd();
-				int end = token.getStart();
-				if (end > start) {
-					String tokenText = text.substring(start, end);
-					MXMLToken extraToken = new MXMLToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getLine(),
-							prevToken.getColumn() + end - start, tokenText);
-					extraToken.setEndLine(token.getLine());
-					extraToken.setEndLine(token.getColumn());
-					tokens.add(extraToken);
-				}
-			}
-			tokens.add(token);
-			prevToken = token;
-		}
-		if (prevToken != null) {
-			int start = prevToken.getEnd();
-			int end = text.length();
-			if (end > start) {
-				String tokenText = text.substring(start, end);
-				MXMLToken extraToken = new MXMLToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getLine(),
-						prevToken.getColumn() + end - start, tokenText);
-				extraToken.setEndLine(prevToken.getLine());
-				extraToken.setEndLine(prevToken.getColumn());
-				tokens.add(extraToken);
-			}
-		}
-		return tokens;
-	}
-
-	private IASToken getNextTokenSkipExtra(List<IASToken> tokens, int startIndex) {
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IASToken token = tokens.get(i);
-			if (token.getType() != TOKEN_TYPE_EXTRA) {
-				return token;
-			}
-		}
-		return null;
-	}
-
-	private IASToken getNextTokenSkipExtraAndComments(List<IASToken> tokens, int startIndex) {
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IASToken token = tokens.get(i);
-			if (token.getType() != TOKEN_TYPE_EXTRA && token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
-				return token;
-			}
-		}
-		return null;
-	}
-
-	private String parseTokens(String filePath, List<IASToken> tokens, FileNode node) throws Exception {
-		int indent = 0;
-		boolean inCaseOrDefaultClause = false;
-		boolean inControlFlowStatement = false;
-		boolean inVarOrConstDeclaration = false;
-		boolean inFunctionDeclaration = false;
-		boolean inPackageDeclaration = false;
-		boolean inClassDeclaration = false;
-		boolean inInterfaceDeclaration = false;
-		boolean blockOpenPending = false;
-		boolean indentedStatement = false;
-		boolean caseOrDefaultBlockOpenPending = false;
-		boolean skipFormatting = false;
-		int varOrConstChainLevel = -1;
-		List<BlockStackItem> blockStack = new ArrayList<BlockStackItem>();
-		int controlFlowParenStack = 0;
-		int ternaryStack = 0;
-		int numRequiredNewLines = 0;
-		boolean requiredSpace = false;
-		IASToken prevTokenNotComment = null;
-		IASToken prevToken = null;
-		IASToken prevTokenOrExtra = null;
-		IASToken token = null;
-		IASToken nextToken = null;
-		IASToken nextTokenOrExtra = null;
-		IASToken nextTokenNotComment = null;
-		StringBuilder builder = new StringBuilder();
-		for (int i = 0; i < tokens.size(); i++) {
-			token = tokens.get(i);
-			if (token.getType() == TOKEN_TYPE_EXTRA) {
-				if (skipFormatting) {
-					builder.append(token.getText());
-				} else {
-					if (i == (tokens.size() - 1)) {
-						// if the last token is whitespace, include at most one
-						// new line, but strip the rest
-						numRequiredNewLines = Math.min(1, Math.max(0, countNewLinesInExtra(token)));
-						appendNewLines(builder, numRequiredNewLines);
-						break;
-					}
-					if (!blockOpenPending) {
-						int newLinesInExtra = countNewLinesInExtra(token);
-						if (prevToken != null && prevToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT) {
-							newLinesInExtra++;
-						}
-						boolean oneLineBlock = prevToken != null && prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
-								&& nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-						if (oneLineBlock && collapseEmptyBlocks) {
-							newLinesInExtra = 0;
-						}
-						numRequiredNewLines = Math.max(numRequiredNewLines, newLinesInExtra);
-						if (!indentedStatement && numRequiredNewLines > 0 && prevTokenNotComment != null
-								&& prevTokenNotComment.getType() != ASTokenTypes.TOKEN_SEMICOLON
-								&& prevTokenNotComment.getType() != ASTokenTypes.TOKEN_BLOCK_CLOSE
-								&& !(caseOrDefaultBlockOpenPending
-										&& prevTokenNotComment.getType() == ASTokenTypes.TOKEN_COLON)
-								&& !(prevTokenNotComment instanceof MetaDataPayloadToken)) {
-							boolean needsIndent = prevTokenNotComment.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
-									|| (!blockStack.isEmpty() && blockStack
-											.get(blockStack.size() - 1) instanceof ObjectLiteralBlockStackItem);
-							if (needsIndent) {
-								indentedStatement = true;
-								indent = increaseIndent(indent);
-							}
-						}
-					}
-				}
-				prevTokenOrExtra = token;
-				continue;
-			}
-			nextTokenOrExtra = ((i + 1) < tokens.size()) ? tokens.get(i + 1) : null;
-			nextToken = getNextTokenSkipExtra(tokens, i + 1);
-			nextTokenNotComment = getNextTokenSkipExtraAndComments(tokens, i + 1);
-
-			boolean skipWhitespaceBeforeSemicolon = nextToken == null
-					|| nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON;
-
-			// characters that must appear before the token
-			if (token instanceof MetaDataPayloadToken) {
-				numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-			} else {
-				switch (token.getType()) {
-					case ASTokenTypes.TOKEN_BLOCK_OPEN: {
-						if (!blockOpenPending) {
-							// detect some cases of blocks that may have been
-							// missed earlier (these will be standalone blocks,
-							// without a control flow statement or definition).
-							// this should not detect object literals, though!
-							blockOpenPending = prevTokenNotComment == null
-									|| prevTokenNotComment.getType() == ASTokenTypes.TOKEN_SEMICOLON
-									|| prevTokenNotComment.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
-									|| prevTokenNotComment.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-							if (!blockOpenPending && prevTokenNotComment.getType() == ASTokenTypes.TOKEN_COLON
-									&& !blockStack.isEmpty()) {
-								IASToken blockToken = blockStack.get(blockStack.size() - 1).token;
-								blockOpenPending = blockToken.getType() == ASTokenTypes.TOKEN_KEYWORD_DEFAULT
-										|| blockToken.getType() == ASTokenTypes.TOKEN_KEYWORD_CASE;
-							}
-							if (blockOpenPending) {
-								blockStack.add(new BlockStackItem(token));
-							}
-						}
-						if (blockOpenPending) {
-							if (indentedStatement) {
-								indentedStatement = false;
-								indent = decreaseIndent(indent);
-							}
-							boolean oneLineBlock = nextToken != null
-									&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-							boolean needsNewLine = placeOpenBraceOnNewLine && (!collapseEmptyBlocks || !oneLineBlock);
-							if (needsNewLine) {
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-							} else {
-								if (oneLineBlock && collapseEmptyBlocks) {
-									numRequiredNewLines = 0;
-								}
-								requiredSpace = true;
-							}
-						} else {
-							// probably an object literal
-							blockStack.add(new ObjectLiteralBlockStackItem(token));
-							indent = increaseIndent(indent);
-						}
-						requiredSpace = true;
-						break;
-					}
-					case ASTokenTypes.TOKEN_BLOCK_CLOSE: {
-						boolean skipSwitchDecrease = false;
-						if (!blockStack.isEmpty()) {
-							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
-							if (stackItem.blockDepth <= 1) {
-								boolean oneLineBlock = prevToken != null
-										&& prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN;
-								if (!collapseEmptyBlocks || !oneLineBlock) {
-									indent = decreaseIndent(indent);
-								}
-								if (stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_CASE
-										|| stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_DEFAULT) {
-									blockStack.remove(blockStack.size() - 1);
-									indent = decreaseIndent(indent);
-									skipSwitchDecrease = true;
-								}
-							}
-						}
-						if (!skipSwitchDecrease && !blockStack.isEmpty()) {
-							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
-							if (stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_SWITCH) {
-								SwitchBlockStackItem switchStackItem = (SwitchBlockStackItem) stackItem;
-								if (switchStackItem.clauseCount > 0
-										&& (prevToken == null
-												|| prevToken.getType() != ASTokenTypes.TOKEN_BLOCK_CLOSE)) {
-									indent = decreaseIndent(indent);
-								}
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_SQUARE_CLOSE: {
-						if (!blockStack.isEmpty()) {
-							BlockStackItem item = blockStack.get(blockStack.size() - 1);
-							if (item.token.getType() == ASTokenTypes.TOKEN_SQUARE_OPEN) {
-								indent = decreaseIndent(indent);
-								blockStack.remove(item);
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_PAREN_CLOSE: {
-						if (!blockStack.isEmpty()) {
-							BlockStackItem item = blockStack.get(blockStack.size() - 1);
-							if (item.token.getType() == ASTokenTypes.TOKEN_PAREN_OPEN) {
-								indent = decreaseIndent(indent);
-								blockStack.remove(item);
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_AS:
-					case ASTokenTypes.TOKEN_KEYWORD_IS:
-					case ASTokenTypes.TOKEN_KEYWORD_IN:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_EACH:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_EXTENDS:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_IMPLEMENTS:
-					case ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT:
-					case ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT: {
-						// needs an extra space before the token
-						requiredSpace = true;
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_NOT_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_STRICT_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_STRICT_NOT_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN:
-					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN:
-					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN_EQUALS:
-					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN_EQUALS:
-					case ASTokenTypes.TOKEN_OPERATOR_DIVISION:
-					case ASTokenTypes.TOKEN_OPERATOR_MODULO:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR:
-					case ASTokenTypes.TOKEN_OPERATOR_PLUS_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_MINUS_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_MULTIPLICATION_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_DIVISION_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_MODULO_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT: {
-						if (insertSpaceBeforeAndAfterBinaryOperators) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_STAR: {
-						boolean isAnyType = checkTokenBeforeAnyType(prevTokenNotComment);
-						boolean isAnyVectorType = checkTokensForAnyVectorType(prevTokenNotComment, nextTokenNotComment);
-						if (!isAnyType && !isAnyVectorType && insertSpaceBeforeAndAfterBinaryOperators
-								&& !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_PLUS:
-					case ASTokenTypes.TOKEN_OPERATOR_MINUS: {
-						boolean isUnary = checkTokenBeforeUnaryOperator(prevTokenNotComment);
-						if (!isUnary && insertSpaceBeforeAndAfterBinaryOperators) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT: {
-						inVarOrConstDeclaration = false;
-						if (insertSpaceBeforeAndAfterBinaryOperators) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_TERNARY: {
-						ternaryStack++;
-						if (insertSpaceBeforeAndAfterBinaryOperators) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_ELLIPSIS: {
-						boolean isFirstArg = prevToken == null || prevToken.getType() == ASTokenTypes.TOKEN_PAREN_OPEN;
-						if (!isFirstArg) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_DEFAULT:
-					case ASTokenTypes.TOKEN_KEYWORD_CASE: {
-						if (!blockStack.isEmpty()) {
-							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
-							switch (stackItem.token.getType()) {
-								case ASTokenTypes.TOKEN_KEYWORD_DEFAULT:
-								case ASTokenTypes.TOKEN_KEYWORD_CASE: {
-									blockStack.remove(blockStack.size() - 1);
-									indent = decreaseIndent(indent);
-									break;
-								}
-							}
-						}
-						if (!blockStack.isEmpty()) {
-							BlockStackItem stackItem = blockStack.get(blockStack.size() - 1);
-							if (stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_SWITCH) {
-								SwitchBlockStackItem switchStackItem = (SwitchBlockStackItem) stackItem;
-								switchStackItem.clauseCount++;
-								inCaseOrDefaultClause = true;
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-								blockStack.add(new BlockStackItem(token));
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_COLON: {
-						if (ternaryStack > 0) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_SEMICOLON: {
-						if (indentedStatement) {
-							indentedStatement = false;
-							indent = decreaseIndent(indent);
-						}
-						inVarOrConstDeclaration = false;
-						varOrConstChainLevel = -1;
-						break;
-					}
-					case ASTokenTypes.TOKEN_ASDOC_COMMENT: {
-						if (prevToken != null && prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
-							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-						} else {
-							// add an extra line before an asdoc comment
-							numRequiredNewLines = Math.max(numRequiredNewLines, 2);
-						}
-						break;
-					}
-				}
-			}
-			if (!skipFormatting && prevToken != null) {
-				if (numRequiredNewLines > 0) {
-					appendNewLines(builder, numRequiredNewLines);
-					appendIndent(builder, indent);
-				} else if (requiredSpace) {
-					builder.append(' ');
-				}
-			}
-			switch (token.getType()) {
-				case ASTokenTypes.TOKEN_BLOCK_OPEN: {
-					if (blockOpenPending) {
-						boolean oneLineBlock = nextToken != null
-								&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-						if (placeOpenBraceOnNewLine && (!collapseEmptyBlocks || !oneLineBlock)) {
-							indent = increaseIndent(indent);
-						}
-					}
-					break;
-				}
-			}
-
-			// include the token's own text
-			builder.append(getTokenText(token, indent, skipFormatting));
-
-			// characters that must appear after the token
-			if (token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT
-					&& token.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN) {
-				blockOpenPending = false;
-			}
-			if (token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
-				caseOrDefaultBlockOpenPending = false;
-			}
-			requiredSpace = false;
-			numRequiredNewLines = 0;
-			if (token instanceof MetaDataPayloadToken) {
-				numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-			} else {
-				switch (token.getType()) {
-					case ASTokenTypes.TOKEN_SEMICOLON: {
-						if (inControlFlowStatement && isInForStatement(blockStack)) {
-							if (insertSpaceAfterSemicolonInForStatements) {
-								requiredSpace = true;
-							}
-							// else no space
-						} else {
-							boolean checkNext = true;
-							while (!blockStack.isEmpty() && checkNext) {
-								checkNext = false;
-								BlockStackItem prevStackItem = blockStack.get(blockStack.size() - 1);
-								if (prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_CASE
-										&& prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_DEFAULT
-										&& prevStackItem.blockDepth <= 0) {
-									blockStack.remove(blockStack.size() - 1);
-									if (prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_CLASS
-											&& prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_INTERFACE
-											&& prevStackItem.token.getType() != ASTokenTypes.TOKEN_KEYWORD_FUNCTION) {
-										checkNext = !prevStackItem.braces;
-										indent = decreaseIndent(indent);
-									}
-								}
-							}
-						}
-						if (!inControlFlowStatement) {
-							if (nextToken != null
-									&& (nextToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-											|| nextToken.getType() == ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT)) {
-								requiredSpace = true;
-							} else {
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_BLOCK_OPEN: {
-						if (blockOpenPending) {
-							blockOpenPending = false;
-							if (!blockStack.isEmpty()) {
-								BlockStackItem item = blockStack.get(blockStack.size() - 1);
-								item.blockDepth++;
-							}
-							boolean oneLineBlock = nextToken != null
-									&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-							if (!collapseEmptyBlocks || !oneLineBlock) {
-								if (!placeOpenBraceOnNewLine) {
-									indent = increaseIndent(indent);
-								}
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_BLOCK_CLOSE: {
-						if (!blockStack.isEmpty()) {
-							BlockStackItem item = blockStack.get(blockStack.size() - 1);
-							item.blockDepth--;
-							if (item.blockDepth <= 0) {
-								blockStack.remove(blockStack.size() - 1);
-							}
-							if (!(item instanceof ObjectLiteralBlockStackItem)
-									&& (nextToken == null || (nextToken.getType() != ASTokenTypes.TOKEN_SEMICOLON
-											&& nextToken.getType() != ASTokenTypes.TOKEN_PAREN_CLOSE
-											&& nextToken.getType() != ASTokenTypes.TOKEN_COMMA
-											&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-											&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT))) {
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_SQUARE_OPEN:
-						indent = increaseIndent(indent);
-						blockStack.add(new BlockStackItem(token));
-						break;
-					case ASTokenTypes.TOKEN_PAREN_OPEN: {
-						indent = increaseIndent(indent);
-						blockStack.add(new BlockStackItem(token));
-						if (inControlFlowStatement) {
-							controlFlowParenStack++;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_PAREN_CLOSE: {
-						if (inControlFlowStatement) {
-							controlFlowParenStack--;
-							if (controlFlowParenStack <= 0) {
-								inControlFlowStatement = false;
-								controlFlowParenStack = 0;
-								blockOpenPending = true;
-								if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON) {
-									blockStack.remove(blockStack.size() - 1);
-									if (!skipWhitespaceBeforeSemicolon) {
-										numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-									}
-								} else if (nextToken != null && nextToken.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
-										&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-										&& !skipWhitespaceBeforeSemicolon) {
-									indent = increaseIndent(indent);
-									BlockStackItem item = blockStack.get(blockStack.size() - 1);
-									item.braces = false;
-									numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-								}
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_INCREMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_DECREMENT: {
-						if (!inControlFlowStatement && prevToken != null
-								&& prevToken.getType() != ASTokenTypes.TOKEN_SEMICOLON && nextToken != null
-								&& nextToken.getType() != ASTokenTypes.TOKEN_SEMICOLON) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_CONTINUE:
-					case ASTokenTypes.TOKEN_KEYWORD_BREAK:
-					case ASTokenTypes.TOKEN_KEYWORD_RETURN: {
-						if (!skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_PACKAGE: {
-						blockStack.add(new BlockStackItem(token));
-						requiredSpace = true;
-						inPackageDeclaration = true;
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_CLASS: {
-						blockStack.add(new BlockStackItem(token));
-						requiredSpace = true;
-						inClassDeclaration = true;
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_INTERFACE: {
-						blockStack.add(new BlockStackItem(token));
-						requiredSpace = true;
-						inInterfaceDeclaration = true;
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_FUNCTION: {
-						blockStack.add(new BlockStackItem(token));
-						inFunctionDeclaration = true;
-						boolean skipSpace = !insertSpaceAfterFunctionKeywordForAnonymousFunctions
-								&& (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_PAREN_OPEN);
-						if (!skipSpace) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_VAR:
-					case ASTokenTypes.TOKEN_KEYWORD_CONST: {
-						inVarOrConstDeclaration = true;
-						requiredSpace = true;
-						varOrConstChainLevel = blockStack.size();
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_CATCH:
-					case ASTokenTypes.TOKEN_KEYWORD_FOR:
-					case ASTokenTypes.TOKEN_KEYWORD_IF:
-					case ASTokenTypes.TOKEN_KEYWORD_WHILE:
-					case ASTokenTypes.TOKEN_KEYWORD_WITH: {
-						inControlFlowStatement = true;
-						blockStack.add(new BlockStackItem(token));
-						if (insertSpaceAfterKeywordsInControlFlowStatements && !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_SWITCH: {
-						inControlFlowStatement = true;
-						blockStack.add(new SwitchBlockStackItem(token));
-						if (insertSpaceAfterKeywordsInControlFlowStatements && !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_TRY: {
-						blockStack.add(new BlockStackItem(token));
-						if (!skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						blockOpenPending = true;
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_ELSE: {
-						if (nextTokenNotComment != null
-								&& nextTokenNotComment.getType() == ASTokenTypes.TOKEN_KEYWORD_IF) {
-							requiredSpace = true;
-						} else {
-							blockStack.add(new BlockStackItem(token));
-							blockOpenPending = true;
-							if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON) {
-								blockStack.remove(blockStack.size() - 1);
-								if (!skipWhitespaceBeforeSemicolon) {
-									numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-								}
-							} else if (nextToken != null && nextToken.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
-									&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-									&& !skipWhitespaceBeforeSemicolon) {
-								indent = increaseIndent(indent);
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_DO: {
-						blockStack.add(new BlockStackItem(token));
-						blockOpenPending = true;
-						if (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON) {
-							blockStack.remove(blockStack.size() - 1);
-							if (!skipWhitespaceBeforeSemicolon) {
-								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-							}
-						} else if (nextToken != null && nextToken.getType() != ASTokenTypes.TOKEN_BLOCK_OPEN
-								&& nextToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-								&& !skipWhitespaceBeforeSemicolon) {
-							indent = increaseIndent(indent);
-							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_COLON: {
-						if (!inControlFlowStatement && !inVarOrConstDeclaration && !inFunctionDeclaration) {
-							if (inCaseOrDefaultClause) {
-								inCaseOrDefaultClause = false;
-								caseOrDefaultBlockOpenPending = true;
-								boolean nextIsBlock = nextTokenNotComment != null
-										&& nextTokenNotComment.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN;
-								if (nextIsBlock) {
-									IASToken afterBlockClose = findTokenAfterBlock(nextTokenNotComment, tokens);
-									if (afterBlockClose != null) {
-										if (afterBlockClose.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE
-												|| afterBlockClose.getType() == ASTokenTypes.TOKEN_KEYWORD_CASE
-												|| afterBlockClose.getType() == ASTokenTypes.TOKEN_KEYWORD_DEFAULT) {
-											blockOpenPending = true;
-											requiredSpace = true;
-											blockStack.remove(blockStack.size() - 1);
-										}
-									}
-								}
-								if (!nextIsBlock || !blockOpenPending) {
-									indent = increaseIndent(indent);
-									if (nextToken != null && (nextToken
-											.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-											|| nextToken.getType() == ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT)) {
-										requiredSpace = true;
-									} else {
-										numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-									}
-								}
-							} else if (ternaryStack > 0) {
-								ternaryStack--;
-								requiredSpace = true;
-							} else {
-								requiredSpace = true;
-							}
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_CASE: {
-						if (!skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_DEFAULT: {
-						if (!inCaseOrDefaultClause && !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_KEYWORD_AS:
-					case ASTokenTypes.TOKEN_KEYWORD_DELETE:
-					case ASTokenTypes.TOKEN_KEYWORD_IMPORT:
-					case ASTokenTypes.TOKEN_KEYWORD_IN:
-					case ASTokenTypes.TOKEN_KEYWORD_INCLUDE:
-					case ASTokenTypes.TOKEN_KEYWORD_INSTANCEOF:
-					case ASTokenTypes.TOKEN_KEYWORD_IS:
-					case ASTokenTypes.TOKEN_KEYWORD_NEW:
-					case ASTokenTypes.TOKEN_KEYWORD_THROW:
-					case ASTokenTypes.TOKEN_KEYWORD_TYPEOF:
-					case ASTokenTypes.TOKEN_KEYWORD_USE:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_CONFIG:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_EXTENDS:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_GET:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_GOTO:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_IMPLEMENTS:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_NAMESPACE:
-					case ASTokenTypes.TOKEN_RESERVED_WORD_SET:
-					case ASTokenTypes.TOKEN_MODIFIER_ABSTRACT:
-					case ASTokenTypes.TOKEN_MODIFIER_DYNAMIC:
-					case ASTokenTypes.TOKEN_MODIFIER_FINAL:
-					case ASTokenTypes.TOKEN_MODIFIER_NATIVE:
-					case ASTokenTypes.TOKEN_MODIFIER_OVERRIDE:
-					case ASTokenTypes.TOKEN_MODIFIER_STATIC:
-					case ASTokenTypes.TOKEN_MODIFIER_VIRTUAL:
-					case ASTokenTypes.TOKEN_NAMESPACE_ANNOTATION: {
-						if (!skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_NOT_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_STRICT_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_STRICT_NOT_EQUAL:
-					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN:
-					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN:
-					case ASTokenTypes.TOKEN_OPERATOR_LESS_THAN_EQUALS:
-					case ASTokenTypes.TOKEN_OPERATOR_GREATER_THAN_EQUALS:
-					case ASTokenTypes.TOKEN_OPERATOR_DIVISION:
-					case ASTokenTypes.TOKEN_OPERATOR_MODULO:
-					case ASTokenTypes.TOKEN_OPERATOR_TERNARY:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR:
-					case ASTokenTypes.TOKEN_OPERATOR_PLUS_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_MINUS_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_MULTIPLICATION_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_DIVISION_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_MODULO_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_AND_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_LEFT_SHIFT_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_OR_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT:
-					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT: {
-						if (insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_STAR: {
-						boolean isAnyType = checkTokenBeforeAnyType(prevTokenNotComment);
-						boolean isAnyVectorType = checkTokensForAnyVectorType(prevTokenNotComment, nextTokenNotComment);
-						if (!isAnyType && !isAnyVectorType && insertSpaceBeforeAndAfterBinaryOperators
-								&& !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_OPERATOR_PLUS:
-					case ASTokenTypes.TOKEN_OPERATOR_MINUS: {
-						boolean isUnary = checkTokenBeforeUnaryOperator(prevTokenNotComment);
-						if (!isUnary && insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_COMMA: {
-						if (varOrConstChainLevel == blockStack.size()) {
-							inVarOrConstDeclaration = true;
-						}
-						if (insertSpaceAfterCommaDelimiter && !skipWhitespaceBeforeSemicolon) {
-							requiredSpace = true;
-						}
-						break;
-					}
-					case ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT: {
-						numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-
-						String trimmed = token.getText().substring(2).trim();
-						if (!skipFormatting && FORMATTER_TAG_OFF.equals(trimmed)) {
-							skipFormatting = true;
-							appendNewLines(builder, 1);
-						} else if (skipFormatting && FORMATTER_TAG_ON.equals(trimmed)) {
-							skipFormatting = false;
-							numRequiredNewLines = 0;
-						}
-						break;
-					}
-					case ASTokenTypes.TOKEN_ASDOC_COMMENT:
-					case ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT: {
-						if (!skipWhitespaceBeforeSemicolon) {
-							if (nextTokenOrExtra != null && nextTokenOrExtra.getType() == TOKEN_TYPE_EXTRA) {
-								requiredSpace = true;
-							}
-						}
-						break;
-					}
-				}
-			}
-			if ((inPackageDeclaration || inClassDeclaration || inInterfaceDeclaration || inFunctionDeclaration)
-					&& nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
-				blockOpenPending = true;
-				inPackageDeclaration = false;
-				inClassDeclaration = false;
-				inInterfaceDeclaration = false;
-				inFunctionDeclaration = false;
-			}
-			prevToken = token;
-			prevTokenOrExtra = prevToken;
-			if (prevToken.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-					&& prevToken.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
-					&& prevToken.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
-				prevTokenNotComment = prevToken;
-			}
-		}
-		if (blockStack.size() > 0) {
-			throw new Exception("Block stack size too large");
-		}
-		return builder.toString();
-	}
-
-	private IASToken findTokenAfterBlock(IASToken tokenBlockOpen, List<IASToken> tokens) {
-		List<IASToken> stack = new ArrayList<IASToken>();
-		int startIndex = tokens.indexOf(tokenBlockOpen) + 1;
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IASToken current = tokens.get(i);
-			if (current.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
-				stack.add(current);
-			} else if (current.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE) {
-				if (stack.size() == 0) {
-					return getNextTokenSkipExtraAndComments(tokens, i + 1);
-				}
-				stack.remove(stack.size() - 1);
-			}
-		}
-		return null;
-	}
-
-	private int countNewLinesInExtra(IASToken tokenOrExtra) {
-		if (tokenOrExtra == null || tokenOrExtra.getType() != TOKEN_TYPE_EXTRA) {
-			return 0;
-		}
-		int numNewLinesInWhitespace = 0;
-		String whitespace = tokenOrExtra.getText();
-		int index = -1;
-		while ((index = whitespace.indexOf('\n', index + 1)) != -1) {
-			numNewLinesInWhitespace++;
-		}
-		return numNewLinesInWhitespace;
-	}
-
-	private String formatSingleLineComment(String comment) {
-		comment = comment.substring(2).trim();
-		StringBuilder builder = new StringBuilder();
-		builder.append("//");
-		if (insertSpaceAtStartOfLineComment) {
-			builder.append(" ");
-		}
-		builder.append(comment);
-		return builder.toString();
-	}
-
-	private String formatMultiLineComment(String comment) {
-		return comment;
-	}
-
-	private String formatLiteralString(String string) {
-		String charsToEscape = "\b\t\n\f\r\\";
-		String escapeChars = "btnfr\\";
-		int escapeIndex = -1;
-		char currChar;
-		StringBuilder builder = new StringBuilder();
-		for (int i = 0; i < string.length(); ++i) {
-			currChar = string.charAt(i);
-			if (i == 0) {
-				charsToEscape += currChar;
-				escapeChars += currChar;
-				builder.append(currChar);
-				continue;
-			}
-			if (i == string.length() - 1) {
-				builder.append(currChar);
-				continue;
-			}
-			escapeIndex = charsToEscape.indexOf(currChar);
-			if (escapeIndex != -1) {
-				builder.append("\\");
-				builder.append(escapeChars.charAt(escapeIndex));
-			} else {
-				builder.append(currChar);
-			}
-		}
-		return builder.toString();
-	}
-
-	private boolean isInForStatement(List<BlockStackItem> blockStack) {
-		for (int i = blockStack.size() - 1; i >= 0; i--) {
-			BlockStackItem item = blockStack.get(i);
-			switch (item.token.getType()) {
-				case ASTokenTypes.TOKEN_BLOCK_OPEN:
-				case ASTokenTypes.TOKEN_SQUARE_OPEN:
-				case ASTokenTypes.TOKEN_PAREN_OPEN: {
-					// these tokens are fine, keep searching
-					break;
-				}
-				case ASTokenTypes.TOKEN_KEYWORD_FOR: {
-					return true;
-				}
-				default: {
-					return false;
-				}
-			}
-		}
-		return false;
-	}
-
-	private boolean isInListing(String lineText, boolean alreadyInListing) {
-		int searchIndex = 0;
-		boolean inListing = alreadyInListing;
-		while (searchIndex < lineText.length()) {
-			if (!inListing) {
-				searchIndex = lineText.indexOf("<listing", searchIndex);
-				if (searchIndex == -1) {
-					return false;
-				}
-				searchIndex += 8;
-				inListing = true;
-			}
-			searchIndex = lineText.indexOf("</listing>", searchIndex);
-			if (searchIndex == -1) {
-				return true;
-			}
-			searchIndex += 10;
-			inListing = false;
-		}
-		return inListing;
-	}
-
-	private String formatASDocComment(String comment, int indent) {
-		String[] lines = comment.split("\n");
-		StringBuilder builder = new StringBuilder();
-		String lineText = lines[0].trim();
-		builder.append(lineText);
-		boolean inListing = isInListing(lineText, false);
-		if (lines.length > 1) {
-			builder.append('\n');
-		}
-		String listingIndent = null;
-		for (int i = 1; i < lines.length - 1; i++) {
-			lineText = lines[i].trim();
-			if (inListing) {
-				Matcher startMatcher = ASDOC_START_LINE_PATTERN.matcher(lineText);
-				if (startMatcher.find()) {
-					if (listingIndent == null) {
-						listingIndent = startMatcher.group(1);
-					} else if (startMatcher.group().length() >= lineText.length()) {
-						lineText = "*" + listingIndent;
-					}
-				}
-			}
-			appendIndent(builder, indent);
-			builder.append(' ');
-			builder.append(lineText);
-			builder.append('\n');
-			inListing = isInListing(lineText, inListing);
-			if (!inListing) {
-				listingIndent = null;
-			}
-		}
-		if (lines.length > 1) {
-			appendIndent(builder, indent);
-			builder.append(' ');
-			builder.append(lines[lines.length - 1].trim());
-		}
-		return builder.toString();
-	}
-
-	private String getTokenText(IASToken token, int indent, boolean skipFormatting) {
-
-		if (token instanceof MetaDataPayloadToken) {
-			MetaDataPayloadToken metaPlayloadToken = (MetaDataPayloadToken) token;
-			return formatMetadataToken(metaPlayloadToken);
-		} else {
-			switch (token.getType()) {
-				case ASTokenTypes.TOKEN_ASDOC_COMMENT: {
-					if (skipFormatting) {
-						return token.getText();
-					}
-					return formatASDocComment(token.getText(), indent);
-				}
-				case ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT: {
-					if (skipFormatting) {
-						return token.getText();
-					}
-					return formatSingleLineComment(token.getText());
-				}
-				case ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT: {
-					if (skipFormatting) {
-						return token.getText();
-					}
-					return formatMultiLineComment(token.getText());
-				}
-				case ASTokenTypes.TOKEN_LITERAL_STRING: {
-					return formatLiteralString(token.getText());
-				}
-				case ASTokenTypes.TOKEN_SEMICOLON: {
-					if (skipFormatting) {
-						return token.isImplicit() ? "" : token.getText();
-					}
-					boolean skipSemicolon = Semicolons.REMOVE.equals(semicolons)
-							|| (Semicolons.IGNORE.equals(semicolons) && token.isImplicit());
-					if (!skipSemicolon) {
-						return token.getText();
-					}
-					return null;
-				}
-				default: {
-					return token.getText();
-				}
-			}
-		}
-	}
-
-	private String formatMetadataToken(MetaDataPayloadToken token) {
-		// metadata doesn't include all of its text as tokens, so we need to
-		// insert some extra characters here and there
-		StringBuilder builder = new StringBuilder();
-		boolean needsComma = false;
-		List<MetadataToken> payload = token.getPayload();
-		for (int i = 0; i < payload.size(); i++) {
-			MetadataToken metaToken = payload.get(i);
-			switch (metaToken.getType()) {
-				case MetadataTokenTypes.TOKEN_ATTR_NAME:
-				case MetadataTokenTypes.TOKEN_ATTR_EVENT:
-				case MetadataTokenTypes.TOKEN_ATTR_FORMAT:
-				case MetadataTokenTypes.TOKEN_ATTR_TYPE:
-				case MetadataTokenTypes.TOKEN_ATTR_ARRAY_TYPE:
-				case MetadataTokenTypes.TOKEN_ATTR_INHERITS:
-				case MetadataTokenTypes.TOKEN_ATTR_ENUM:
-				case MetadataTokenTypes.TOKEN_ATTR_UNKNOWN:
-				case MetadataTokenTypes.TOKEN_ATTR_ENV:
-				case MetadataTokenTypes.TOKEN_ATTR_VERBOSE:
-				case MetadataTokenTypes.TOKEN_ATTR_CATEGORY:
-				case MetadataTokenTypes.TOKEN_ATTR_VARIABLE:
-				case MetadataTokenTypes.TOKEN_ATTR_DEFAULT_VALUE:
-				case MetadataTokenTypes.TOKEN_ATTR_STATES:
-				case MetadataTokenTypes.TOKEN_ATTR_IMPLEMENTATION:
-				case MetadataTokenTypes.TOKEN_ATTR_OPERATOR_NS_QUALIFIER: {
-					if (needsComma) {
-						builder.append(",");
-						if (insertSpaceBetweenMetadataAttributes) {
-							builder.append(" ");
-						}
-					}
-					needsComma = true;
-					builder.append(metaToken.getText());
-					MetadataToken nextToken = payload.get(i + 1);
-					if (nextToken.getType() == 8) {
-						builder.append("=");
-						builder.append("\"");
-						builder.append(nextToken.getText());
-						builder.append("\"");
-						i++;
-					}
-					break;
-				}
-				case MetadataTokenTypes.TOKEN_STRING: {
-					if (needsComma) {
-						builder.append(",");
-						if (insertSpaceBetweenMetadataAttributes) {
-							builder.append(" ");
-						}
-					}
-					builder.append("\"");
-					builder.append(metaToken.getText());
-					builder.append("\"");
-					needsComma = true;
-					break;
-				}
-				default: {
-					builder.append(metaToken.getText());
-				}
-			}
-		}
-		return builder.toString();
-	}
-
-	private boolean checkTokenBeforeAnyType(IASToken token) {
-		return token.getType() == ASTokenTypes.TOKEN_COLON;
-	}
-
-	private boolean checkTokensForAnyVectorType(IASToken prevToken, IASToken nextToken) {
-		return prevToken != null && nextToken != null
-				&& ((prevToken.getType() == ASTokenTypes.TOKEN_TYPED_COLLECTION_OPEN
-						&& nextToken.getType() == ASTokenTypes.TOKEN_TYPED_COLLECTION_CLOSE)
-						|| (prevToken.getType() == ASTokenTypes.TOKEN_TYPED_LITERAL_OPEN
-								&& nextToken.getType() == ASTokenTypes.TOKEN_TYPED_LITERAL_CLOSE));
-	}
-
-	private boolean checkTokenBeforeUnaryOperator(IASToken token) {
-		return (token instanceof ASToken) ? ((ASToken) token).isOperator()
-				|| token.getType() == ASTokenTypes.TOKEN_SQUARE_OPEN || token.getType() == ASTokenTypes.TOKEN_PAREN_OPEN
-				|| token.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN || token.getType() == ASTokenTypes.TOKEN_SEMICOLON
-				|| token.getType() == ASTokenTypes.TOKEN_KEYWORD_RETURN || token.getType() == ASTokenTypes.TOKEN_COMMA
-				|| token.getType() == ASTokenTypes.TOKEN_COLON : (token == null);
-	}
-
-	private int increaseIndent(int indent) {
-		return indent + 1;
-	}
-
-	private int decreaseIndent(int indent) {
-		return Math.max(0, indent - 1);
-	}
-
-	private String getIndent() {
-		if (insertSpaces) {
-			String result = "";
-			for (int j = 0; j < tabSize; j++) {
-				result += " ";
-			}
-			return result;
-		}
-		return "\t";
-	}
-
-	private String getAttributeIndent(IMXMLToken openTagToken) {
-		if (!mxmlAlignAttributes) {
-			return getIndent();
-		}
-		int indentSize = openTagToken.getText().length() + 1;
-		String result = "";
-		while (indentSize >= tabSize) {
-			result += getIndent();
-			indentSize -= tabSize;
-		}
-		for (int i = 0; i < indentSize; i++) {
-			result += " ";
-		}
-		return result;
-	}
-
-	private void appendIndent(StringBuilder builder, int indent) {
-		String indentString = getIndent();
-		for (int i = 0; i < indent; i++) {
-			builder.append(indentString);
-		}
-	}
-
-	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 boolean hasErrors(Collection<ICompilerProblem> problems) {
-		CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer(null);
-		for (ICompilerProblem problem : problems) {
-			CompilerProblemSeverity severity = categorizer.getProblemSeverity(problem);
-			if (CompilerProblemSeverity.ERROR.equals(severity)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	private static class BlockStackItem {
-		public BlockStackItem(IASToken token) {
-			this.token = token;
-		}
-
-		public IASToken token;
-		public int blockDepth = 0;
-		public boolean braces = true;
-	}
-
-	private static class ObjectLiteralBlockStackItem extends BlockStackItem {
-		public ObjectLiteralBlockStackItem(IASToken token) {
-			super(token);
-		}
-	}
-
-	private static class SwitchBlockStackItem extends BlockStackItem {
-		public SwitchBlockStackItem(IASToken token) {
-			super(token);
-		}
-
-		public int clauseCount = 0;
-	}
-
-	private String formatMXMLTextInternal(String filePath, String text, Collection<ICompilerProblem> problems) {
-		if (problems == null) {
-			problems = new ArrayList<ICompilerProblem>();
-		}
-
-		StringReader textReader = new StringReader(text);
-		MXMLTokenizer mxmlTokenizer = new MXMLTokenizer();
-		IMXMLToken[] originalTokens = null;
-		try {
-			originalTokens = mxmlTokenizer.getTokens(textReader);
-		} finally {
-			IOUtils.closeQuietly(textReader);
-			IOUtils.closeQuietly(mxmlTokenizer);
-		}
-
-		if (mxmlTokenizer.hasTokenizationProblems()) {
-			problems.addAll(mxmlTokenizer.getTokenizationProblems());
-		}
-
-		if (!ignoreProblems && hasErrors(problems)) {
-			return text;
-		}
-
-		Pattern scriptStartPattern = Pattern.compile("<((?:mx|fx):(Script|Metadata))");
-
-		List<IMXMLToken> tokens = insertExtraMXMLTokens(originalTokens, text);
-
-		int indent = 0;
-		int numRequiredNewLines = 0;
-		boolean requiredSpace = false;
-		boolean inOpenTag = false;
-		boolean inCloseTag = false;
-		boolean skipFormatting = false;
-		String attributeIndent = "";
-		IMXMLToken prevToken = null;
-		IMXMLToken prevTokenOrExtra = null;
-		IMXMLToken token = null;
-		IMXMLToken nextToken = null;
-		List<ElementStackItem> elementStack = new ArrayList<ElementStackItem>();
-		StringBuilder builder = new StringBuilder();
-		for (int i = 0; i < tokens.size(); i++) {
-			token = tokens.get(i);
-			nextToken = null;
-			if (i < (tokens.size() - 1)) {
-				nextToken = tokens.get(i + 1);
-			}
-			if (token.getType() == TOKEN_TYPE_EXTRA) {
-				if (skipFormatting) {
-					builder.append(token.getText());
-				} else {
-					if (i == (tokens.size() - 1)) {
-						// if the last token is whitespace, include at most one
-						// new line, but strip the rest
-						numRequiredNewLines = Math.min(1, Math.max(0, countNewLinesInExtra(token)));
-						appendNewLines(builder, numRequiredNewLines);
-						break;
-					}
-					numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
-				}
-				prevTokenOrExtra = token;
-				continue;
-			} else if (token.getType() == MXMLTokenTypes.TOKEN_WHITESPACE) {
-				if (skipFormatting) {
-					builder.append(token.getText());
-				} else {
-					if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-						numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
-					} else {
-						// if the parent element contains text, treat whitespace
-						// the same as text, and don't reformat it
-						// text is never reformatted because some components use it
-						// without collapsing whitespace, and developers would be
-						// confused if whitespace that they deliberately added were
-						// to be removed
-						builder.append(token.getText());
-					}
-					if (i == (tokens.size() - 1)) {
-						// if the last token is whitespace, include at most one
-						// new line, but strip the rest
-						numRequiredNewLines = Math.min(1, numRequiredNewLines);
-						appendNewLines(builder, numRequiredNewLines);
-					}
-				}
-				continue;
-			} else if (token.getType() == MXMLTokenTypes.TOKEN_OPEN_TAG_START
-					&& scriptStartPattern.matcher(token.getText()).matches()) {
-
-				if (prevToken != null && numRequiredNewLines > 0) {
-					appendNewLines(builder, numRequiredNewLines);
-				}
-				StringBuilder scriptBuilder = new StringBuilder();
-				scriptBuilder.append(token.getText());
-				boolean inScriptCloseTag = false;
-				while (i < (tokens.size() - 1)) {
-					i++;
-					token = tokens.get(i);
-					scriptBuilder.append(token.getText());
-					if (token.getType() == MXMLTokenTypes.TOKEN_CLOSE_TAG_START) {
-						inScriptCloseTag = true;
-					} else if (inScriptCloseTag && token.getType() == MXMLTokenTypes.TOKEN_TAG_END) {
-						break;
-					}
-				}
-				if (problems == null) {
-					// we need to know if there were problems because it means that we
-					// need to return the original, unformatted text
-					problems = new ArrayList<ICompilerProblem>();
-				}
-				builder.append(formatMXMLScriptElement(filePath, token.getLine(), scriptBuilder.toString(), problems));
-				if (hasErrors(problems)) {
-					return text;
-				}
-				prevToken = token;
-				prevTokenOrExtra = token;
-				requiredSpace = false;
-				numRequiredNewLines = 1;
-				continue;
-			}
-
-			// characters that must appear before the token
-			switch (token.getType()) {
-				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
-					inOpenTag = true;
-					// if the parent contains text, children should be the same
-					boolean containsText = !elementStack.isEmpty()
-							&& elementStack.get(elementStack.size() - 1).containsText;
-					elementStack.add(new ElementStackItem(token, token.getText().substring(1), containsText));
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
-					if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-						indent = decreaseIndent(indent);
-					}
-					inCloseTag = true;
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_NAME: {
-					requiredSpace = true;
-					break;
-				}
-			}
-
-			if (!skipFormatting && prevToken != null) {
-				if (numRequiredNewLines > 0) {
-					appendNewLines(builder, numRequiredNewLines);
-					appendIndent(builder, indent);
-					if (attributeIndent.length() > 0) {
-						builder.append(attributeIndent);
-					}
-				} else if (requiredSpace) {
-					builder.append(' ');
-				}
-			}
-
-			// include the token's own text
-			// no token gets reformatted before being appended
-			// whitespace is the only special case, but that's not handled here
-			builder.append(token.getText());
-
-			// characters that must appear after the token
-			requiredSpace = false;
-			numRequiredNewLines = 0;
-
-			switch (token.getType()) {
-				case MXMLTokenTypes.TOKEN_PROCESSING_INSTRUCTION: {
-					numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
-					if (nextToken != null && nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END
-							&& nextToken.getType() != TOKEN_TYPE_EXTRA) {
-						requiredSpace = true;
-					}
-					if (elementStack.isEmpty()) {
-						// something is very wrong!
-						return text;
-					}
-					String elementName = token.getText().substring(2);
-					ElementStackItem elementItem = elementStack.remove(elementStack.size() - 1);
-					if (!elementName.equals(elementItem.elementName)) {
-						// there's a unclosed tag with a different name somewhere
-						return text;
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
-					if (nextToken != null && nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END) {
-						attributeIndent = getAttributeIndent(token);
-						if (nextToken.getType() != TOKEN_TYPE_EXTRA) {
-							requiredSpace = true;
-						}
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_TAG_END: {
-					if (inOpenTag) {
-						ElementStackItem element = elementStack.get(elementStack.size() - 1);
-						if (!element.containsText) {
-							element.containsText = elementContainsText(tokens, i + 1, element.token);
-						}
-						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-							indent = increaseIndent(indent);
-						}
-					} else {
-						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-						}
-					}
-					inOpenTag = false;
-					attributeIndent = "";
-					inCloseTag = false;
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_EMPTY_TAG_END: {
-					if (inOpenTag) {
-						elementStack.remove(elementStack.size() - 1);
-					} else {
-						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-						}
-					}
-					inOpenTag = false;
-					// no need to change nested indent after this tag
-					// however, we may need to remove attribute indent
-					attributeIndent = "";
-					// we shouldn't find an empty close tag, but clear flag anyway
-					inCloseTag = false;
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_STRING: {
-					if (inOpenTag && mxmlInsertNewLineBetweenAttributes && nextToken != null
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END) {
-						numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_COMMENT: {
-					String tokenText = token.getText();
-					String trimmed = tokenText.substring(4, tokenText.length() - 3).trim();
-					if (!skipFormatting && FORMATTER_TAG_OFF.equals(trimmed)) {
-						skipFormatting = true;
-					} else if (skipFormatting && FORMATTER_TAG_ON.equals(trimmed)) {
-						skipFormatting = false;
-					}
-					break;
-				}
-			}
-
-			prevToken = token;
-			prevTokenOrExtra = token;
-		}
-
-		return builder.toString();
-	}
-
-	private boolean elementContainsText(List<IMXMLToken> tokens, int startIndex, IMXMLToken openTagToken) {
-		ArrayList<IMXMLToken> elementStack = new ArrayList<IMXMLToken>();
-		elementStack.add(openTagToken);
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IMXMLToken token = tokens.get(i);
-			switch (token.getType()) {
-				case MXMLTokenTypes.TOKEN_TEXT: {
-					if (elementStack.size() == 1) {
-						return true;
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
-					elementStack.add(token);
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_EMPTY_TAG_END: {
-					elementStack.remove(elementStack.size() - 1);
-					if (elementStack.size() == 0) {
-						return false;
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
-					elementStack.remove(elementStack.size() - 1);
-					if (elementStack.size() == 0) {
-						return false;
-					}
-					break;
-				}
-			}
-		}
-		return false;
-	}
-
-	private int countNewLinesInExtra(IMXMLToken token) {
-		if (token == null
-				|| (token.getType() != MXMLTokenTypes.TOKEN_WHITESPACE && token.getType() != TOKEN_TYPE_EXTRA)) {
-			return 0;
-		}
-		int numNewLinesInWhitespace = 0;
-		String whitespace = token.getText();
-		int index = -1;
-		while ((index = whitespace.indexOf('\n', index + 1)) != -1) {
-			numNewLinesInWhitespace++;
-		}
-		return numNewLinesInWhitespace;
-	}
-
-	private static class ElementStackItem {
-		public ElementStackItem(IMXMLToken token, String elementName, boolean containsText) {
-			this.token = token;
-			this.elementName = elementName;
-			this.containsText = containsText;
-		}
-
-		public IMXMLToken token;
-		public String elementName;
-		public boolean containsText = false;
-	}
 }
\ No newline at end of file
diff --git a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java b/formatter/src/main/java/org/apache/royale/formatter/internal/ASTokenFormatter.java
similarity index 59%
copy from formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
copy to formatter/src/main/java/org/apache/royale/formatter/internal/ASTokenFormatter.java
index a0f71dcb2..97c0b18f4 100644
--- a/formatter/src/main/java/org/apache/royale/formatter/FORMATTER.java
+++ b/formatter/src/main/java/org/apache/royale/formatter/internal/ASTokenFormatter.java
@@ -17,32 +17,17 @@
 //
 ////////////////////////////////////////////////////////////////////////////////
 
-package org.apache.royale.formatter;
+package org.apache.royale.formatter.internal;
 
-import java.io.File;
-import java.io.IOException;
 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 java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
-import org.apache.royale.compiler.clients.problems.CompilerProblemCategorizer;
-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.clients.problems.WorkspaceProblemFormatter;
-import org.apache.royale.compiler.common.VersionInfo;
-import org.apache.royale.compiler.exceptions.ConfigurationException;
-import org.apache.royale.compiler.filespecs.FileSpecification;
-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;
@@ -52,416 +37,51 @@ import org.apache.royale.compiler.internal.parsing.as.MetadataToken;
 import org.apache.royale.compiler.internal.parsing.as.MetadataTokenTypes;
 import org.apache.royale.compiler.internal.parsing.as.RepairingTokenBuffer;
 import org.apache.royale.compiler.internal.parsing.as.StreamingASTokenizer;
-import org.apache.royale.compiler.internal.parsing.mxml.MXMLToken;
-import org.apache.royale.compiler.internal.parsing.mxml.MXMLTokenizer;
 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.parsing.IMXMLToken;
-import org.apache.royale.compiler.parsing.MXMLTokenTypes;
-import org.apache.royale.compiler.problems.CompilerProblemSeverity;
-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.FORMATTER;
 import org.apache.royale.formatter.config.Semicolons;
-import org.apache.royale.utils.FilenameNormalization;
 
-/**
- * Formats .as source files.
- */
-public class FORMATTER {
+public class ASTokenFormatter extends BaseTokenFormatter {
 	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";
 	private static final Pattern ASDOC_START_LINE_PATTERN = Pattern.compile("^\\*(\\s*)");
 	private static final String FORMATTER_TAG_OFF = "@formatter:off";
 	private static final String FORMATTER_TAG_ON = "@formatter:on";
 
-	static enum ExitCode {
-		SUCCESS(0), PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), FAILED_WITH_CONFIG_PROBLEMS(4);
-
-		ExitCode(int code) {
-			this.code = code;
-		}
-
-		final int code;
-
-		int getCode() {
-			return code;
-		}
-	}
-
-	/**
-	 * Java program entry point.
-	 * 
-	 * @param args command line arguments
-	 */
-	public static void main(final String[] args) {
-		FORMATTER formatter = new FORMATTER();
-		int exitCode = formatter.execute(args);
-		System.exit(exitCode);
-	}
-
-	public FORMATTER() {
-
-	}
-
-	public int tabSize = 4;
-	public boolean insertSpaces = false;
-	public boolean insertFinalNewLine = false;
-	public boolean placeOpenBraceOnNewLine = true;
-	public boolean insertSpaceAfterSemicolonInForStatements = true;
-	public boolean insertSpaceAfterKeywordsInControlFlowStatements = true;
-	public boolean insertSpaceAfterFunctionKeywordForAnonymousFunctions = false;
-	public boolean insertSpaceBeforeAndAfterBinaryOperators = true;
-	public boolean insertSpaceAfterCommaDelimiter = true;
-	public boolean insertSpaceBetweenMetadataAttributes = true;
-	public boolean insertSpaceAtStartOfLineComment = true;
-	public int maxPreserveNewLines = 2;
-	public Semicolons semicolons = Semicolons.INSERT;
-	public boolean ignoreProblems = false;
-	public boolean collapseEmptyBlocks = false;
-	public boolean mxmlAlignAttributes = false;
-	public boolean mxmlInsertNewLineBetweenAttributes = false;
-
-	private ProblemQuery problemQuery;
-	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;
-		problemQuery = new ProblemQuery();
-		problemQuery.setShowWarnings(false);
-
-		try {
-			boolean continueFormatting = configure(args, problemQuery);
-			if (continueFormatting) {
-				if (inputFiles.size() == 0) {
-					StringBuilder builder = new StringBuilder();
-					Scanner sysInScanner = new Scanner(System.in);
-					try {
-						while (sysInScanner.hasNext()) {
-							builder.append(sysInScanner.next());
-						}
-					} finally {
-						IOUtils.closeQuietly(sysInScanner);
-					}
-					String filePath = FilenameNormalization.normalize("stdin.as");
-					String fileText = builder.toString();
-					String formattedText = formatFileText(filePath, fileText, problemQuery.getProblems());
-					if (!fileText.equals(formattedText)) {
-						if (listChangedFiles) {
-							System.out.println(filePath);
-						}
-					}
-					if (!listChangedFiles) {
-						System.out.println(formattedText);
-					}
-				} else {
-					for (File inputFile : inputFiles) {
-						String filePath = FilenameNormalization.normalize(inputFile.getAbsolutePath());
-						FileSpecification fileSpec = new FileSpecification(filePath);
-						String fileText = IOUtils.toString(fileSpec.createReader());
-						String formattedText = formatFileText(filePath, fileText, problemQuery.getProblems());
-						if (!fileText.equals(formattedText)) {
-							if (listChangedFiles) {
-								System.out.println(filePath);
-							}
-							if (writeBackToInputFiles) {
-								FileUtils.write(inputFile, formattedText, "utf8");
-							}
-						}
-						if (!listChangedFiles && !writeBackToInputFiles) {
-							System.out.println(formattedText);
-						}
-					}
-				}
-			} else if (problemQuery.hasFilteredProblems()) {
-				exitCode = ExitCode.FAILED_WITH_CONFIG_PROBLEMS;
-			} else {
-				exitCode = ExitCode.PRINT_HELP;
-			}
-		} catch (Exception e) {
-			problemQuery.add(new UnexpectedExceptionProblem(e));
-			System.err.println(e.getMessage());
-			exitCode = ExitCode.FAILED_WITH_EXCEPTIONS;
-		} finally {
-			if (problemQuery.hasFilteredProblems()) {
-				final Workspace workspace = new Workspace();
-				final CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer();
-				final ProblemFormatter formatter = new WorkspaceProblemFormatter(workspace, categorizer);
-				final ProblemPrinter printer = new ProblemPrinter(formatter);
-				printer.printProblems(problemQuery.getFilteredProblems());
-			}
-		}
-		return exitCode.getCode();
-	}
-
-	public String formatFile(File file, Collection<ICompilerProblem> problems) throws IOException {
-		String filePath = FilenameNormalization.normalize(file.getAbsolutePath());
-		FileSpecification fileSpec = new FileSpecification(filePath);
-		String fileText = IOUtils.toString(fileSpec.createReader());
-		return formatFileText(filePath, fileText, problems);
-	}
-
-	public String formatFile(File file) throws IOException {
-		return formatFile(file, null);
-	}
-
-	public String formatFileText(String filePath, String text, Collection<ICompilerProblem> problems) {
-		filePath = FilenameNormalization.normalize(filePath);
-		String result = null;
-		if (filePath.endsWith(".mxml")) {
-			result = formatMXMLTextInternal(filePath, text, problems);
-		} else {
-			result = formatAS3TextInternal(filePath, text, problems);
-		}
-		if (insertFinalNewLine && result.charAt(result.length() - 1) != '\n') {
-			return result + '\n';
-		}
-		return result;
-	}
-
-	public String formatFileText(String filePath, String text) {
-		return formatFileText(filePath, text, null);
-	}
-
-	public String formatActionScriptText(String text, Collection<ICompilerProblem> problems) {
-		String filePath = FilenameNormalization.normalize("stdin.as");
-		return formatAS3TextInternal(filePath, text, problems);
-	}
-
-	public String formatActionScriptText(String text) {
-		return formatActionScriptText(text, null);
-	}
-
-	public String formatMXMLText(String text, Collection<ICompilerProblem> problems) {
-		String filePath = FilenameNormalization.normalize("stdin.mxml");
-		return formatMXMLTextInternal(filePath, text, problems);
-	}
-
-	public String formatMXMLText(String text) {
-		return formatMXMLText(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 "asformat".
-	 */
-	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>();
-		if (helpVar != null) {
-			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, ProblemQuery problems) {
-		try {
-			Configurator configurator = new Configurator();
-			configurator.setConfiguration(args, "files");
-			configuration = configurator.getConfiguration();
-			configBuffer = configurator.getConfigurationBuffer();
-
-			problems.addAll(configurator.getConfigurationProblems());
-
-			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 || args.length == 0) {
-				processHelp(helpVar);
-				return false;
-			}
-
-			if (problems.hasErrors()) {
-				return false;
-			}
-
-			collapseEmptyBlocks = configuration.getCollapseEmptyBlocks();
-			ignoreProblems = configuration.getIgnoreParsingProblems();
-			insertFinalNewLine = configuration.getInsertFinalNewLine();
-			insertSpaceAfterCommaDelimiter = configuration.getInsertSpaceAfterCommaDelimiter();
-			insertSpaceBetweenMetadataAttributes = configuration.getInsertSpaceBetweenMetadataAttributes();
-			insertSpaceAfterFunctionKeywordForAnonymousFunctions = configuration
-					.getInsertSpaceAfterFunctionKeywordForAnonymousFunctions();
-			insertSpaceAfterKeywordsInControlFlowStatements = configuration
-					.getInsertSpaceAfterKeywordsInControlFlowStatements();
-			insertSpaceAfterSemicolonInForStatements = configuration.getInsertSpaceAfterSemicolonInForStatements();
-			insertSpaceBeforeAndAfterBinaryOperators = configuration.getInsertSpaceBeforeAndAfterBinaryOperators();
-			insertSpaceAtStartOfLineComment = configuration.getInsertSpaceAtStartOfLineComment();
-			insertSpaces = configuration.getInsertSpaces();
-			mxmlInsertNewLineBetweenAttributes = configuration.getMxmlInsertNewLineBetweenAttributes();
-			mxmlAlignAttributes = configuration.getMxmlAlignAttributes();
-			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);
-				}
-				if (inputFile.isDirectory()) {
-					addDirectory(inputFile);
-				} else {
-					inputFiles.add(inputFile);
-				}
-			}
-			if (inputFiles.size() == 0 && listChangedFiles) {
-				throw new ConfigurationException("Cannot use -list-files with standard input", 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;
-		}
-	}
-
-	private void addDirectory(File inputFile) {
-		for (File file : inputFile.listFiles()) {
-			String fileName = file.getName();
-			if (fileName.startsWith(".")) {
-				continue;
-			}
-			if (file.isDirectory()) {
-				addDirectory(file);
-			} else if (fileName.endsWith(".as") || fileName.endsWith(".mxml")) {
-				inputFiles.add(file);
-			}
-		}
-	}
-
-	private String formatMXMLScriptElement(String filePath, int line, String text,
-			Collection<ICompilerProblem> problems) {
-		String indent = "\t";
-		if (insertSpaces) {
-			indent = "";
-			for (int i = 0; i < tabSize; i++) {
-				indent += " ";
-			}
-		}
-		StringBuilder builder = new StringBuilder();
-		Pattern scriptPattern = Pattern.compile(
-				"^<((?:mx|fx):(\\w+))>\\s*(<!\\[CDATA\\[)?((?:.|(?:\\r?\\n))*?)(?:\\]\\]>)?\\s*<\\/(?:mx|fx):(?:\\w+)>$");
-		Matcher scriptMatcher = scriptPattern.matcher(text);
-		if (!scriptMatcher.matches()) {
-			return text;
-		}
-		if (problems == null) {
-			// we need to know if there were problems because it means that we
-			// need to return the original, unformatted text
-			problems = new ArrayList<ICompilerProblem>();
-		}
-		String scriptTagText = scriptMatcher.group(1);
-		String scriptTagName = scriptMatcher.group(2);
-		String cdataText = scriptMatcher.group(3);
-		String scriptText = scriptMatcher.group(4);
-		boolean requireCdata = cdataText != null || "Script".equals(scriptTagName);
-		String formattedScriptText = formatAS3TextInternal(filePath + "@Script[" + line + "]", scriptText, problems);
-		if (!ignoreProblems && hasErrors(problems)) {
-			return text;
-		}
-		if (formattedScriptText.length() > 0) {
-			String[] formattedLines = formattedScriptText.split("\n");
-			String lineIndent = requireCdata ? (indent + indent + indent) : (indent + indent);
-			for (int i = 0; i < formattedLines.length; i++) {
-				formattedLines[i] = lineIndent + formattedLines[i];
-			}
-			formattedScriptText = String.join("\n", formattedLines);
-		}
-		builder.append(indent);
-		builder.append("<");
-		builder.append(scriptTagText);
-		builder.append(">\n");
-		if (requireCdata) {
-			builder.append(indent);
-			builder.append(indent);
-			builder.append("<![CDATA[\n");
-		}
-		if (formattedScriptText.length() > 0) {
-			builder.append(formattedScriptText);
-			builder.append("\n");
-		}
-		if (requireCdata) {
-			builder.append(indent);
-			builder.append(indent);
-			builder.append("]]>\n");
-		}
-		builder.append(indent);
-		builder.append("</");
-		builder.append(scriptTagText);
-		builder.append(">");
-
-		return builder.toString();
+	public ASTokenFormatter(FORMATTER formatter) {
+		super(formatter);
 	}
 
-	private String formatAS3TextInternal(String filePath, String text, Collection<ICompilerProblem> problems) {
+	private int indent;
+	private boolean inCaseOrDefaultClause;
+	private boolean inControlFlowStatement;
+	private boolean inVarOrConstDeclaration;
+	private boolean inFunctionDeclaration;
+	private boolean inPackageDeclaration;
+	private boolean inClassDeclaration;
+	private boolean inInterfaceDeclaration;
+	private boolean blockOpenPending;
+	private boolean indentedStatement;
+	private boolean caseOrDefaultBlockOpenPending;
+	private boolean skipFormatting;
+	private int varOrConstChainLevel;
+	private List<BlockStackItem> blockStack;
+	private int controlFlowParenStack;
+	private int ternaryStack;
+	private int numRequiredNewLines;
+	private boolean requiredSpace;
+	private IASToken prevTokenNotComment;
+	private IASToken prevToken;
+	private IASToken prevTokenOrExtra;
+	private IASToken token;
+	private IASToken nextToken;
+	private IASToken nextTokenOrExtra;
+	private IASToken nextTokenNotComment;
+
+	public String format(String filePath, String text, Collection<ICompilerProblem> problems) {
 		if (problems == null) {
 			problems = new ArrayList<ICompilerProblem>();
 		}
@@ -484,7 +104,7 @@ public class FORMATTER {
 			problems.addAll(tokenizer.getTokenizationProblems());
 		}
 
-		if (!ignoreProblems && hasErrors(problems)) {
+		if (!formatter.ignoreProblems && hasErrors(problems)) {
 			return text;
 		}
 
@@ -522,7 +142,7 @@ public class FORMATTER {
 			problems.addAll(parser.getSyntaxProblems());
 		}
 
-		if (!ignoreProblems && hasErrors(problems)) {
+		if (!formatter.ignoreProblems && hasErrors(problems)) {
 			return text;
 		}
 
@@ -550,7 +170,7 @@ public class FORMATTER {
 
 		List<IASToken> tokens = insertExtraAS3Tokens(repairedTokensList, text);
 		try {
-			return parseTokens(filePath, tokens, node);
+			return parseTokens(tokens);
 		} catch (Exception e) {
 			if (problems != null) {
 				System.err.println(e);
@@ -559,133 +179,35 @@ public class FORMATTER {
 			}
 			return text;
 		}
-
-	}
-
-	private List<IASToken> insertExtraAS3Tokens(List<IASToken> originalTokens, String text) {
-		ArrayList<IASToken> tokens = new ArrayList<IASToken>();
-		IASToken prevToken = null;
-		for (IASToken token : originalTokens) {
-			if (prevToken != null) {
-
-				boolean skipSemicolon = token.getType() == ASTokenTypes.TOKEN_SEMICOLON && token.isImplicit()
-						&& prevToken != null && (prevToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-								|| prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN);
-				if (skipSemicolon) {
-					continue;
-				}
-
-				int start = prevToken.getAbsoluteEnd();
-				int end = token.getAbsoluteStart();
-				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(token.getLine());
-					extraToken.setEndLine(token.getColumn());
-					tokens.add(extraToken);
-				}
-			}
-			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);
-			}
-		}
-		return tokens;
-	}
-
-	private List<IMXMLToken> insertExtraMXMLTokens(IMXMLToken[] originalTokens, String text) {
-		ArrayList<IMXMLToken> tokens = new ArrayList<IMXMLToken>();
-		IMXMLToken prevToken = null;
-		for (IMXMLToken token : originalTokens) {
-			if (prevToken != null) {
-				int start = prevToken.getEnd();
-				int end = token.getStart();
-				if (end > start) {
-					String tokenText = text.substring(start, end);
-					MXMLToken extraToken = new MXMLToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getLine(),
-							prevToken.getColumn() + end - start, tokenText);
-					extraToken.setEndLine(token.getLine());
-					extraToken.setEndLine(token.getColumn());
-					tokens.add(extraToken);
-				}
-			}
-			tokens.add(token);
-			prevToken = token;
-		}
-		if (prevToken != null) {
-			int start = prevToken.getEnd();
-			int end = text.length();
-			if (end > start) {
-				String tokenText = text.substring(start, end);
-				MXMLToken extraToken = new MXMLToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getLine(),
-						prevToken.getColumn() + end - start, tokenText);
-				extraToken.setEndLine(prevToken.getLine());
-				extraToken.setEndLine(prevToken.getColumn());
-				tokens.add(extraToken);
-			}
-		}
-		return tokens;
-	}
-
-	private IASToken getNextTokenSkipExtra(List<IASToken> tokens, int startIndex) {
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IASToken token = tokens.get(i);
-			if (token.getType() != TOKEN_TYPE_EXTRA) {
-				return token;
-			}
-		}
-		return null;
 	}
 
-	private IASToken getNextTokenSkipExtraAndComments(List<IASToken> tokens, int startIndex) {
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IASToken token = tokens.get(i);
-			if (token.getType() != TOKEN_TYPE_EXTRA && token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
-					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
-				return token;
-			}
-		}
-		return null;
-	}
+	private String parseTokens(List<IASToken> tokens) throws Exception {
+		indent = 0;
+		inCaseOrDefaultClause = false;
+		inControlFlowStatement = false;
+		inVarOrConstDeclaration = false;
+		inFunctionDeclaration = false;
+		inPackageDeclaration = false;
+		inClassDeclaration = false;
+		inInterfaceDeclaration = false;
+		blockOpenPending = false;
+		indentedStatement = false;
+		caseOrDefaultBlockOpenPending = false;
+		skipFormatting = false;
+		varOrConstChainLevel = -1;
+		blockStack = new ArrayList<BlockStackItem>();
+		controlFlowParenStack = 0;
+		ternaryStack = 0;
+		numRequiredNewLines = 0;
+		requiredSpace = false;
+		prevTokenNotComment = null;
+		prevToken = null;
+		prevTokenOrExtra = null;
+		token = null;
+		nextToken = null;
+		nextTokenOrExtra = null;
+		nextTokenNotComment = null;
 
-	private String parseTokens(String filePath, List<IASToken> tokens, FileNode node) throws Exception {
-		int indent = 0;
-		boolean inCaseOrDefaultClause = false;
-		boolean inControlFlowStatement = false;
-		boolean inVarOrConstDeclaration = false;
-		boolean inFunctionDeclaration = false;
-		boolean inPackageDeclaration = false;
-		boolean inClassDeclaration = false;
-		boolean inInterfaceDeclaration = false;
-		boolean blockOpenPending = false;
-		boolean indentedStatement = false;
-		boolean caseOrDefaultBlockOpenPending = false;
-		boolean skipFormatting = false;
-		int varOrConstChainLevel = -1;
-		List<BlockStackItem> blockStack = new ArrayList<BlockStackItem>();
-		int controlFlowParenStack = 0;
-		int ternaryStack = 0;
-		int numRequiredNewLines = 0;
-		boolean requiredSpace = false;
-		IASToken prevTokenNotComment = null;
-		IASToken prevToken = null;
-		IASToken prevTokenOrExtra = null;
-		IASToken token = null;
-		IASToken nextToken = null;
-		IASToken nextTokenOrExtra = null;
-		IASToken nextTokenNotComment = null;
 		StringBuilder builder = new StringBuilder();
 		for (int i = 0; i < tokens.size(); i++) {
 			token = tokens.get(i);
@@ -707,7 +229,7 @@ public class FORMATTER {
 						}
 						boolean oneLineBlock = prevToken != null && prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN
 								&& nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-						if (oneLineBlock && collapseEmptyBlocks) {
+						if (oneLineBlock && formatter.collapseEmptyBlocks) {
 							newLinesInExtra = 0;
 						}
 						numRequiredNewLines = Math.max(numRequiredNewLines, newLinesInExtra);
@@ -769,11 +291,11 @@ public class FORMATTER {
 							}
 							boolean oneLineBlock = nextToken != null
 									&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-							boolean needsNewLine = placeOpenBraceOnNewLine && (!collapseEmptyBlocks || !oneLineBlock);
+							boolean needsNewLine = formatter.placeOpenBraceOnNewLine && (!formatter.collapseEmptyBlocks || !oneLineBlock);
 							if (needsNewLine) {
 								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
 							} else {
-								if (oneLineBlock && collapseEmptyBlocks) {
+								if (oneLineBlock && formatter.collapseEmptyBlocks) {
 									numRequiredNewLines = 0;
 								}
 								requiredSpace = true;
@@ -793,7 +315,7 @@ public class FORMATTER {
 							if (stackItem.blockDepth <= 1) {
 								boolean oneLineBlock = prevToken != null
 										&& prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN;
-								if (!collapseEmptyBlocks || !oneLineBlock) {
+								if (!formatter.collapseEmptyBlocks || !oneLineBlock) {
 									indent = decreaseIndent(indent);
 								}
 								if (stackItem.token.getType() == ASTokenTypes.TOKEN_KEYWORD_CASE
@@ -880,7 +402,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT:
 					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT:
 					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT: {
-						if (insertSpaceBeforeAndAfterBinaryOperators) {
+						if (formatter.insertSpaceBeforeAndAfterBinaryOperators) {
 							requiredSpace = true;
 						}
 						break;
@@ -888,7 +410,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_OPERATOR_STAR: {
 						boolean isAnyType = checkTokenBeforeAnyType(prevTokenNotComment);
 						boolean isAnyVectorType = checkTokensForAnyVectorType(prevTokenNotComment, nextTokenNotComment);
-						if (!isAnyType && !isAnyVectorType && insertSpaceBeforeAndAfterBinaryOperators
+						if (!isAnyType && !isAnyVectorType && formatter.insertSpaceBeforeAndAfterBinaryOperators
 								&& !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
@@ -897,21 +419,21 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_OPERATOR_PLUS:
 					case ASTokenTypes.TOKEN_OPERATOR_MINUS: {
 						boolean isUnary = checkTokenBeforeUnaryOperator(prevTokenNotComment);
-						if (!isUnary && insertSpaceBeforeAndAfterBinaryOperators) {
+						if (!isUnary && formatter.insertSpaceBeforeAndAfterBinaryOperators) {
 							requiredSpace = true;
 						}
 						break;
 					}
 					case ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT: {
 						inVarOrConstDeclaration = false;
-						if (insertSpaceBeforeAndAfterBinaryOperators) {
+						if (formatter.insertSpaceBeforeAndAfterBinaryOperators) {
 							requiredSpace = true;
 						}
 						break;
 					}
 					case ASTokenTypes.TOKEN_OPERATOR_TERNARY: {
 						ternaryStack++;
-						if (insertSpaceBeforeAndAfterBinaryOperators) {
+						if (formatter.insertSpaceBeforeAndAfterBinaryOperators) {
 							requiredSpace = true;
 						}
 						break;
@@ -987,7 +509,7 @@ public class FORMATTER {
 					if (blockOpenPending) {
 						boolean oneLineBlock = nextToken != null
 								&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-						if (placeOpenBraceOnNewLine && (!collapseEmptyBlocks || !oneLineBlock)) {
+						if (formatter.placeOpenBraceOnNewLine && (!formatter.collapseEmptyBlocks || !oneLineBlock)) {
 							indent = increaseIndent(indent);
 						}
 					}
@@ -1018,7 +540,7 @@ public class FORMATTER {
 				switch (token.getType()) {
 					case ASTokenTypes.TOKEN_SEMICOLON: {
 						if (inControlFlowStatement && isInForStatement(blockStack)) {
-							if (insertSpaceAfterSemicolonInForStatements) {
+							if (formatter.insertSpaceAfterSemicolonInForStatements) {
 								requiredSpace = true;
 							}
 							// else no space
@@ -1060,8 +582,8 @@ public class FORMATTER {
 							}
 							boolean oneLineBlock = nextToken != null
 									&& nextToken.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE;
-							if (!collapseEmptyBlocks || !oneLineBlock) {
-								if (!placeOpenBraceOnNewLine) {
+							if (!formatter.collapseEmptyBlocks || !oneLineBlock) {
+								if (!formatter.placeOpenBraceOnNewLine) {
 									indent = increaseIndent(indent);
 								}
 								numRequiredNewLines = Math.max(numRequiredNewLines, 1);
@@ -1161,7 +683,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_KEYWORD_FUNCTION: {
 						blockStack.add(new BlockStackItem(token));
 						inFunctionDeclaration = true;
-						boolean skipSpace = !insertSpaceAfterFunctionKeywordForAnonymousFunctions
+						boolean skipSpace = !formatter.insertSpaceAfterFunctionKeywordForAnonymousFunctions
 								&& (nextToken != null && nextToken.getType() == ASTokenTypes.TOKEN_PAREN_OPEN);
 						if (!skipSpace) {
 							requiredSpace = true;
@@ -1182,7 +704,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_KEYWORD_WITH: {
 						inControlFlowStatement = true;
 						blockStack.add(new BlockStackItem(token));
-						if (insertSpaceAfterKeywordsInControlFlowStatements && !skipWhitespaceBeforeSemicolon) {
+						if (formatter.insertSpaceAfterKeywordsInControlFlowStatements && !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
 						break;
@@ -1190,7 +712,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_KEYWORD_SWITCH: {
 						inControlFlowStatement = true;
 						blockStack.add(new SwitchBlockStackItem(token));
-						if (insertSpaceAfterKeywordsInControlFlowStatements && !skipWhitespaceBeforeSemicolon) {
+						if (formatter.insertSpaceAfterKeywordsInControlFlowStatements && !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
 						break;
@@ -1354,7 +876,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT:
 					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT:
 					case ASTokenTypes.TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT: {
-						if (insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeSemicolon) {
+						if (formatter.insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
 						break;
@@ -1362,7 +884,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_OPERATOR_STAR: {
 						boolean isAnyType = checkTokenBeforeAnyType(prevTokenNotComment);
 						boolean isAnyVectorType = checkTokensForAnyVectorType(prevTokenNotComment, nextTokenNotComment);
-						if (!isAnyType && !isAnyVectorType && insertSpaceBeforeAndAfterBinaryOperators
+						if (!isAnyType && !isAnyVectorType && formatter.insertSpaceBeforeAndAfterBinaryOperators
 								&& !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
@@ -1371,7 +893,7 @@ public class FORMATTER {
 					case ASTokenTypes.TOKEN_OPERATOR_PLUS:
 					case ASTokenTypes.TOKEN_OPERATOR_MINUS: {
 						boolean isUnary = checkTokenBeforeUnaryOperator(prevTokenNotComment);
-						if (!isUnary && insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeSemicolon) {
+						if (!isUnary && formatter.insertSpaceBeforeAndAfterBinaryOperators && !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
 						break;
@@ -1380,7 +902,7 @@ public class FORMATTER {
 						if (varOrConstChainLevel == blockStack.size()) {
 							inVarOrConstDeclaration = true;
 						}
-						if (insertSpaceAfterCommaDelimiter && !skipWhitespaceBeforeSemicolon) {
+						if (formatter.insertSpaceAfterCommaDelimiter && !skipWhitespaceBeforeSemicolon) {
 							requiredSpace = true;
 						}
 						break;
@@ -1431,164 +953,29 @@ public class FORMATTER {
 		return builder.toString();
 	}
 
-	private IASToken findTokenAfterBlock(IASToken tokenBlockOpen, List<IASToken> tokens) {
-		List<IASToken> stack = new ArrayList<IASToken>();
-		int startIndex = tokens.indexOf(tokenBlockOpen) + 1;
+	private IASToken getNextTokenSkipExtra(List<IASToken> tokens, int startIndex) {
 		for (int i = startIndex; i < tokens.size(); i++) {
-			IASToken current = tokens.get(i);
-			if (current.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
-				stack.add(current);
-			} else if (current.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE) {
-				if (stack.size() == 0) {
-					return getNextTokenSkipExtraAndComments(tokens, i + 1);
-				}
-				stack.remove(stack.size() - 1);
+			IASToken token = tokens.get(i);
+			if (token.getType() != TOKEN_TYPE_EXTRA) {
+				return token;
 			}
 		}
 		return null;
 	}
 
-	private int countNewLinesInExtra(IASToken tokenOrExtra) {
-		if (tokenOrExtra == null || tokenOrExtra.getType() != TOKEN_TYPE_EXTRA) {
-			return 0;
-		}
-		int numNewLinesInWhitespace = 0;
-		String whitespace = tokenOrExtra.getText();
-		int index = -1;
-		while ((index = whitespace.indexOf('\n', index + 1)) != -1) {
-			numNewLinesInWhitespace++;
-		}
-		return numNewLinesInWhitespace;
-	}
-
-	private String formatSingleLineComment(String comment) {
-		comment = comment.substring(2).trim();
-		StringBuilder builder = new StringBuilder();
-		builder.append("//");
-		if (insertSpaceAtStartOfLineComment) {
-			builder.append(" ");
+	private IASToken getNextTokenSkipExtraAndComments(List<IASToken> tokens, int startIndex) {
+		for (int i = startIndex; i < tokens.size(); i++) {
+			IASToken token = tokens.get(i);
+			if (token.getType() != TOKEN_TYPE_EXTRA && token.getType() != ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
+					&& token.getType() != ASTokenTypes.HIDDEN_TOKEN_MULTI_LINE_COMMENT
+					&& token.getType() != ASTokenTypes.TOKEN_ASDOC_COMMENT) {
+				return token;
+			}
 		}
-		builder.append(comment);
-		return builder.toString();
-	}
-
-	private String formatMultiLineComment(String comment) {
-		return comment;
-	}
-
-	private String formatLiteralString(String string) {
-		String charsToEscape = "\b\t\n\f\r\\";
-		String escapeChars = "btnfr\\";
-		int escapeIndex = -1;
-		char currChar;
-		StringBuilder builder = new StringBuilder();
-		for (int i = 0; i < string.length(); ++i) {
-			currChar = string.charAt(i);
-			if (i == 0) {
-				charsToEscape += currChar;
-				escapeChars += currChar;
-				builder.append(currChar);
-				continue;
-			}
-			if (i == string.length() - 1) {
-				builder.append(currChar);
-				continue;
-			}
-			escapeIndex = charsToEscape.indexOf(currChar);
-			if (escapeIndex != -1) {
-				builder.append("\\");
-				builder.append(escapeChars.charAt(escapeIndex));
-			} else {
-				builder.append(currChar);
-			}
-		}
-		return builder.toString();
-	}
-
-	private boolean isInForStatement(List<BlockStackItem> blockStack) {
-		for (int i = blockStack.size() - 1; i >= 0; i--) {
-			BlockStackItem item = blockStack.get(i);
-			switch (item.token.getType()) {
-				case ASTokenTypes.TOKEN_BLOCK_OPEN:
-				case ASTokenTypes.TOKEN_SQUARE_OPEN:
-				case ASTokenTypes.TOKEN_PAREN_OPEN: {
-					// these tokens are fine, keep searching
-					break;
-				}
-				case ASTokenTypes.TOKEN_KEYWORD_FOR: {
-					return true;
-				}
-				default: {
-					return false;
-				}
-			}
-		}
-		return false;
-	}
-
-	private boolean isInListing(String lineText, boolean alreadyInListing) {
-		int searchIndex = 0;
-		boolean inListing = alreadyInListing;
-		while (searchIndex < lineText.length()) {
-			if (!inListing) {
-				searchIndex = lineText.indexOf("<listing", searchIndex);
-				if (searchIndex == -1) {
-					return false;
-				}
-				searchIndex += 8;
-				inListing = true;
-			}
-			searchIndex = lineText.indexOf("</listing>", searchIndex);
-			if (searchIndex == -1) {
-				return true;
-			}
-			searchIndex += 10;
-			inListing = false;
-		}
-		return inListing;
-	}
-
-	private String formatASDocComment(String comment, int indent) {
-		String[] lines = comment.split("\n");
-		StringBuilder builder = new StringBuilder();
-		String lineText = lines[0].trim();
-		builder.append(lineText);
-		boolean inListing = isInListing(lineText, false);
-		if (lines.length > 1) {
-			builder.append('\n');
-		}
-		String listingIndent = null;
-		for (int i = 1; i < lines.length - 1; i++) {
-			lineText = lines[i].trim();
-			if (inListing) {
-				Matcher startMatcher = ASDOC_START_LINE_PATTERN.matcher(lineText);
-				if (startMatcher.find()) {
-					if (listingIndent == null) {
-						listingIndent = startMatcher.group(1);
-					} else if (startMatcher.group().length() >= lineText.length()) {
-						lineText = "*" + listingIndent;
-					}
-				}
-			}
-			appendIndent(builder, indent);
-			builder.append(' ');
-			builder.append(lineText);
-			builder.append('\n');
-			inListing = isInListing(lineText, inListing);
-			if (!inListing) {
-				listingIndent = null;
-			}
-		}
-		if (lines.length > 1) {
-			appendIndent(builder, indent);
-			builder.append(' ');
-			builder.append(lines[lines.length - 1].trim());
-		}
-		return builder.toString();
+		return null;
 	}
 
 	private String getTokenText(IASToken token, int indent, boolean skipFormatting) {
-
 		if (token instanceof MetaDataPayloadToken) {
 			MetaDataPayloadToken metaPlayloadToken = (MetaDataPayloadToken) token;
 			return formatMetadataToken(metaPlayloadToken);
@@ -1619,8 +1006,8 @@ public class FORMATTER {
 					if (skipFormatting) {
 						return token.isImplicit() ? "" : token.getText();
 					}
-					boolean skipSemicolon = Semicolons.REMOVE.equals(semicolons)
-							|| (Semicolons.IGNORE.equals(semicolons) && token.isImplicit());
+					boolean skipSemicolon = Semicolons.REMOVE.equals(formatter.semicolons)
+							|| (Semicolons.IGNORE.equals(formatter.semicolons) && token.isImplicit());
 					if (!skipSemicolon) {
 						return token.getText();
 					}
@@ -1660,7 +1047,7 @@ public class FORMATTER {
 				case MetadataTokenTypes.TOKEN_ATTR_OPERATOR_NS_QUALIFIER: {
 					if (needsComma) {
 						builder.append(",");
-						if (insertSpaceBetweenMetadataAttributes) {
+						if (formatter.insertSpaceBetweenMetadataAttributes) {
 							builder.append(" ");
 						}
 					}
@@ -1679,7 +1066,7 @@ public class FORMATTER {
 				case MetadataTokenTypes.TOKEN_STRING: {
 					if (needsComma) {
 						builder.append(",");
-						if (insertSpaceBetweenMetadataAttributes) {
+						if (formatter.insertSpaceBetweenMetadataAttributes) {
 							builder.append(" ");
 						}
 					}
@@ -1697,6 +1084,111 @@ public class FORMATTER {
 		return builder.toString();
 	}
 
+	private String formatSingleLineComment(String comment) {
+		comment = comment.substring(2).trim();
+		StringBuilder builder = new StringBuilder();
+		builder.append("//");
+		if (formatter.insertSpaceAtStartOfLineComment) {
+			builder.append(" ");
+		}
+		builder.append(comment);
+		return builder.toString();
+	}
+
+	private String formatMultiLineComment(String comment) {
+		return comment;
+	}
+
+	private String formatLiteralString(String string) {
+		String charsToEscape = "\b\t\n\f\r\\";
+		String escapeChars = "btnfr\\";
+		int escapeIndex = -1;
+		char currChar;
+		StringBuilder builder = new StringBuilder();
+		for (int i = 0; i < string.length(); ++i) {
+			currChar = string.charAt(i);
+			if (i == 0) {
+				charsToEscape += currChar;
+				escapeChars += currChar;
+				builder.append(currChar);
+				continue;
+			}
+			if (i == string.length() - 1) {
+				builder.append(currChar);
+				continue;
+			}
+			escapeIndex = charsToEscape.indexOf(currChar);
+			if (escapeIndex != -1) {
+				builder.append("\\");
+				builder.append(escapeChars.charAt(escapeIndex));
+			} else {
+				builder.append(currChar);
+			}
+		}
+		return builder.toString();
+	}
+
+	private boolean isInListing(String lineText, boolean alreadyInListing) {
+		int searchIndex = 0;
+		boolean inListing = alreadyInListing;
+		while (searchIndex < lineText.length()) {
+			if (!inListing) {
+				searchIndex = lineText.indexOf("<listing", searchIndex);
+				if (searchIndex == -1) {
+					return false;
+				}
+				searchIndex += 8;
+				inListing = true;
+			}
+			searchIndex = lineText.indexOf("</listing>", searchIndex);
+			if (searchIndex == -1) {
+				return true;
+			}
+			searchIndex += 10;
+			inListing = false;
+		}
+		return inListing;
+	}
+
+	private String formatASDocComment(String comment, int indent) {
+		String[] lines = comment.split("\n");
+		StringBuilder builder = new StringBuilder();
+		String lineText = lines[0].trim();
+		builder.append(lineText);
+		boolean inListing = isInListing(lineText, false);
+		if (lines.length > 1) {
+			builder.append('\n');
+		}
+		String listingIndent = null;
+		for (int i = 1; i < lines.length - 1; i++) {
+			lineText = lines[i].trim();
+			if (inListing) {
+				Matcher startMatcher = ASDOC_START_LINE_PATTERN.matcher(lineText);
+				if (startMatcher.find()) {
+					if (listingIndent == null) {
+						listingIndent = startMatcher.group(1);
+					} else if (startMatcher.group().length() >= lineText.length()) {
+						lineText = "*" + listingIndent;
+					}
+				}
+			}
+			appendIndent(builder, indent);
+			builder.append(' ');
+			builder.append(lineText);
+			builder.append('\n');
+			inListing = isInListing(lineText, inListing);
+			if (!inListing) {
+				listingIndent = null;
+			}
+		}
+		if (lines.length > 1) {
+			appendIndent(builder, indent);
+			builder.append(' ');
+			builder.append(lines[lines.length - 1].trim());
+		}
+		return builder.toString();
+	}
+
 	private boolean checkTokenBeforeAnyType(IASToken token) {
 		return token.getType() == ASTokenTypes.TOKEN_COLON;
 	}
@@ -1717,66 +1209,97 @@ public class FORMATTER {
 				|| token.getType() == ASTokenTypes.TOKEN_COLON : (token == null);
 	}
 
-	private int increaseIndent(int indent) {
-		return indent + 1;
-	}
-
-	private int decreaseIndent(int indent) {
-		return Math.max(0, indent - 1);
+	private boolean isInForStatement(List<BlockStackItem> blockStack) {
+		for (int i = blockStack.size() - 1; i >= 0; i--) {
+			BlockStackItem item = blockStack.get(i);
+			switch (item.token.getType()) {
+				case ASTokenTypes.TOKEN_BLOCK_OPEN:
+				case ASTokenTypes.TOKEN_SQUARE_OPEN:
+				case ASTokenTypes.TOKEN_PAREN_OPEN: {
+					// these tokens are fine, keep searching
+					break;
+				}
+				case ASTokenTypes.TOKEN_KEYWORD_FOR: {
+					return true;
+				}
+				default: {
+					return false;
+				}
+			}
+		}
+		return false;
 	}
 
-	private String getIndent() {
-		if (insertSpaces) {
-			String result = "";
-			for (int j = 0; j < tabSize; j++) {
-				result += " ";
+	private IASToken findTokenAfterBlock(IASToken tokenBlockOpen, List<IASToken> tokens) {
+		List<IASToken> stack = new ArrayList<IASToken>();
+		int startIndex = tokens.indexOf(tokenBlockOpen) + 1;
+		for (int i = startIndex; i < tokens.size(); i++) {
+			IASToken current = tokens.get(i);
+			if (current.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN) {
+				stack.add(current);
+			} else if (current.getType() == ASTokenTypes.TOKEN_BLOCK_CLOSE) {
+				if (stack.size() == 0) {
+					return getNextTokenSkipExtraAndComments(tokens, i + 1);
+				}
+				stack.remove(stack.size() - 1);
 			}
-			return result;
 		}
-		return "\t";
+		return null;
 	}
 
-	private String getAttributeIndent(IMXMLToken openTagToken) {
-		if (!mxmlAlignAttributes) {
-			return getIndent();
-		}
-		int indentSize = openTagToken.getText().length() + 1;
-		String result = "";
-		while (indentSize >= tabSize) {
-			result += getIndent();
-			indentSize -= tabSize;
+	private int countNewLinesInExtra(IASToken tokenOrExtra) {
+		if (tokenOrExtra == null || tokenOrExtra.getType() != TOKEN_TYPE_EXTRA) {
+			return 0;
 		}
-		for (int i = 0; i < indentSize; i++) {
-			result += " ";
+		int numNewLinesInWhitespace = 0;
+		String whitespace = tokenOrExtra.getText();
+		int index = -1;
+		while ((index = whitespace.indexOf('\n', index + 1)) != -1) {
+			numNewLinesInWhitespace++;
 		}
-		return result;
+		return numNewLinesInWhitespace;
 	}
 
-	private void appendIndent(StringBuilder builder, int indent) {
-		String indentString = getIndent();
-		for (int i = 0; i < indent; i++) {
-			builder.append(indentString);
-		}
-	}
+	private List<IASToken> insertExtraAS3Tokens(List<IASToken> originalTokens, String text) {
+		ArrayList<IASToken> tokens = new ArrayList<IASToken>();
+		IASToken prevToken = null;
+		for (IASToken token : originalTokens) {
+			if (prevToken != null) {
 
-	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');
-		}
-	}
+				boolean skipSemicolon = token.getType() == ASTokenTypes.TOKEN_SEMICOLON && token.isImplicit()
+						&& prevToken != null && (prevToken.getType() == ASTokenTypes.HIDDEN_TOKEN_SINGLE_LINE_COMMENT
+								|| prevToken.getType() == ASTokenTypes.TOKEN_BLOCK_OPEN);
+				if (skipSemicolon) {
+					continue;
+				}
 
-	private boolean hasErrors(Collection<ICompilerProblem> problems) {
-		CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer(null);
-		for (ICompilerProblem problem : problems) {
-			CompilerProblemSeverity severity = categorizer.getProblemSeverity(problem);
-			if (CompilerProblemSeverity.ERROR.equals(severity)) {
-				return true;
+				int start = prevToken.getAbsoluteEnd();
+				int end = token.getAbsoluteStart();
+				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(token.getLine());
+					extraToken.setEndLine(token.getColumn());
+					tokens.add(extraToken);
+				}
 			}
+			tokens.add(token);
+			prevToken = token;
 		}
-		return false;
+		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);
+			}
+		}
+		return tokens;
 	}
 
 	private static class BlockStackItem {
@@ -1802,323 +1325,4 @@ public class FORMATTER {
 
 		public int clauseCount = 0;
 	}
-
-	private String formatMXMLTextInternal(String filePath, String text, Collection<ICompilerProblem> problems) {
-		if (problems == null) {
-			problems = new ArrayList<ICompilerProblem>();
-		}
-
-		StringReader textReader = new StringReader(text);
-		MXMLTokenizer mxmlTokenizer = new MXMLTokenizer();
-		IMXMLToken[] originalTokens = null;
-		try {
-			originalTokens = mxmlTokenizer.getTokens(textReader);
-		} finally {
-			IOUtils.closeQuietly(textReader);
-			IOUtils.closeQuietly(mxmlTokenizer);
-		}
-
-		if (mxmlTokenizer.hasTokenizationProblems()) {
-			problems.addAll(mxmlTokenizer.getTokenizationProblems());
-		}
-
-		if (!ignoreProblems && hasErrors(problems)) {
-			return text;
-		}
-
-		Pattern scriptStartPattern = Pattern.compile("<((?:mx|fx):(Script|Metadata))");
-
-		List<IMXMLToken> tokens = insertExtraMXMLTokens(originalTokens, text);
-
-		int indent = 0;
-		int numRequiredNewLines = 0;
-		boolean requiredSpace = false;
-		boolean inOpenTag = false;
-		boolean inCloseTag = false;
-		boolean skipFormatting = false;
-		String attributeIndent = "";
-		IMXMLToken prevToken = null;
-		IMXMLToken prevTokenOrExtra = null;
-		IMXMLToken token = null;
-		IMXMLToken nextToken = null;
-		List<ElementStackItem> elementStack = new ArrayList<ElementStackItem>();
-		StringBuilder builder = new StringBuilder();
-		for (int i = 0; i < tokens.size(); i++) {
-			token = tokens.get(i);
-			nextToken = null;
-			if (i < (tokens.size() - 1)) {
-				nextToken = tokens.get(i + 1);
-			}
-			if (token.getType() == TOKEN_TYPE_EXTRA) {
-				if (skipFormatting) {
-					builder.append(token.getText());
-				} else {
-					if (i == (tokens.size() - 1)) {
-						// if the last token is whitespace, include at most one
-						// new line, but strip the rest
-						numRequiredNewLines = Math.min(1, Math.max(0, countNewLinesInExtra(token)));
-						appendNewLines(builder, numRequiredNewLines);
-						break;
-					}
-					numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
-				}
-				prevTokenOrExtra = token;
-				continue;
-			} else if (token.getType() == MXMLTokenTypes.TOKEN_WHITESPACE) {
-				if (skipFormatting) {
-					builder.append(token.getText());
-				} else {
-					if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-						numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
-					} else {
-						// if the parent element contains text, treat whitespace
-						// the same as text, and don't reformat it
-						// text is never reformatted because some components use it
-						// without collapsing whitespace, and developers would be
-						// confused if whitespace that they deliberately added were
-						// to be removed
-						builder.append(token.getText());
-					}
-					if (i == (tokens.size() - 1)) {
-						// if the last token is whitespace, include at most one
-						// new line, but strip the rest
-						numRequiredNewLines = Math.min(1, numRequiredNewLines);
-						appendNewLines(builder, numRequiredNewLines);
-					}
-				}
-				continue;
-			} else if (token.getType() == MXMLTokenTypes.TOKEN_OPEN_TAG_START
-					&& scriptStartPattern.matcher(token.getText()).matches()) {
-
-				if (prevToken != null && numRequiredNewLines > 0) {
-					appendNewLines(builder, numRequiredNewLines);
-				}
-				StringBuilder scriptBuilder = new StringBuilder();
-				scriptBuilder.append(token.getText());
-				boolean inScriptCloseTag = false;
-				while (i < (tokens.size() - 1)) {
-					i++;
-					token = tokens.get(i);
-					scriptBuilder.append(token.getText());
-					if (token.getType() == MXMLTokenTypes.TOKEN_CLOSE_TAG_START) {
-						inScriptCloseTag = true;
-					} else if (inScriptCloseTag && token.getType() == MXMLTokenTypes.TOKEN_TAG_END) {
-						break;
-					}
-				}
-				if (problems == null) {
-					// we need to know if there were problems because it means that we
-					// need to return the original, unformatted text
-					problems = new ArrayList<ICompilerProblem>();
-				}
-				builder.append(formatMXMLScriptElement(filePath, token.getLine(), scriptBuilder.toString(), problems));
-				if (hasErrors(problems)) {
-					return text;
-				}
-				prevToken = token;
-				prevTokenOrExtra = token;
-				requiredSpace = false;
-				numRequiredNewLines = 1;
-				continue;
-			}
-
-			// characters that must appear before the token
-			switch (token.getType()) {
-				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
-					inOpenTag = true;
-					// if the parent contains text, children should be the same
-					boolean containsText = !elementStack.isEmpty()
-							&& elementStack.get(elementStack.size() - 1).containsText;
-					elementStack.add(new ElementStackItem(token, token.getText().substring(1), containsText));
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
-					if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-						indent = decreaseIndent(indent);
-					}
-					inCloseTag = true;
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_NAME: {
-					requiredSpace = true;
-					break;
-				}
-			}
-
-			if (!skipFormatting && prevToken != null) {
-				if (numRequiredNewLines > 0) {
-					appendNewLines(builder, numRequiredNewLines);
-					appendIndent(builder, indent);
-					if (attributeIndent.length() > 0) {
-						builder.append(attributeIndent);
-					}
-				} else if (requiredSpace) {
-					builder.append(' ');
-				}
-			}
-
-			// include the token's own text
-			// no token gets reformatted before being appended
-			// whitespace is the only special case, but that's not handled here
-			builder.append(token.getText());
-
-			// characters that must appear after the token
-			requiredSpace = false;
-			numRequiredNewLines = 0;
-
-			switch (token.getType()) {
-				case MXMLTokenTypes.TOKEN_PROCESSING_INSTRUCTION: {
-					numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
-					if (nextToken != null && nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END
-							&& nextToken.getType() != TOKEN_TYPE_EXTRA) {
-						requiredSpace = true;
-					}
-					if (elementStack.isEmpty()) {
-						// something is very wrong!
-						return text;
-					}
-					String elementName = token.getText().substring(2);
-					ElementStackItem elementItem = elementStack.remove(elementStack.size() - 1);
-					if (!elementName.equals(elementItem.elementName)) {
-						// there's a unclosed tag with a different name somewhere
-						return text;
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
-					if (nextToken != null && nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END) {
-						attributeIndent = getAttributeIndent(token);
-						if (nextToken.getType() != TOKEN_TYPE_EXTRA) {
-							requiredSpace = true;
-						}
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_TAG_END: {
-					if (inOpenTag) {
-						ElementStackItem element = elementStack.get(elementStack.size() - 1);
-						if (!element.containsText) {
-							element.containsText = elementContainsText(tokens, i + 1, element.token);
-						}
-						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-							indent = increaseIndent(indent);
-						}
-					} else {
-						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-						}
-					}
-					inOpenTag = false;
-					attributeIndent = "";
-					inCloseTag = false;
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_EMPTY_TAG_END: {
-					if (inOpenTag) {
-						elementStack.remove(elementStack.size() - 1);
-					} else {
-						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
-							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-						}
-					}
-					inOpenTag = false;
-					// no need to change nested indent after this tag
-					// however, we may need to remove attribute indent
-					attributeIndent = "";
-					// we shouldn't find an empty close tag, but clear flag anyway
-					inCloseTag = false;
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_STRING: {
-					if (inOpenTag && mxmlInsertNewLineBetweenAttributes && nextToken != null
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
-							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END) {
-						numRequiredNewLines = Math.max(numRequiredNewLines, 1);
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_COMMENT: {
-					String tokenText = token.getText();
-					String trimmed = tokenText.substring(4, tokenText.length() - 3).trim();
-					if (!skipFormatting && FORMATTER_TAG_OFF.equals(trimmed)) {
-						skipFormatting = true;
-					} else if (skipFormatting && FORMATTER_TAG_ON.equals(trimmed)) {
-						skipFormatting = false;
-					}
-					break;
-				}
-			}
-
-			prevToken = token;
-			prevTokenOrExtra = token;
-		}
-
-		return builder.toString();
-	}
-
-	private boolean elementContainsText(List<IMXMLToken> tokens, int startIndex, IMXMLToken openTagToken) {
-		ArrayList<IMXMLToken> elementStack = new ArrayList<IMXMLToken>();
-		elementStack.add(openTagToken);
-		for (int i = startIndex; i < tokens.size(); i++) {
-			IMXMLToken token = tokens.get(i);
-			switch (token.getType()) {
-				case MXMLTokenTypes.TOKEN_TEXT: {
-					if (elementStack.size() == 1) {
-						return true;
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
-					elementStack.add(token);
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_EMPTY_TAG_END: {
-					elementStack.remove(elementStack.size() - 1);
-					if (elementStack.size() == 0) {
-						return false;
-					}
-					break;
-				}
-				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
-					elementStack.remove(elementStack.size() - 1);
-					if (elementStack.size() == 0) {
-						return false;
-					}
-					break;
-				}
-			}
-		}
-		return false;
-	}
-
-	private int countNewLinesInExtra(IMXMLToken token) {
-		if (token == null
-				|| (token.getType() != MXMLTokenTypes.TOKEN_WHITESPACE && token.getType() != TOKEN_TYPE_EXTRA)) {
-			return 0;
-		}
-		int numNewLinesInWhitespace = 0;
-		String whitespace = token.getText();
-		int index = -1;
-		while ((index = whitespace.indexOf('\n', index + 1)) != -1) {
-			numNewLinesInWhitespace++;
-		}
-		return numNewLinesInWhitespace;
-	}
-
-	private static class ElementStackItem {
-		public ElementStackItem(IMXMLToken token, String elementName, boolean containsText) {
-			this.token = token;
-			this.elementName = elementName;
-			this.containsText = containsText;
-		}
-
-		public IMXMLToken token;
-		public String elementName;
-		public boolean containsText = false;
-	}
-}
\ No newline at end of file
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/internal/BaseTokenFormatter.java b/formatter/src/main/java/org/apache/royale/formatter/internal/BaseTokenFormatter.java
new file mode 100644
index 000000000..158f64237
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/internal/BaseTokenFormatter.java
@@ -0,0 +1,81 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.internal;
+
+import java.util.Collection;
+
+import org.apache.royale.compiler.clients.problems.CompilerProblemCategorizer;
+import org.apache.royale.compiler.problems.CompilerProblemSeverity;
+import org.apache.royale.compiler.problems.ICompilerProblem;
+import org.apache.royale.formatter.FORMATTER;
+
+public abstract class BaseTokenFormatter {
+	protected FORMATTER formatter;
+
+	protected BaseTokenFormatter(FORMATTER formatter) {
+		this.formatter = formatter;
+	}
+
+	protected boolean hasErrors(Collection<ICompilerProblem> problems) {
+		CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer(null);
+		for (ICompilerProblem problem : problems) {
+			CompilerProblemSeverity severity = categorizer.getProblemSeverity(problem);
+			if (CompilerProblemSeverity.ERROR.equals(severity)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	protected int increaseIndent(int indent) {
+		return indent + 1;
+	}
+
+	protected int decreaseIndent(int indent) {
+		return Math.max(0, indent - 1);
+	}
+
+	protected String getIndent() {
+		if (formatter.insertSpaces) {
+			String result = "";
+			for (int j = 0; j < formatter.tabSize; j++) {
+				result += " ";
+			}
+			return result;
+		}
+		return "\t";
+	}
+
+	protected void appendIndent(StringBuilder builder, int indent) {
+		String indentString = getIndent();
+		for (int i = 0; i < indent; i++) {
+			builder.append(indentString);
+		}
+	}
+
+	protected void appendNewLines(StringBuilder builder, int numRequiredNewLines) {
+		if (formatter.maxPreserveNewLines != 0) {
+			numRequiredNewLines = Math.min(formatter.maxPreserveNewLines, numRequiredNewLines);
+		}
+		for (int j = 0; j < numRequiredNewLines; j++) {
+			builder.append('\n');
+		}
+	}
+}
diff --git a/formatter/src/main/java/org/apache/royale/formatter/internal/MXMLTokenFormatter.java b/formatter/src/main/java/org/apache/royale/formatter/internal/MXMLTokenFormatter.java
new file mode 100644
index 000000000..662a3b798
--- /dev/null
+++ b/formatter/src/main/java/org/apache/royale/formatter/internal/MXMLTokenFormatter.java
@@ -0,0 +1,506 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.internal;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.royale.compiler.internal.parsing.mxml.MXMLToken;
+import org.apache.royale.compiler.internal.parsing.mxml.MXMLTokenizer;
+import org.apache.royale.compiler.parsing.IMXMLToken;
+import org.apache.royale.compiler.parsing.MXMLTokenTypes;
+import org.apache.royale.compiler.problems.ICompilerProblem;
+import org.apache.royale.compiler.problems.UnexpectedExceptionProblem;
+import org.apache.royale.formatter.FORMATTER;
+
+public class MXMLTokenFormatter extends BaseTokenFormatter {
+	private static final int TOKEN_TYPE_EXTRA = 999999;
+	private static final Pattern SCRIPT_START_PATTERN = Pattern.compile("<((?:mx|fx):(Script|Metadata))");
+	private static final String FORMATTER_TAG_OFF = "@formatter:off";
+	private static final String FORMATTER_TAG_ON = "@formatter:on";
+
+	public MXMLTokenFormatter(FORMATTER formatter) {
+		super(formatter);
+	}
+
+	private int indent;
+	private int numRequiredNewLines;
+	private boolean requiredSpace;
+	private boolean inOpenTag;
+	private boolean inCloseTag;
+	private boolean skipFormatting;
+	private String attributeIndent;
+	private IMXMLToken prevToken;
+	private IMXMLToken prevTokenOrExtra;
+	private IMXMLToken token;
+	private IMXMLToken nextToken;
+	private List<ElementStackItem> elementStack;
+
+	public String format(String filePath, String text, Collection<ICompilerProblem> problems) {
+		if (problems == null) {
+			problems = new ArrayList<ICompilerProblem>();
+		}
+
+		StringReader textReader = new StringReader(text);
+		MXMLTokenizer mxmlTokenizer = new MXMLTokenizer();
+		IMXMLToken[] originalTokens = null;
+		try {
+			originalTokens = mxmlTokenizer.getTokens(textReader);
+		} finally {
+			IOUtils.closeQuietly(textReader);
+			IOUtils.closeQuietly(mxmlTokenizer);
+		}
+
+		if (mxmlTokenizer.hasTokenizationProblems()) {
+			problems.addAll(mxmlTokenizer.getTokenizationProblems());
+		}
+
+		if (!formatter.ignoreProblems && hasErrors(problems)) {
+			return text;
+		}
+
+		List<IMXMLToken> tokens = insertExtraMXMLTokens(originalTokens, text);
+		try {
+			return parseTokens(filePath, text, tokens, problems);
+		} catch (Exception e) {
+			if (problems != null) {
+				System.err.println(e);
+				e.printStackTrace(System.err);
+				problems.add(new UnexpectedExceptionProblem(e));
+			}
+			return text;
+		}
+
+	}
+
+	private String parseTokens(String filePath, String text, List<IMXMLToken> tokens, Collection<ICompilerProblem> problems) throws Exception {
+		indent = 0;
+		numRequiredNewLines = 0;
+		requiredSpace = false;
+		inOpenTag = false;
+		inCloseTag = false;
+		skipFormatting = false;
+		attributeIndent = "";
+		prevToken = null;
+		prevTokenOrExtra = null;
+		token = null;
+		nextToken = null;
+		elementStack = new ArrayList<ElementStackItem>();
+
+		StringBuilder builder = new StringBuilder();
+		for (int i = 0; i < tokens.size(); i++) {
+			token = tokens.get(i);
+			nextToken = null;
+			if (i < (tokens.size() - 1)) {
+				nextToken = tokens.get(i + 1);
+			}
+			if (token.getType() == TOKEN_TYPE_EXTRA) {
+				if (skipFormatting) {
+					builder.append(token.getText());
+				} else {
+					if (i == (tokens.size() - 1)) {
+						// if the last token is whitespace, include at most one
+						// new line, but strip the rest
+						numRequiredNewLines = Math.min(1, Math.max(0, countNewLinesInExtra(token)));
+						appendNewLines(builder, numRequiredNewLines);
+						break;
+					}
+					numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
+				}
+				prevTokenOrExtra = token;
+				continue;
+			} else if (token.getType() == MXMLTokenTypes.TOKEN_WHITESPACE) {
+				if (skipFormatting) {
+					builder.append(token.getText());
+				} else {
+					if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
+						numRequiredNewLines = Math.max(numRequiredNewLines, countNewLinesInExtra(token));
+					} else {
+						// if the parent element contains text, treat whitespace
+						// the same as text, and don't reformat it
+						// text is never reformatted because some components use it
+						// without collapsing whitespace, and developers would be
+						// confused if whitespace that they deliberately added were
+						// to be removed
+						builder.append(token.getText());
+					}
+					if (i == (tokens.size() - 1)) {
+						// if the last token is whitespace, include at most one
+						// new line, but strip the rest
+						numRequiredNewLines = Math.min(1, numRequiredNewLines);
+						appendNewLines(builder, numRequiredNewLines);
+					}
+				}
+				continue;
+			} else if (token.getType() == MXMLTokenTypes.TOKEN_OPEN_TAG_START
+					&& SCRIPT_START_PATTERN.matcher(token.getText()).matches()) {
+
+				if (prevToken != null && numRequiredNewLines > 0) {
+					appendNewLines(builder, numRequiredNewLines);
+				}
+				StringBuilder scriptBuilder = new StringBuilder();
+				scriptBuilder.append(token.getText());
+				boolean inScriptCloseTag = false;
+				while (i < (tokens.size() - 1)) {
+					i++;
+					token = tokens.get(i);
+					scriptBuilder.append(token.getText());
+					if (token.getType() == MXMLTokenTypes.TOKEN_CLOSE_TAG_START) {
+						inScriptCloseTag = true;
+					} else if (inScriptCloseTag && token.getType() == MXMLTokenTypes.TOKEN_TAG_END) {
+						break;
+					}
+				}
+				if (problems == null) {
+					// we need to know if there were problems because it means that we
+					// need to return the original, unformatted text
+					problems = new ArrayList<ICompilerProblem>();
+				}
+				builder.append(formatMXMLScriptElement(filePath, token.getLine(), scriptBuilder.toString(), problems));
+				if (hasErrors(problems)) {
+					return text;
+				}
+				prevToken = token;
+				prevTokenOrExtra = token;
+				requiredSpace = false;
+				numRequiredNewLines = 1;
+				continue;
+			}
+
+			// characters that must appear before the token
+			switch (token.getType()) {
+				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
+					inOpenTag = true;
+					// if the parent contains text, children should be the same
+					boolean containsText = !elementStack.isEmpty()
+							&& elementStack.get(elementStack.size() - 1).containsText;
+					elementStack.add(new ElementStackItem(token, token.getText().substring(1), containsText));
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
+					if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
+						indent = decreaseIndent(indent);
+					}
+					inCloseTag = true;
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_NAME: {
+					requiredSpace = true;
+					break;
+				}
+			}
+
+			if (!skipFormatting && prevToken != null) {
+				if (numRequiredNewLines > 0) {
+					appendNewLines(builder, numRequiredNewLines);
+					appendIndent(builder, indent);
+					if (attributeIndent.length() > 0) {
+						builder.append(attributeIndent);
+					}
+				} else if (requiredSpace) {
+					builder.append(' ');
+				}
+			}
+
+			// include the token's own text
+			// no token gets reformatted before being appended
+			// whitespace is the only special case, but that's not handled here
+			builder.append(token.getText());
+
+			// characters that must appear after the token
+			requiredSpace = false;
+			numRequiredNewLines = 0;
+
+			switch (token.getType()) {
+				case MXMLTokenTypes.TOKEN_PROCESSING_INSTRUCTION: {
+					numRequiredNewLines = Math.max(numRequiredNewLines, 1);
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
+					if (nextToken != null && nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
+							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END
+							&& nextToken.getType() != TOKEN_TYPE_EXTRA) {
+						requiredSpace = true;
+					}
+					if (elementStack.isEmpty()) {
+						// something is very wrong!
+						return text;
+					}
+					String elementName = token.getText().substring(2);
+					ElementStackItem elementItem = elementStack.remove(elementStack.size() - 1);
+					if (!elementName.equals(elementItem.elementName)) {
+						// there's a unclosed tag with a different name somewhere
+						return text;
+					}
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
+					if (nextToken != null && nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
+							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END) {
+						attributeIndent = getAttributeIndent(token);
+						if (nextToken.getType() != TOKEN_TYPE_EXTRA) {
+							requiredSpace = true;
+						}
+					}
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_TAG_END: {
+					if (inOpenTag) {
+						ElementStackItem element = elementStack.get(elementStack.size() - 1);
+						if (!element.containsText) {
+							element.containsText = elementContainsText(tokens, i + 1, element.token);
+						}
+						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
+							indent = increaseIndent(indent);
+						}
+					} else {
+						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
+							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
+						}
+					}
+					inOpenTag = false;
+					attributeIndent = "";
+					inCloseTag = false;
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_EMPTY_TAG_END: {
+					if (inOpenTag) {
+						elementStack.remove(elementStack.size() - 1);
+					} else {
+						if (elementStack.isEmpty() || !elementStack.get(elementStack.size() - 1).containsText) {
+							numRequiredNewLines = Math.max(numRequiredNewLines, 1);
+						}
+					}
+					inOpenTag = false;
+					// no need to change nested indent after this tag
+					// however, we may need to remove attribute indent
+					attributeIndent = "";
+					// we shouldn't find an empty close tag, but clear flag anyway
+					inCloseTag = false;
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_STRING: {
+					if (inOpenTag && formatter.mxmlInsertNewLineBetweenAttributes && nextToken != null
+							&& nextToken.getType() != MXMLTokenTypes.TOKEN_TAG_END
+							&& nextToken.getType() != MXMLTokenTypes.TOKEN_EMPTY_TAG_END) {
+						numRequiredNewLines = Math.max(numRequiredNewLines, 1);
+					}
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_COMMENT: {
+					String tokenText = token.getText();
+					String trimmed = tokenText.substring(4, tokenText.length() - 3).trim();
+					if (!skipFormatting && FORMATTER_TAG_OFF.equals(trimmed)) {
+						skipFormatting = true;
+					} else if (skipFormatting && FORMATTER_TAG_ON.equals(trimmed)) {
+						skipFormatting = false;
+					}
+					break;
+				}
+			}
+
+			prevToken = token;
+			prevTokenOrExtra = token;
+		}
+
+		return builder.toString();
+	}
+
+	private String formatMXMLScriptElement(String filePath, int line, String text,
+			Collection<ICompilerProblem> problems) {
+		String indent = "\t";
+		if (formatter.insertSpaces) {
+			indent = "";
+			for (int i = 0; i < formatter.tabSize; i++) {
+				indent += " ";
+			}
+		}
+		StringBuilder builder = new StringBuilder();
+		Pattern scriptPattern = Pattern.compile(
+				"^<((?:mx|fx):(\\w+))>\\s*(<!\\[CDATA\\[)?((?:.|(?:\\r?\\n))*?)(?:\\]\\]>)?\\s*<\\/(?:mx|fx):(?:\\w+)>$");
+		Matcher scriptMatcher = scriptPattern.matcher(text);
+		if (!scriptMatcher.matches()) {
+			return text;
+		}
+		if (problems == null) {
+			// we need to know if there were problems because it means that we
+			// need to return the original, unformatted text
+			problems = new ArrayList<ICompilerProblem>();
+		}
+		String scriptTagText = scriptMatcher.group(1);
+		String scriptTagName = scriptMatcher.group(2);
+		String cdataText = scriptMatcher.group(3);
+		String scriptText = scriptMatcher.group(4);
+		boolean requireCdata = cdataText != null || "Script".equals(scriptTagName);
+		ASTokenFormatter asFormatter = new ASTokenFormatter(formatter);
+		String formattedScriptText = asFormatter.format(filePath + "@Script[" + line + "]", scriptText, problems);
+		if (!formatter.ignoreProblems && hasErrors(problems)) {
+			return text;
+		}
+		if (formattedScriptText.length() > 0) {
+			String[] formattedLines = formattedScriptText.split("\n");
+			String lineIndent = requireCdata ? (indent + indent + indent) : (indent + indent);
+			for (int i = 0; i < formattedLines.length; i++) {
+				formattedLines[i] = lineIndent + formattedLines[i];
+			}
+			formattedScriptText = String.join("\n", formattedLines);
+		}
+		builder.append(indent);
+		builder.append("<");
+		builder.append(scriptTagText);
+		builder.append(">\n");
+		if (requireCdata) {
+			builder.append(indent);
+			builder.append(indent);
+			builder.append("<![CDATA[\n");
+		}
+		if (formattedScriptText.length() > 0) {
+			builder.append(formattedScriptText);
+			builder.append("\n");
+		}
+		if (requireCdata) {
+			builder.append(indent);
+			builder.append(indent);
+			builder.append("]]>\n");
+		}
+		builder.append(indent);
+		builder.append("</");
+		builder.append(scriptTagText);
+		builder.append(">");
+
+		return builder.toString();
+	}
+
+	private boolean elementContainsText(List<IMXMLToken> tokens, int startIndex, IMXMLToken openTagToken) {
+		ArrayList<IMXMLToken> elementStack = new ArrayList<IMXMLToken>();
+		elementStack.add(openTagToken);
+		for (int i = startIndex; i < tokens.size(); i++) {
+			IMXMLToken token = tokens.get(i);
+			switch (token.getType()) {
+				case MXMLTokenTypes.TOKEN_TEXT: {
+					if (elementStack.size() == 1) {
+						return true;
+					}
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_OPEN_TAG_START: {
+					elementStack.add(token);
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_EMPTY_TAG_END: {
+					elementStack.remove(elementStack.size() - 1);
+					if (elementStack.size() == 0) {
+						return false;
+					}
+					break;
+				}
+				case MXMLTokenTypes.TOKEN_CLOSE_TAG_START: {
+					elementStack.remove(elementStack.size() - 1);
+					if (elementStack.size() == 0) {
+						return false;
+					}
+					break;
+				}
+			}
+		}
+		return false;
+	}
+
+	private int countNewLinesInExtra(IMXMLToken token) {
+		if (token == null
+				|| (token.getType() != MXMLTokenTypes.TOKEN_WHITESPACE && token.getType() != TOKEN_TYPE_EXTRA)) {
+			return 0;
+		}
+		int numNewLinesInWhitespace = 0;
+		String whitespace = token.getText();
+		int index = -1;
+		while ((index = whitespace.indexOf('\n', index + 1)) != -1) {
+			numNewLinesInWhitespace++;
+		}
+		return numNewLinesInWhitespace;
+	}
+
+	private String getAttributeIndent(IMXMLToken openTagToken) {
+		if (!formatter.mxmlAlignAttributes) {
+			return getIndent();
+		}
+		int indentSize = openTagToken.getText().length() + 1;
+		String result = "";
+		while (indentSize >= formatter.tabSize) {
+			result += getIndent();
+			indentSize -= formatter.tabSize;
+		}
+		for (int i = 0; i < indentSize; i++) {
+			result += " ";
+		}
+		return result;
+	}
+
+	private List<IMXMLToken> insertExtraMXMLTokens(IMXMLToken[] originalTokens, String text) {
+		ArrayList<IMXMLToken> tokens = new ArrayList<IMXMLToken>();
+		IMXMLToken prevToken = null;
+		for (IMXMLToken token : originalTokens) {
+			if (prevToken != null) {
+				int start = prevToken.getEnd();
+				int end = token.getStart();
+				if (end > start) {
+					String tokenText = text.substring(start, end);
+					MXMLToken extraToken = new MXMLToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getLine(),
+							prevToken.getColumn() + end - start, tokenText);
+					extraToken.setEndLine(token.getLine());
+					extraToken.setEndLine(token.getColumn());
+					tokens.add(extraToken);
+				}
+			}
+			tokens.add(token);
+			prevToken = token;
+		}
+		if (prevToken != null) {
+			int start = prevToken.getEnd();
+			int end = text.length();
+			if (end > start) {
+				String tokenText = text.substring(start, end);
+				MXMLToken extraToken = new MXMLToken(TOKEN_TYPE_EXTRA, start, end, prevToken.getLine(),
+						prevToken.getColumn() + end - start, tokenText);
+				extraToken.setEndLine(prevToken.getLine());
+				extraToken.setEndLine(prevToken.getColumn());
+				tokens.add(extraToken);
+			}
+		}
+		return tokens;
+	}
+
+	private static class ElementStackItem {
+		public ElementStackItem(IMXMLToken token, String elementName, boolean containsText) {
+			this.token = token;
+			this.elementName = elementName;
+			this.containsText = containsText;
+		}
+
+		public IMXMLToken token;
+		public String elementName;
+		public boolean containsText = false;
+	}
+}