| /******************************************************************************* |
| * Copyright (c) 2010 xored software, Inc. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * xored software, Inc. - initial API and Implementation (Vladislav Kuzkokov) |
| *******************************************************************************/ |
| package org.eclipse.dltk.javascript.core.dom.rewrite; |
| |
| import org.eclipse.dltk.internal.javascript.core.manipulation.JavascriptManipulationPlugin; |
| import org.eclipse.dltk.javascript.core.dom.ArrayAccessExpression; |
| import org.eclipse.dltk.javascript.core.dom.ArrayLiteral; |
| import org.eclipse.dltk.javascript.core.dom.AttributeIdentifier; |
| import org.eclipse.dltk.javascript.core.dom.BinaryExpression; |
| import org.eclipse.dltk.javascript.core.dom.BlockStatement; |
| import org.eclipse.dltk.javascript.core.dom.BooleanLiteral; |
| import org.eclipse.dltk.javascript.core.dom.BreakStatement; |
| import org.eclipse.dltk.javascript.core.dom.CallExpression; |
| import org.eclipse.dltk.javascript.core.dom.CaseClause; |
| import org.eclipse.dltk.javascript.core.dom.CatchClause; |
| import org.eclipse.dltk.javascript.core.dom.Comment; |
| import org.eclipse.dltk.javascript.core.dom.ConditionalExpression; |
| import org.eclipse.dltk.javascript.core.dom.ConstStatement; |
| import org.eclipse.dltk.javascript.core.dom.ContinueStatement; |
| import org.eclipse.dltk.javascript.core.dom.DefaultClause; |
| import org.eclipse.dltk.javascript.core.dom.DefaultXmlNamespaceStatement; |
| import org.eclipse.dltk.javascript.core.dom.DescendantAccessExpression; |
| import org.eclipse.dltk.javascript.core.dom.DoStatement; |
| import org.eclipse.dltk.javascript.core.dom.Elision; |
| import org.eclipse.dltk.javascript.core.dom.EmptyStatement; |
| import org.eclipse.dltk.javascript.core.dom.Expression; |
| import org.eclipse.dltk.javascript.core.dom.ExpressionSelector; |
| import org.eclipse.dltk.javascript.core.dom.ExpressionStatement; |
| import org.eclipse.dltk.javascript.core.dom.FilterExpression; |
| import org.eclipse.dltk.javascript.core.dom.FinallyClause; |
| import org.eclipse.dltk.javascript.core.dom.ForEachInStatement; |
| import org.eclipse.dltk.javascript.core.dom.ForInStatement; |
| import org.eclipse.dltk.javascript.core.dom.ForStatement; |
| import org.eclipse.dltk.javascript.core.dom.FunctionExpression; |
| import org.eclipse.dltk.javascript.core.dom.GetterAssignment; |
| import org.eclipse.dltk.javascript.core.dom.IArrayElement; |
| import org.eclipse.dltk.javascript.core.dom.Identifier; |
| import org.eclipse.dltk.javascript.core.dom.IfStatement; |
| import org.eclipse.dltk.javascript.core.dom.Label; |
| import org.eclipse.dltk.javascript.core.dom.LabeledStatement; |
| import org.eclipse.dltk.javascript.core.dom.NewExpression; |
| import org.eclipse.dltk.javascript.core.dom.Node; |
| import org.eclipse.dltk.javascript.core.dom.NullLiteral; |
| import org.eclipse.dltk.javascript.core.dom.NumericLiteral; |
| import org.eclipse.dltk.javascript.core.dom.ObjectLiteral; |
| import org.eclipse.dltk.javascript.core.dom.Parameter; |
| import org.eclipse.dltk.javascript.core.dom.ParenthesizedExpression; |
| import org.eclipse.dltk.javascript.core.dom.PropertyAccessExpression; |
| import org.eclipse.dltk.javascript.core.dom.PropertyAssignment; |
| import org.eclipse.dltk.javascript.core.dom.QualifiedIdentifier; |
| import org.eclipse.dltk.javascript.core.dom.RegularExpressionLiteral; |
| import org.eclipse.dltk.javascript.core.dom.ReturnStatement; |
| import org.eclipse.dltk.javascript.core.dom.SetterAssignment; |
| import org.eclipse.dltk.javascript.core.dom.SimplePropertyAssignment; |
| import org.eclipse.dltk.javascript.core.dom.Source; |
| import org.eclipse.dltk.javascript.core.dom.Statement; |
| import org.eclipse.dltk.javascript.core.dom.StringLiteral; |
| import org.eclipse.dltk.javascript.core.dom.SwitchElement; |
| import org.eclipse.dltk.javascript.core.dom.SwitchStatement; |
| import org.eclipse.dltk.javascript.core.dom.ThisExpression; |
| import org.eclipse.dltk.javascript.core.dom.ThrowStatement; |
| import org.eclipse.dltk.javascript.core.dom.TryStatement; |
| import org.eclipse.dltk.javascript.core.dom.UnaryExpression; |
| import org.eclipse.dltk.javascript.core.dom.UnaryOperator; |
| import org.eclipse.dltk.javascript.core.dom.VariableDeclaration; |
| import org.eclipse.dltk.javascript.core.dom.VariableReference; |
| import org.eclipse.dltk.javascript.core.dom.VariableStatement; |
| import org.eclipse.dltk.javascript.core.dom.WhileStatement; |
| import org.eclipse.dltk.javascript.core.dom.WildcardIdentifier; |
| import org.eclipse.dltk.javascript.core.dom.WithStatement; |
| import org.eclipse.dltk.javascript.core.dom.XmlExpressionFragment; |
| import org.eclipse.dltk.javascript.core.dom.XmlFragment; |
| import org.eclipse.dltk.javascript.core.dom.XmlInitializer; |
| import org.eclipse.dltk.javascript.core.dom.XmlTextFragment; |
| import org.eclipse.dltk.javascript.core.dom.util.DomSwitch; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.change.ChangeDescription; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.text.edits.TextEdit; |
| |
| public class Generator extends DomSwitch<StringBuilder> { |
| private final StringBuilder sb = new StringBuilder(); |
| private final ChangeDescription cd; |
| private final String text; |
| private final String lineDelimiter; |
| private String nlStr; |
| |
| public Generator(ChangeDescription cd, String text, int pos, |
| String lineDelimiter) { |
| this.cd = cd; |
| this.text = text; |
| this.lineDelimiter = lineDelimiter; |
| int st, end = pos; |
| for (st = pos - 1; st >= 0; st--) { |
| try { |
| char c = text.charAt(st); |
| if (c == '\n' || c == '\r') |
| break; |
| if (c != ' ' && c != '\t') |
| end = st; |
| } catch (RuntimeException e) { |
| System.err.println("OK"); |
| } |
| } |
| nlStr = lineDelimiter + text.substring(st + 1, end); |
| } |
| |
| public StringBuilder generate(Node node) { |
| if (node.getBegin() != -1 && cd != null) { |
| RewriteAnalyzer ra = new RewriteAnalyzer(cd, text, lineDelimiter); |
| ra.rewrite(node); |
| Document doc = new Document(text.substring(node.getBegin(), |
| node.getEnd())); |
| try { |
| TextEdit edit = ra.getEdit(); |
| if (edit.hasChildren()) |
| edit.moveTree(-node.getBegin()); |
| edit.apply(doc); |
| String res = doc.get(); |
| if (res.contains(lineDelimiter)) { |
| int st, end = node.getBegin(); |
| for (st = node.getBegin() - 1; st >= 0; st--) { |
| char c = text.charAt(st); |
| if (c == '\n' || c == '\r') |
| break; |
| if (c != ' ' && c != '\t') |
| end = st; |
| } |
| String nl = lineDelimiter + text.substring(st + 1, end); |
| sb.append(res.replace(nl, nlStr)); |
| } else |
| sb.append(res); |
| } catch (BadLocationException e) { |
| JavascriptManipulationPlugin.log(e); |
| } |
| return sb; |
| } |
| doSwitch(node); |
| return sb; |
| } |
| |
| public Generator append(String s) { |
| sb.append(s); |
| return this; |
| } |
| |
| public String toString() { |
| return sb.toString(); |
| } |
| |
| private void indent() { |
| nlStr = nlStr + '\t'; |
| } |
| |
| private void unindent() { |
| nlStr = nlStr.substring(0, nlStr.length() - 1); |
| } |
| |
| public String getIndentation() { |
| return nlStr; |
| } |
| |
| public void newLine() { |
| sb.append(nlStr); |
| } |
| |
| @Override |
| public StringBuilder caseIdentifier(Identifier object) { |
| return sb.append(object.getName()); |
| } |
| |
| @Override |
| public StringBuilder caseVariableReference(VariableReference object) { |
| return sb.append(object.getVariable().getName()); |
| } |
| |
| @Override |
| public StringBuilder caseLabel(Label object) { |
| return sb.append(object.getName()); |
| } |
| |
| @Override |
| public StringBuilder caseNullLiteral(NullLiteral object) { |
| return sb.append("null"); |
| } |
| |
| @Override |
| public StringBuilder caseBooleanLiteral(BooleanLiteral object) { |
| return sb.append(object.getText()); |
| } |
| |
| @Override |
| public StringBuilder caseNumericLiteral(NumericLiteral object) { |
| return sb.append(object.getText()); |
| } |
| |
| @Override |
| public StringBuilder caseStringLiteral(StringLiteral object) { |
| return sb.append(object.getText()); |
| } |
| |
| @Override |
| public StringBuilder caseRegularExpressionLiteral( |
| RegularExpressionLiteral object) { |
| return sb.append(object.getText()); |
| } |
| |
| @Override |
| public StringBuilder caseThisExpression(ThisExpression object) { |
| return sb.append("this"); |
| } |
| |
| @Override |
| public StringBuilder caseArrayLiteral(ArrayLiteral object) { |
| sb.append('['); |
| boolean first = true; |
| for (IArrayElement elem : object.getElements()) { |
| if (!first) |
| sb.append(','); |
| generate(elem); |
| first = false; |
| } |
| return sb.append(']'); |
| } |
| |
| @Override |
| public StringBuilder caseElision(Elision object) { |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseObjectLiteral(ObjectLiteral object) { |
| sb.append('{').append(nlStr); |
| boolean first = true; |
| indent(); |
| for (PropertyAssignment pa : object.getProperties()) { |
| if (!first) |
| sb.append(',').append(nlStr); |
| generate(pa); |
| first = false; |
| } |
| unindent(); |
| return sb.append(nlStr).append('}'); |
| } |
| |
| @Override |
| public StringBuilder caseSimplePropertyAssignment( |
| SimplePropertyAssignment object) { |
| generate(object.getName()); |
| sb.append(':'); |
| return generate(object.getInitializer()); |
| } |
| |
| private void generateStatements(EList<Statement> list) { |
| indent(); |
| for (int i = 0; i < list.size(); i++) { |
| generate(list.get(i)); |
| if (i == list.size() - 1) |
| unindent(); |
| newLine(); |
| } |
| } |
| |
| @Override |
| public StringBuilder caseGetterAssignment(GetterAssignment object) { |
| sb.append("get "); |
| generate(object.getName()); |
| generate(object.getBody()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseSetterAssignment(SetterAssignment object) { |
| sb.append("set "); |
| generate(object.getName()); |
| sb.append('(').append(object.getParameter().getName()).append(") "); |
| generate(object.getBody()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseParenthesizedExpression( |
| ParenthesizedExpression object) { |
| sb.append('('); |
| generate(object.getEnclosed()); |
| return sb.append(')'); |
| } |
| |
| @Override |
| public StringBuilder caseArrayAccessExpression(ArrayAccessExpression object) { |
| generate(object.getArray()); |
| sb.append('['); |
| generate(object.getIndex()); |
| return sb.append(']'); |
| } |
| |
| @Override |
| public StringBuilder casePropertyAccessExpression( |
| PropertyAccessExpression object) { |
| generate(object.getObject()); |
| sb.append('.'); |
| return generate(object.getProperty()); |
| } |
| |
| private void generateExpressions(EList<Expression> list) { |
| boolean first = true; |
| for (Expression expr : list) { |
| if (!first) |
| sb.append(','); |
| generate(expr); |
| first = false; |
| } |
| } |
| |
| @Override |
| public StringBuilder caseNewExpression(NewExpression object) { |
| sb.append("new "); |
| generate(object.getConstructor()); |
| sb.append('('); |
| generateExpressions(object.getArguments()); |
| return sb.append(')'); |
| } |
| |
| @Override |
| public StringBuilder caseCallExpression(CallExpression object) { |
| generate(object.getApplicant()); |
| sb.append('('); |
| generateExpressions(object.getArguments()); |
| return sb.append(')'); |
| } |
| |
| @Override |
| public StringBuilder caseUnaryExpression(UnaryExpression object) { |
| UnaryOperator op = object.getOperation(); |
| boolean postfix = RewriteAnalyzer.isPostfix(op); |
| boolean text = RewriteAnalyzer.isTextUnary(op); |
| if (!postfix) |
| sb.append(object.getOperation().toString()); |
| if (text) |
| sb.append(' '); |
| generate(object.getArgument()); |
| if (postfix) |
| sb.append(object.getOperation().toString()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseBinaryExpression(BinaryExpression object) { |
| generate(object.getLeft()); |
| boolean text = RewriteAnalyzer.isTextBinary(object.getOperation()); |
| if (text) |
| sb.append(' '); |
| sb.append(object.getOperation().toString()); |
| if (text) |
| sb.append(' '); |
| generate(object.getRight()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseConditionalExpression(ConditionalExpression object) { |
| generate(object.getPredicate()); |
| sb.append('?'); |
| generate(object.getConsequent()); |
| sb.append(':'); |
| return generate(object.getAlternative()); |
| } |
| |
| @Override |
| public StringBuilder caseBlockStatement(BlockStatement object) { |
| indent(); |
| sb.append('{').append(nlStr); |
| unindent(); |
| generateStatements(object.getStatements()); |
| sb.append('}'); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseVariableStatement(VariableStatement object) { |
| sb.append("var "); |
| boolean first = true; |
| for (VariableDeclaration decl : object.getDeclarations()) { |
| if (!first) |
| sb.append(','); |
| generate(decl); |
| first = false; |
| } |
| sb.append(';'); // TODO (alex) conditionally add semicolons for other |
| // statements; |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseConstStatement(ConstStatement object) { |
| sb.append("const "); |
| boolean first = true; |
| for (VariableDeclaration decl : object.getDeclarations()) { |
| if (!first) |
| sb.append(','); |
| generate(decl); |
| first = false; |
| } |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseVariableDeclaration(VariableDeclaration object) { |
| sb.append(object.getIdentifier().getName()); |
| if (object.getInitializer() != null) { |
| sb.append('='); |
| generate(object.getInitializer()); |
| } |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseEmptyStatement(EmptyStatement object) { |
| return sb.append(';'); |
| } |
| |
| @Override |
| public StringBuilder caseExpressionStatement(ExpressionStatement object) { |
| return generate(object.getExpression()); |
| } |
| |
| @Override |
| public StringBuilder caseIfStatement(IfStatement object) { |
| sb.append("if ("); |
| generate(object.getPredicate()); |
| sb.append(") "); |
| generate(object.getConsequent()); |
| if (object.getAlternative() != null) { |
| sb.append(" else "); |
| generate(object.getAlternative()); |
| } |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseDoStatement(DoStatement object) { |
| sb.append("do "); |
| generate(object.getBody()); |
| sb.append("while ("); |
| generate(object.getCondition()); |
| return sb.append(')'); |
| } |
| |
| @Override |
| public StringBuilder caseWhileStatement(WhileStatement object) { |
| sb.append("while ("); |
| generate(object.getCondition()); |
| sb.append(") "); |
| return generate(object.getBody()); |
| } |
| |
| @Override |
| public StringBuilder caseForStatement(ForStatement object) { |
| sb.append("for ("); |
| if (object.getInitialization() != null) |
| generate(object.getInitialization()); |
| sb.append(';'); |
| if (object.getCondition() != null) |
| generate(object.getCondition()); |
| sb.append(';'); |
| if (object.getIncrement() != null) |
| generate(object.getIncrement()); |
| sb.append(") "); |
| return generate(object.getBody()); |
| } |
| |
| @Override |
| public StringBuilder caseForInStatement(ForInStatement object) { |
| sb.append("for ("); |
| generate(object.getItem()); |
| sb.append(" in "); |
| generate(object.getCollection()); |
| sb.append(") "); |
| return generate(object.getBody()); |
| } |
| |
| @Override |
| public StringBuilder caseForEachInStatement(ForEachInStatement object) { |
| sb.append("for each("); |
| generate(object.getItem()); |
| sb.append(" in "); |
| generate(object.getCollection()); |
| sb.append(") "); |
| return generate(object.getBody()); |
| } |
| |
| @Override |
| public StringBuilder caseContinueStatement(ContinueStatement object) { |
| sb.append("continue"); |
| if (object.getLabel() != null) |
| sb.append(' ').append(object.getLabel().getName()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseBreakStatement(BreakStatement object) { |
| sb.append("break"); |
| if (object.getLabel() != null) |
| sb.append(' ').append(object.getLabel().getName()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseReturnStatement(ReturnStatement object) { |
| sb.append("return"); |
| if (object.getExpression() != null) { |
| sb.append(' '); |
| generate(object.getExpression()); |
| } |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseWithStatement(WithStatement object) { |
| sb.append("with ("); |
| generate(object.getExpression()); |
| sb.append(") "); |
| return generate(object.getStatement()); |
| } |
| |
| @Override |
| public StringBuilder caseSwitchStatement(SwitchStatement object) { |
| sb.append("switch ("); |
| generate(object.getSelector()); |
| sb.append(") {"); |
| for (SwitchElement se : object.getElements()) |
| generate(se); |
| return sb.append('}'); |
| } |
| |
| @Override |
| public StringBuilder caseCaseClause(CaseClause object) { |
| sb.append("case "); |
| generate(object.getExpression()); |
| sb.append(':').append(nlStr); |
| generateStatements(object.getStatements()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseDefaultClause(DefaultClause object) { |
| sb.append("default:").append(nlStr); |
| generateStatements(object.getStatements()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseLabeledStatement(LabeledStatement object) { |
| sb.append(object.getLabel().getName()).append(": "); |
| return generate(object.getStatement()); |
| } |
| |
| @Override |
| public StringBuilder caseThrowStatement(ThrowStatement object) { |
| sb.append("throw "); |
| return generate(object.getException()); |
| } |
| |
| @Override |
| public StringBuilder caseTryStatement(TryStatement object) { |
| sb.append("try "); |
| generate(object.getBody()); |
| for (CatchClause cc : object.getCatches()) { |
| sb.append(' '); |
| generate(cc); |
| } |
| if (object.getFinallyClause() != null) { |
| sb.append(' '); |
| generate(object.getFinallyClause()); |
| } |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseCatchClause(CatchClause object) { |
| sb.append("catch ("); |
| generate(object.getException()); |
| if (object.getFilter() != null) { |
| sb.append(" if "); |
| generate(object.getFilter()); |
| } |
| sb.append(") "); |
| return generate(object.getBody()); |
| } |
| |
| @Override |
| public StringBuilder caseFinallyClause(FinallyClause object) { |
| sb.append("finally "); |
| return generate(object.getBody()); |
| } |
| |
| @Override |
| public StringBuilder caseFunctionExpression(FunctionExpression object) { |
| if (object.getDocumentation() != null) |
| sb.append(object.getDocumentation().getText()); |
| sb.append("function "); |
| if (object.getIdentifier() != null) |
| sb.append(object.getIdentifier().getName()); |
| sb.append('('); |
| boolean first = true; |
| for (Parameter prm : object.getParameters()) { |
| if (!first) |
| sb.append(','); |
| generate(prm); |
| first = false; |
| } |
| sb.append(')'); |
| sb.append(' '); |
| generate(object.getBody()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseSource(Source object) { |
| generateStatements(object.getStatements()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseXmlInitializer(XmlInitializer object) { |
| for (XmlFragment fragment : object.getFragments()) |
| generate(fragment); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseAttributeIdentifier(AttributeIdentifier object) { |
| sb.append('@'); |
| return generate(object.getSelector()); |
| } |
| |
| @Override |
| public StringBuilder caseQualifiedIdentifier(QualifiedIdentifier object) { |
| generate(object.getNamespace()); |
| sb.append("::"); |
| return generate(object.getMember()); |
| } |
| |
| @Override |
| public StringBuilder caseWildcardIdentifier(WildcardIdentifier object) { |
| return sb.append('*'); |
| } |
| |
| @Override |
| public StringBuilder caseExpressionSelector(ExpressionSelector object) { |
| sb.append('['); |
| generate(object.getIndex()); |
| return sb.append(']'); |
| } |
| |
| @Override |
| public StringBuilder caseXmlTextFragment(XmlTextFragment object) { |
| return sb.append(object.getText()); |
| } |
| |
| @Override |
| public StringBuilder caseXmlExpressionFragment(XmlExpressionFragment object) { |
| sb.append('{'); |
| generate(object.getExpression()); |
| return sb.append('}'); |
| } |
| |
| @Override |
| public StringBuilder caseDescendantAccessExpression( |
| DescendantAccessExpression object) { |
| generate(object.getObject()); |
| sb.append(".."); |
| return generate(object.getProperty()); |
| } |
| |
| @Override |
| public StringBuilder caseFilterExpression(FilterExpression object) { |
| generate(object.getObject()); |
| sb.append(".("); |
| generate(object.getFilter()); |
| return sb.append(')'); |
| } |
| |
| @Override |
| public StringBuilder caseDefaultXmlNamespaceStatement( |
| DefaultXmlNamespaceStatement object) { |
| sb.append("default xml namespace = "); |
| return generate(object.getExpression()); |
| } |
| |
| @Override |
| public StringBuilder caseParameter(Parameter object) { |
| sb.append(object.getName().getName()); |
| return sb; |
| } |
| |
| @Override |
| public StringBuilder caseComment(Comment object) { |
| return sb.append(object.getText()); |
| } |
| } |