package uk.co.badgersinfoil.metaas; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.HashSet; import java.util.Set; import org.asdt.core.internal.antlr.AS3Parser; import uk.co.badgersinfoil.metaas.dom.ASCompilationUnit; import uk.co.badgersinfoil.metaas.impl.ASTASCompilationUnit; import uk.co.badgersinfoil.metaas.impl.ASTIterator; import uk.co.badgersinfoil.metaas.impl.ASTUtils; import uk.co.badgersinfoil.metaas.impl.antlr.ASTDot; import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListToken; import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTree; import junit.framework.Assert; public class CodeMirror { /** * Serialises the given compilation unit to a string, parses it back * to a second compilation unit, and serialises that to a second string, * finally asserting that the first string and the second string of * source code are the same. */ public static ASCompilationUnit assertReflection(ActionScriptFactory fact, ASCompilationUnit unit) { LinkedListTree ast = ((ASTASCompilationUnit)unit).getAST(); saintyCheckTokenStream(ast); saintyCheckStartStopTokens(ast); assertTokenStreamNotDisjoint(ast); StringWriter out = new StringWriter(); ActionScriptWriter writer = fact.newWriter(); try { writer.write(out, unit); } catch (IOException e) { throw new Error(e); } try { ASCompilationUnit unit2 = fact.newParser().parse(new StringReader(out.toString())); LinkedListTree ast2 = ((ASTASCompilationUnit)unit2).getAST(); assertASTMatch(ast, ast2); StringWriter out2 = new StringWriter(); try { writer.write(out2, unit2); } catch (IOException e) { throw new Error(e); } Assert.assertEquals(out.toString(), out2.toString()); return unit2; } catch (SyntaxException e) { Assert.fail(e.toString()+"\n"+out.toString()); return null; // never reached } } public static Set assertTokenStreamNotDisjoint(LinkedListTree ast) { Set tokensFromStartToStop = tokenStreamToSet(ast); for (int i=0; i<ast.getChildCount(); i++) { LinkedListTree child = (LinkedListTree)ast.getChild(i); Set childTokens = assertTokenStreamNotDisjoint(child); Assert.assertTrue("'"+child+"' (child "+i+" of '"+ast+"') had a token stream disjoint with its parent", tokensFromStartToStop.containsAll(childTokens)); } return tokensFromStartToStop; } private static Set tokenStreamToSet(LinkedListTree ast) { Set tokens = new HashSet(); for (LinkedListToken tok = ast.getStartToken(); tok!=null;) { tokens.add(tok); if (tok == ast.getStopToken()) { break; } tok = tok.getNext(); } return tokens; } public static void assertASTMatch(LinkedListTree ast1, LinkedListTree ast2) { String path = pathTo(ast1); ASTUtils.assertAS3TokenTypeIs("At "+path, ast1.getType(), ast2.getType()); if (ast1.getType() == AS3Parser.IDENT) { Assert.assertEquals("At "+path, ast1.getText(), ast2.getText()); } Assert.assertEquals("At "+path+" child count mismatch: "+stringifyFirstLevel(ast1)+" vs. "+stringifyFirstLevel(ast2), ast1.getChildCount(), ast2.getChildCount()); for (int i=0; i<ast1.getChildCount(); i++) { assertASTMatch((LinkedListTree)ast1.getChild(i), (LinkedListTree)ast2.getChild(i)); } } private static String pathTo(LinkedListTree ast) { StringBuffer buff = new StringBuffer(); while (ast != null) { buff.insert(0, ast); ast = ast.getParent(); if (ast != null) { buff.insert(0, "/"); } } return buff.toString(); } private static String stringifyFirstLevel(LinkedListTree ast) { StringBuffer buf = new StringBuffer("("); for (int i=0; i<ast.getChildCount(); i++) { if (i > 0) { buf.append(" "); } buf.append(ast.getChild(i)); } buf.append(")"); return buf.toString(); } /** * Search through the stream for tokens who's 'prev' property doesn't * match the token who's 'last' property we just dereferenced in * the last iteration. * i.e. checks that when prev.next==next, then next.prev==prev too. */ private static void saintyCheckTokenStream(LinkedListTree ast) { LinkedListToken last = null; for (LinkedListToken tok=ast.getStartToken(); tok!=null; tok=tok.getNext()) { if (last != null && last != tok.getPrev()) { Assert.fail("last["+last+"].next=>["+tok+"] but next.prev=>["+tok.getPrev()+"]"); } last = tok; } } private static void saintyCheckStartStopTokens(LinkedListTree ast) { assertStopNotBeforeStart(ast); ASTIterator i = new ASTIterator(ast); while (i.hasNext()) { saintyCheckStartStopTokens(i.next()); } } private static void assertStopNotBeforeStart(LinkedListTree ast) { LinkedListToken start = ast.getStartToken(); LinkedListToken stop = ast.getStopToken(); if (stop == start) return; for (LinkedListToken tok=stop; tok!=null; tok=tok.getNext()) { Assert.assertFalse("Found stopToken preceeding startToken: "+ast+"("+start+" - "+stop+")", tok==start); } } }