blob: 488e60d862184b1414504959248d6072e2cd6b0c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 xored software, Inc.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* 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());
}
}