Package org.stjs.generator.javascript.rhino

Source Code of org.stjs.generator.javascript.rhino.RhinoJavaScriptWriter

package org.stjs.generator.javascript.rhino;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.List;

import org.mozilla.javascript.Node;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Token;
import org.mozilla.javascript.ast.ArrayLiteral;
import org.mozilla.javascript.ast.Assignment;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.Block;
import org.mozilla.javascript.ast.BreakStatement;
import org.mozilla.javascript.ast.CatchClause;
import org.mozilla.javascript.ast.ConditionalExpression;
import org.mozilla.javascript.ast.ContinueStatement;
import org.mozilla.javascript.ast.DoLoop;
import org.mozilla.javascript.ast.ElementGet;
import org.mozilla.javascript.ast.EmptyExpression;
import org.mozilla.javascript.ast.EmptyStatement;
import org.mozilla.javascript.ast.ExpressionStatement;
import org.mozilla.javascript.ast.ForInLoop;
import org.mozilla.javascript.ast.ForLoop;
import org.mozilla.javascript.ast.FunctionCall;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.ast.IfStatement;
import org.mozilla.javascript.ast.InfixExpression;
import org.mozilla.javascript.ast.KeywordLiteral;
import org.mozilla.javascript.ast.Label;
import org.mozilla.javascript.ast.LabeledStatement;
import org.mozilla.javascript.ast.Name;
import org.mozilla.javascript.ast.NewExpression;
import org.mozilla.javascript.ast.NumberLiteral;
import org.mozilla.javascript.ast.ObjectLiteral;
import org.mozilla.javascript.ast.ObjectProperty;
import org.mozilla.javascript.ast.ParenthesizedExpression;
import org.mozilla.javascript.ast.PropertyGet;
import org.mozilla.javascript.ast.ReturnStatement;
import org.mozilla.javascript.ast.StringLiteral;
import org.mozilla.javascript.ast.SwitchCase;
import org.mozilla.javascript.ast.SwitchStatement;
import org.mozilla.javascript.ast.ThrowStatement;
import org.mozilla.javascript.ast.TryStatement;
import org.mozilla.javascript.ast.UnaryExpression;
import org.mozilla.javascript.ast.VariableDeclaration;
import org.mozilla.javascript.ast.VariableInitializer;
import org.mozilla.javascript.ast.WhileLoop;
import org.stjs.generator.STJSRuntimeException;

import com.google.debugging.sourcemap.FilePosition;
import com.google.debugging.sourcemap.SourceMapFormat;
import com.google.debugging.sourcemap.SourceMapGenerator;
import com.google.debugging.sourcemap.SourceMapGeneratorFactory;

/**
* This class visits a JavaScript AST tree and generate the corresponding source code. It handles also the source maps.
*
* @author acraciun
*/
@SuppressWarnings("PMD.ExcessivePublicCount")
public class RhinoJavaScriptWriter implements AstVisitor<Boolean> {
  private static final String LINE_JAVA_DOC = " * ";
  private static final String INDENT = "    ";
  private static final String START_JAVA_DOC = "/**";
  private static final String END_JAVA_DOC = " */";
  private int level;

  private boolean indented;

  private final Writer writer;

  private int currentLine;
  private int currentColumn;
  private final RhinoNodeVisitorSupport visitorSupport = new RhinoNodeVisitorSupport();

  private final SourceMapGenerator sourceMapGenerator;
  private final boolean generateSourceMap;
  private final File inputFile;

  private FilePosition javaPosition;
  private FilePosition javaScriptPosition;

  public RhinoJavaScriptWriter(Writer writer, File inputFile, boolean generateSourceMap) {
    this.writer = writer;
    this.inputFile = inputFile;
    this.generateSourceMap = generateSourceMap;
    this.sourceMapGenerator = generateSourceMap ? SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3) : null;
  }

  protected RhinoJavaScriptWriter indent() {
    level++;
    return this;
  }

  protected RhinoJavaScriptWriter unindent() {
    level--;
    return this;
  }

  protected void makeIndent() {
    for (int i = 0; i < level; i++) {
      try {
        writer.append(INDENT);
      }
      catch (IOException e) {
        throw new STJSRuntimeException("Writing problem:" + e, e);
      }
      currentColumn += INDENT.length();
    }
  }

  protected RhinoJavaScriptWriter print(String arg) {
    if (!indented) {
      makeIndent();
      indented = true;
    }
    try {
      writer.append(arg);
    }
    catch (IOException e) {
      throw new STJSRuntimeException("Writing problem:" + e, e);
    }
    // TODO check for newlines in the string
    currentColumn += arg.length();
    return this;
  }

  protected RhinoJavaScriptWriter printComments(AstNode node) {
    String comment = node.getJsDoc();
    if (comment != null) {
      println(START_JAVA_DOC);
      String[] lines = comment.split("\n");
      for (String line : lines) {
        print(LINE_JAVA_DOC).println(line);
      }
      println(END_JAVA_DOC);
    }
    return this;
  }

  public RhinoJavaScriptWriter println(String arg) {
    print(arg);
    println();
    return this;
  }

  public RhinoJavaScriptWriter println() {
    try {
      writer.append('\n');
    }
    catch (IOException e) {
      throw new STJSRuntimeException("Writing problem:" + e, e);
    }
    indented = false;
    currentLine++;
    currentColumn = 0;
    addMapping();
    return this;
  }

  protected void startPosition(AstNode node) {
    if (generateSourceMap) {
      javaPosition = new FilePosition(RhinoJavaScriptBuilder.getLineNumber(node) - 1, RhinoJavaScriptBuilder.getColumnNumber(node) - 1);
      javaScriptPosition = new FilePosition(currentLine, currentColumn);
    }
  }

  /**
   * this is only for statements on several lines to be able to catch end of inline function defintions
   *
   * @param node
   */
  protected void endPosition(AstNode node) {
    if (generateSourceMap) {
      boolean hasPosition = javaScriptPosition != null && javaScriptPosition.getLine() != currentLine;
      if (hasPosition) {
        javaPosition = new FilePosition(RhinoJavaScriptBuilder.getEndLineNumber(node) - 1,
            RhinoJavaScriptBuilder.getEndColumnNumber(node) - 1);
        javaScriptPosition = new FilePosition(currentLine, currentColumn);
      }
    }
  }

  protected void addMapping() {
    if (generateSourceMap) {
      FilePosition endJavaScriptPosition = new FilePosition(currentLine, currentColumn);
      if (javaPosition != null && javaPosition.getLine() >= 0 && javaPosition.getColumn() >= 0) {
        sourceMapGenerator.addMapping(inputFile.getName(), null, javaPosition, javaScriptPosition, endJavaScriptPosition);
        javaPosition = null;
      }
    }
  }

  @Override
  public void visitArrayLiteral(ArrayLiteral a, Boolean param) {
    print("[");
    if (a.getElements() != null) {
      printList(a.getElements(), param);
    }
    print("]");
  }

  protected <T extends AstNode> void printList(List<T> items, Boolean param) {
    int max = items.size();
    int count = 0;
    for (AstNode item : items) {
      visitorSupport.accept(item, this, param);
      if (count < max - 1) {
        count++;
        print(", ");
      } else if (item instanceof EmptyExpression) {
        print(",");
      }
    }
  }

  @Override
  public void visitAssignment(Assignment a, Boolean param) {
    printComments(a);
    printBinaryOperator(a.getType(), a.getLeft(), a.getRight(), param);
  }

  protected void printBinaryOperator(int op, AstNode left, AstNode right, Boolean param) {
    visitorSupport.accept(left, this, param);
    print(" ");
    print(AstNode.operatorToString(op));
    print(" ");
    visitorSupport.accept(right, this, param);
  }

  @Override
  public void visitAstRoot(AstRoot r, Boolean param) {
    for (Node child : r) {
      visitorSupport.accept(child, this, param);
    }
    addSourceMapURL();
  }

  @Override
  public void visitBlock(Block block, Boolean param) {
    if (block.getFirstChild() == null) {
      // empty blocks
      print("{}");
      return;
    }
    println("{").indent();
    for (Node child : block) {
      visitorSupport.accept(child, this, param);
    }
    unindent().print("}");
  }

  @Override
  public void visitBreakStatemen(BreakStatement b, Boolean param) {
    startPosition(b);
    print("break");
    if (b.getBreakLabel() != null) {
      print(" ");
      visitorSupport.accept(b.getBreakLabel(), this, param);
    }
    println(";");

  }

  @Override
  public void visitCatchClause(CatchClause c, Boolean param) {
    print("catch (");
    visitorSupport.accept(c.getVarName(), this, param);
    print(") ");
    visitorSupport.accept(c.getBody(), this, param);
  }

  @Override
  public void visitConditionalExpression(ConditionalExpression c, Boolean param) {
    visitorSupport.accept(c.getTestExpression(), this, param);
    print(" ? ");
    visitorSupport.accept(c.getTrueExpression(), this, param);
    print(" : ");
    visitorSupport.accept(c.getFalseExpression(), this, param);
  }

  @Override
  public void visitContinueStatement(ContinueStatement c, Boolean param) {
    startPosition(c);
    print("continue");
    if (c.getLabel() != null) {
      print(" ");
      visitorSupport.accept(c.getLabel(), this, param);
    }
    println(";");

  }

  @Override
  public void visitDoLoop(DoLoop d, Boolean param) {
    startPosition(d);
    print("do ");
    visitorSupport.accept(d.getBody(), this, param);
    print(" while (");
    visitorSupport.accept(d.getCondition(), this, param);
    println(");");

  }

  @Override
  public void visitElementGet(ElementGet eg, Boolean param) {
    visitorSupport.accept(eg.getTarget(), this, param);
    print("[");
    visitorSupport.accept(eg.getElement(), this, param);
    print("]");
  }

  @Override
  public void visitEmptyStatement(EmptyStatement s, Boolean param) {
    startPosition(s);
    println(";");

  }

  @Override
  public void visitEmptyExpression(EmptyExpression s, Boolean param) {
    // do nothing
  }

  @Override
  public void visitExpressionStatement(ExpressionStatement e, Boolean param) {
    printComments(e);
    startPosition(e);
    visitorSupport.accept(e.getExpression(), this, param);
    endPosition(e);
    println(";");

  }

  private void printStatementAsBlock(AstNode stmt, Boolean param) {
    printStatementAsBlock(stmt, param, true);
  }

  private void printStatementAsBlock(AstNode stmt, Boolean param, boolean addNewLineAfterBlock) {
    if (stmt instanceof Block) {
      visitorSupport.accept(stmt, this, param);
      if (addNewLineAfterBlock) {
        println();
      }
    } else {
      println().indent();
      visitorSupport.accept(stmt, this, param);
      unindent();
    }
  }

  @Override
  public void visitForInLoop(ForInLoop f, Boolean param) {
    startPosition(f);
    print("for (");
    visitorSupport.accept(f.getIterator(), this, param);
    print(" in ");
    visitorSupport.accept(f.getIteratedObject(), this, param);
    print(") ");
    printStatementAsBlock(f.getBody(), param);

  }

  @Override
  public void visitForLoop(ForLoop f, Boolean param) {
    startPosition(f);
    print("for (");
    visitorSupport.accept(f.getInitializer(), this, param);
    print("; ");
    visitorSupport.accept(f.getCondition(), this, param);
    print("; ");
    visitorSupport.accept(f.getIncrement(), this, param);
    print(") ");
    printStatementAsBlock(f.getBody(), param);

  }

  @Override
  public void visitFunctionCall(FunctionCall fc, Boolean param) {
    visitorSupport.accept(fc.getTarget(), this, param);
    print("(");
    if (fc.getArguments() != null) {
      printList(fc.getArguments(), param);
    }
    print(")");
  }

  @Override
  public void visitFunctionNode(FunctionNode f, Boolean param) {
    printComments(f);
    print("function");
    if (f.getFunctionName() != null) {
      print(" ");
      visitorSupport.accept(f.getFunctionName(), this, param);
    }
    if (f.getParams() == null) {
      print("() ");
    } else {
      print("(");
      printList(f.getParams(), param);
      print(") ");
    }
    visitorSupport.accept(f.getBody(), this, param);
    // if (functionType == FUNCTION_STATEMENT) {
    // sb.append("\n");
    // }
  }

  @Override
  public void visitIfStatement(IfStatement ifs, Boolean param) {
    startPosition(ifs);
    print("if (");
    visitorSupport.accept(ifs.getCondition(), this, param);
    print(") ");
    printStatementAsBlock(ifs.getThenPart(), param, ifs.getElsePart() == null);
    if (ifs.getElsePart() instanceof IfStatement) {
      print(" else ");
      visitorSupport.accept(ifs.getElsePart(), this, param);
    } else if (ifs.getElsePart() != null) {
      print(" else ");
      printStatementAsBlock(ifs.getElsePart(), param);
    }

  }

  @Override
  public void visitInfixExpression(InfixExpression ie, Boolean param) {
    visitorSupport.accept(ie.getLeft(), this, param);
    print(" ");
    print(AstNode.operatorToString(ie.getType()));
    print(" ");
    visitorSupport.accept(ie.getRight(), this, param);
  }

  @Override
  public void visitKeywordLiteral(KeywordLiteral k, Boolean param) {
    switch (k.getType()) {
    case Token.THIS:
      print("this");
      break;
    case Token.NULL:
      print("null");
      break;
    case Token.TRUE:
      print("true");
      break;
    case Token.FALSE:
      print("false");
      break;
    case Token.DEBUGGER:
      println("debugger;");
      break;
    default:
      break;
    }
  }

  @Override
  public void visitLabel(Label label, Boolean param) {
    print(label.getName());
    println(":");
  }

  @Override
  public void visitLabeledStatement(LabeledStatement labelStatement, Boolean param) {
    for (Label label : labelStatement.getLabels()) {
      visitorSupport.accept(label, this, param); // prints newline
    }
    indent();
    visitorSupport.accept(labelStatement.getStatement(), this, param);
    unindent();
  }

  @Override
  public void visitName(Name name, Boolean param) {
    print(name.getIdentifier());
  }

  @Override
  public void visitNewExpression(NewExpression ne, Boolean param) {
    print("new ");
    visitorSupport.accept(ne.getTarget(), this, param);
    print("(");
    if (ne.getArguments() != null) {
      printList(ne.getArguments(), param);
    }
    print(")");
    if (ne.getInitializer() != null) {
      print(" ");
      visitorSupport.accept(ne.getInitializer(), this, param);
    }
  }

  @Override
  public void visitNumberLitera(NumberLiteral n, Boolean param) {
    print(n.getValue());
  }

  @Override
  public void visitObjectLiteral(ObjectLiteral p, Boolean param) {
    print("{");
    if (p.getElements() != null) {
      printList(p.getElements(), param);
    }
    print("}");
  }

  @Override
  public void visitObjectProperty(ObjectProperty p, Boolean param) {
    visitorSupport.accept(p.getLeft(), this, param);
    if (p.getType() == Token.COLON) {
      print(": ");
    }
    visitorSupport.accept(p.getRight(), this, param);
  }

  @Override
  public void visitParenthesizedExpression(ParenthesizedExpression p, Boolean param) {
    print("(");
    visitorSupport.accept(p.getExpression(), this, param);
    print(")");
  }

  @Override
  public void visitPropertyGet(PropertyGet p, Boolean param) {
    visitorSupport.accept(p.getLeft(), this, param);
    print(".");
    visitorSupport.accept(p.getRight(), this, param);
  }

  @Override
  public void visitReturnStatement(ReturnStatement r, Boolean param) {
    startPosition(r);
    print("return");
    if (r.getReturnValue() != null) {
      print(" ");
      visitorSupport.accept(r.getReturnValue(), this, param);
    }
    println(";");

  }

  @Override
  public void visitStatements(Statements s, Boolean param) {
    printComments(s);
    for (Node stmt : s) {
      visitorSupport.accept(stmt, this, param);
    }
  }

  @Override
  public void visitStringLiteral(StringLiteral expr, Boolean param) {
    print(Character.toString(expr.getQuoteCharacter()));
    print(ScriptRuntime.escapeString(expr.getValue(), expr.getQuoteCharacter()));
    print(Character.toString(expr.getQuoteCharacter()));
  }

  @Override
  public void visitSwitchCase(SwitchCase s, Boolean param) {
    if (s.getExpression() == null) {
      println("default:");
    } else {
      print("case ");
      visitorSupport.accept(s.getExpression(), this, param);
      println(":");
    }
    if (s.getStatements() != null) {
      indent();
      for (AstNode stmt : s.getStatements()) {
        visitorSupport.accept(stmt, this, param);
      }
      unindent();
    }
  }

  @Override
  public void visitSwitchStatement(SwitchStatement s, Boolean param) {
    startPosition(s);
    print("switch (");
    visitorSupport.accept(s.getExpression(), this, param);
    println(") {");
    indent();
    for (SwitchCase sc : s.getCases()) {
      visitorSupport.accept(sc, this, param);
    }
    unindent();
    println("}");

  }

  @Override
  public void visitTryStatement(TryStatement t, Boolean param) {
    startPosition(t);
    print("try ");
    visitorSupport.accept(t.getTryBlock(), this, param);
    for (CatchClause cc : t.getCatchClauses()) {
      visitorSupport.accept(cc, this, param);
    }
    if (t.getFinallyBlock() != null) {
      print(" finally ");
      visitorSupport.accept(t.getFinallyBlock(), this, param);
    }
    println();

  }

  @Override
  public void visitUnaryExpression(UnaryExpression u, Boolean param) {
    int type = u.getType();
    if (!u.isPostfix()) {
      print(AstNode.operatorToString(type));
      if (type == Token.TYPEOF || type == Token.DELPROP || type == Token.VOID) {
        print(" ");
      }
    }
    visitorSupport.accept(u.getOperand(), this, param);
    if (u.isPostfix()) {
      print(AstNode.operatorToString(type));
    }
  }

  @Override
  public void visitVariableDeclaration(VariableDeclaration v, Boolean param) {
    printComments(v);
    if (v.isStatement()) {
      startPosition(v);
    }
    print("var ");
    printList(v.getVariables(), param);
    if (v.isStatement()) {
      println(";");

    }
  }

  @Override
  public void visitVariableInitializer(VariableInitializer v, Boolean param) {
    visitorSupport.accept(v.getTarget(), this, param);
    if (v.getInitializer() != null) {
      print(" = ");
      visitorSupport.accept(v.getInitializer(), this, param);
    }
  }

  @Override
  public void visitWhileLoop(WhileLoop w, Boolean param) {
    startPosition(w);
    print(" while (");
    visitorSupport.accept(w.getCondition(), this, param);
    print(")");
    printStatementAsBlock(w.getBody(), param);

  }

  @Override
  public void visitThrowStatement(ThrowStatement e, Boolean param) {
    startPosition(e);
    print(" throw ");
    visitorSupport.accept(e.getExpression(), this, param);
    println(";");

  }

  public void addSourceMapURL() {
    if (generateSourceMap) {
      print("//@ sourceMappingURL=").print(inputFile.getName().replaceAll("\\.java$", ".map"));
    }
  }

  public SourceMapGenerator getSourceMapGenerator() {
    return sourceMapGenerator;
  }

  @Override
  public void visitCodeFragment(CodeFragment c, Boolean param) {
    if (c.getCode() != null) {
      print(c.getCode());
    }
  }

}
TOP

Related Classes of org.stjs.generator.javascript.rhino.RhinoJavaScriptWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.