package org.stjs.generator.writer.expression;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Element;
import org.stjs.generator.GenerationContext;
import org.stjs.generator.javac.TreeUtils;
import org.stjs.generator.javac.TreeWrapper;
import org.stjs.generator.javascript.NameValue;
import org.stjs.generator.writer.WriterContributor;
import org.stjs.generator.writer.WriterVisitor;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
public class NewClassWriter<JS> implements WriterContributor<NewClassTree, JS> {
public static BlockTree getDoubleBracesBlock(NewClassTree tree) {
if (tree.getClassBody() == null) {
return null;
}
for (Tree member : tree.getClassBody().getMembers()) {
if (member instanceof BlockTree) {
// XXX I should be sure it's not the one generated by the compiler
BlockTree block = (BlockTree) member;
if (!block.isStatic()) {
return block;
}
}
}
return null;
}
private String getPropertyName(ExpressionTree var) {
if (var instanceof IdentifierTree) {
return ((IdentifierTree) var).getName().toString();
}
if (var instanceof MemberSelectTree) {
return ((MemberSelectTree) var).getIdentifier().toString();
}
// TODO exception!?
return null;
}
/**
* special construction for object initialization new Object(){{x = 1; y = 2; }};
*/
private JS getObjectInitializer(WriterVisitor<JS> visitor, TreeWrapper<NewClassTree, JS> tw) {
NewClassTree tree = tw.getTree();
BlockTree initBlock = getDoubleBracesBlock(tree);
if (initBlock == null && !tw.child(tree.getIdentifier()).isSyntheticType()) {
return null;
}
List<NameValue<JS>> props = new ArrayList<NameValue<JS>>();
if (initBlock != null) {
for (StatementTree stmt : initBlock.getStatements()) {
// check the right type of statements x=y is done in NewClassObjectInitCheck
AssignmentTree assign = (AssignmentTree) ((ExpressionStatementTree) stmt).getExpression();
props.add(NameValue.of(getPropertyName(assign.getVariable()), visitor.scan(assign.getExpression(), tw.getContext())));
}
}
return tw.getContext().js().object(props);
}
/**
* check by {@link org.stjs.generator.check.expression.NewClassInlineFunctionCheck} generate the code for inline
* functions:
*
* <pre>
* new FunctionInterface(){
* public void $invoke(args){
* }
* }
* </pre>
*
* is transformed in
*
* <pre>
* function(args){
* }
* </pre>
*/
private JS getInlineFunctionDeclaration(WriterVisitor<JS> visitor, TreeWrapper<NewClassTree, JS> tw) {
// special construction for inline function definition
if (!tw.child(tw.getTree().getIdentifier()).isJavaScriptFunction()) {
return null;
}
// the check verifies the existence of a single method (first is the generated
// constructor)
Tree method = tw.getTree().getClassBody().getMembers().get(1);
return visitor.scan(method, tw.getContext());
}
private JS getAnonymousClassDeclaration(WriterVisitor<JS> visitor, NewClassTree tree, GenerationContext<JS> context) {
if (tree.getClassBody() == null) {
return null;
}
JS typeDeclaration = visitor.scan(tree.getClassBody(), context);
return context.js().newExpression(context.js().paren(typeDeclaration), arguments(visitor, tree, context));
}
private List<JS> arguments(WriterVisitor<JS> visitor, NewClassTree tree, GenerationContext<JS> context) {
List<JS> arguments = new ArrayList<JS>();
for (Tree arg : tree.getArguments()) {
arguments.add(visitor.scan(arg, context));
}
return arguments;
}
private JS getRegularNewExpression(WriterVisitor<JS> visitor, NewClassTree tree, GenerationContext<JS> context) {
Element type = TreeUtils.elementFromUse(tree.getIdentifier());
JS typeName = context.js().name(context.getNames().getTypeName(context, type));
return context.js().newExpression(typeName, arguments(visitor, tree, context));
}
@Override
public JS visit(WriterVisitor<JS> visitor, NewClassTree tree, GenerationContext<JS> context) {
TreeWrapper<NewClassTree, JS> tw = context.getCurrentWrapper();
JS js = getInlineFunctionDeclaration(visitor, tw);
if (js != null) {
return js;
}
js = getObjectInitializer(visitor, tw);
if (js != null) {
return js;
}
js = getAnonymousClassDeclaration(visitor, tree, context);
if (js != null) {
return js;
}
return getRegularNewExpression(visitor, tree, context);
// if (clazz instanceof ClassWrapper && ClassUtils.isSyntheticType(clazz)) {
// // this is a call to an mock type
// printer.print("{}");
// return;
// }
}
}