Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.stjs.generator.writer.declaration.ClassWriter Maven / Gradle / Ivy
package org.stjs.generator.writer.declaration;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import org.stjs.generator.GenerationContext;
import org.stjs.generator.GeneratorConstants;
import org.stjs.generator.javac.AnnotationHelper;
import org.stjs.generator.javac.ElementUtils;
import org.stjs.generator.javac.TreeUtils;
import org.stjs.generator.javac.TreeWrapper;
import org.stjs.generator.javac.TypesUtils;
import org.stjs.generator.javascript.AssignOperator;
import org.stjs.generator.javascript.JavaScriptBuilder;
import org.stjs.generator.javascript.Keyword;
import org.stjs.generator.javascript.NameValue;
import org.stjs.generator.javascript.UnaryOperator;
import org.stjs.generator.name.DependencyType;
import org.stjs.generator.utils.JavaNodes;
import org.stjs.generator.writer.JavascriptKeywords;
import org.stjs.generator.writer.MemberWriters;
import org.stjs.generator.writer.WriterContributor;
import org.stjs.generator.writer.WriterVisitor;
import org.stjs.javascript.annotation.ServerSide;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
public class ClassWriter implements WriterContributor {
/**
* generate the namespace declaration stjs.ns("namespace") if needed
*/
private void addNamespace(ClassTree tree, GenerationContext context, List stmts) {
Element type = TreeUtils.elementFromDeclaration(tree);
if (JavaNodes.isInnerType(type)) {
// this is an inner (anonymous or not) class - no namespace declaration is generated
return;
}
String namespace = context.getCurrentWrapper().getNamespace();
if (!namespace.isEmpty()) {
JavaScriptBuilder js = context.js();
JS target = js.property(js.name(GeneratorConstants.STJS), "ns");
stmts.add(js.expressionStatement(js.functionCall(target, Collections.singleton(js.string(namespace)))));
}
}
/**
* @return the node to put in the super class. for intefaces, the super class goes also in the interfaces list
*/
private JS getSuperClass(ClassTree clazz, GenerationContext context) {
Element type = TreeUtils.elementFromDeclaration(clazz);
if (clazz.getExtendsClause() == null || type.getKind() == ElementKind.INTERFACE) {
// no super class found
return context.js().keyword(Keyword.NULL);
}
TreeWrapper superType = context.getCurrentWrapper().child(clazz.getExtendsClause());
if (superType.isSyntheticType()) {
return context.js().keyword(Keyword.NULL);
}
DependencyType depType = getDependencyTypeForClassDef(type);
return context.js().name(superType.getTypeName(depType));
}
private DependencyType getDependencyTypeForClassDef(Element type) {
if (JavaNodes.isInnerType(type)) {
// this is an inner (anonymous or not) class -> STATIC dep type instead
return DependencyType.STATIC;
}
return DependencyType.EXTENDS;
}
/**
*
@return the list of implemented interfaces. for intefaces, the super class goes also in the interfaces list
*/
private JS getInterfaces(ClassTree clazz, GenerationContext context) {
Element type = TreeUtils.elementFromDeclaration(clazz);
DependencyType depType = getDependencyTypeForClassDef(type);
List ifaces = new ArrayList();
for (Tree iface : clazz.getImplementsClause()) {
TreeWrapper ifaceType = context.getCurrentWrapper().child(iface);
if (!ifaceType.isSyntheticType()) {
ifaces.add(context.js().name(ifaceType.getTypeName(depType)));
}
}
if (clazz.getExtendsClause() != null && type.getKind() == ElementKind.INTERFACE) {
TreeWrapper superType = context.getCurrentWrapper().child(clazz.getExtendsClause());
if (!superType.isSyntheticType()) {
ifaces.add(0, context.js().name(superType.getTypeName(DependencyType.EXTENDS)));
}
}
return context.js().array(ifaces);
}
/**
* @return the JavaScript node for the class' constructor
*/
private JS getConstructor(WriterVisitor visitor, ClassTree clazz, GenerationContext context) {
for (Tree member : clazz.getMembers()) {
if (JavaNodes.isConstructor(member)) {
// TODO skip the "native" constructors
JS node = visitor.scan(member, context);
if (node != null) {
return node;
}
}
}
// no constructor found : interfaces, return an empty function
return context.js().function(null, Collections. emptyList(), null);
}
private List getAllMembersExceptConstructors(ClassTree clazz) {
List nonConstructors = new ArrayList();
for (Tree member : clazz.getMembers()) {
if (!JavaNodes.isConstructor(member) && !(member instanceof BlockTree)) {
nonConstructors.add(member);
}
}
return nonConstructors;
}
/**
* @return the JavaScript node for the class' members
*/
private JS getMembers(WriterVisitor visitor, ClassTree clazz, GenerationContext context) {
// the following members must not appear in the initializer function:
// - constructors (they are printed elsewhere)
// - abstract methods (they should be omitted)
List nonConstructors = getAllMembersExceptConstructors(clazz);
if (nonConstructors.isEmpty()) {
return context.js().keyword(Keyword.NULL);
}
@SuppressWarnings("unchecked")
List params = Arrays.asList(context.js().name(JavascriptKeywords.CONSTRUCTOR), context.js().name(JavascriptKeywords.PROTOTYPE));
List stmts = new ArrayList();
for (Tree member : nonConstructors) {
stmts.add(visitor.scan(member, context));
}
return context.js().function(null, params, context.js().block(stmts));
}
private void addStaticInitializers(WriterVisitor visitor, ClassTree tree, GenerationContext context, List stmts) {
for (Tree member : tree.getMembers()) {
if (member instanceof BlockTree) {
stmts.add(visitor.scan(member, context));
}
}
}
public static boolean isMainMethod(MethodTree method) {
if (JavaNodes.isStatic(method) && "main".equals(method.getName().toString()) && method.getParameters().size() == 1) {
VariableElement var = TreeUtils.elementFromDeclaration(method.getParameters().get(0));
if (var.asType() instanceof ArrayType) {
TypeMirror componentType = ((ArrayType) var.asType()).getComponentType();
return TypesUtils.isString(componentType);
}
}
return false;
}
private boolean hasMainMethod(ClassTree clazz) {
for (Tree member : clazz.getMembers()) {
if (!(member instanceof MethodTree)) {
continue;
}
MethodTree method = (MethodTree) member;
if (isMainMethod(method)) {
return true;
}
}
return false;
}
/**
* add the call to the main method, if it exists
*/
private void addMainMethodCall(ClassTree clazz, List stmts, GenerationContext context) {
if (!hasMainMethod(clazz)) {
return;
}
TypeElement type = TreeUtils.elementFromDeclaration(clazz);
JS target = context.getCurrentWrapper().isGlobal() ? null : context.js().name(
context.getNames().getTypeName(context, type, DependencyType.STATIC));
JavaScriptBuilder js = context.js();
JS condition = js.unary(UnaryOperator.LOGICAL_COMPLEMENT, js.property(js.name(GeneratorConstants.STJS), "mainCallDisabled"));
JS thenPart = js.expressionStatement(js.functionCall(js.property(target, "main"), Collections. emptyList()));
stmts.add(js.ifStatement(condition, thenPart, null));
}
@SuppressWarnings("unchecked")
private JS getFieldTypeDesc(TypeMirror type, GenerationContext context) {
JavaScriptBuilder js = context.js();
if (JavaNodes.isJavaScriptPrimitive(type)) {
return js.keyword(Keyword.NULL);
}
JS typeName = js.string(context.getNames().getTypeName(context, type, DependencyType.OTHER));
if (type instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) type;
// enum
if (declaredType.asElement().getKind() == ElementKind.ENUM) {
return js.object(Arrays.asList(NameValue.of("name", js.string("Enum")),
NameValue.of("arguments", js.array(Collections.singleton(typeName)))));
}
// parametrized type
if (!declaredType.getTypeArguments().isEmpty()) {
List array = new ArrayList();
for (TypeMirror arg : declaredType.getTypeArguments()) {
array.add(getFieldTypeDesc(arg, context));
}
return js.object(Arrays.asList(NameValue.of("name", typeName), NameValue.of("arguments", js.array(array))));
}
}
return typeName;
}
@SuppressWarnings("unused")
private JS getTypeDescription(WriterVisitor visitor, ClassTree tree, GenerationContext context) {
// if (isGlobal(type)) {
// printer.print(JavascriptKeywords.NULL);
// return;
// }
TypeElement type = TreeUtils.elementFromDeclaration(tree);
List> props = new ArrayList>();
for (Element member : ElementUtils.getAllFieldsIn(type)) {
TypeMirror memberType = ElementUtils.getType(member);
if (JavaNodes.isJavaScriptPrimitive(memberType)) {
continue;
}
if (member.getKind() == ElementKind.ENUM_CONSTANT) {
continue;
}
if (memberType instanceof TypeVariable) {
// what to do with fields of generic parameters !?
continue;
}
if (!skipTypeDescForField(member)) {
props.add(NameValue.of(member.getSimpleName(), getFieldTypeDesc(memberType, context)));
}
}
return context.js().object(props);
}
private boolean skipTypeDescForField(Element member) {
if (((TypeElement) member.getEnclosingElement()).getQualifiedName().toString().startsWith("java.lang.")) {
// maybe we should rather skip the bridge classes here
return true;
}
if (member.getAnnotation(ServerSide.class) != null) {
return true;
}
return false;
}
/**
* transform a.b.type in constructor.type
*/
private String replaceFullNameWithConstructor(String typeName) {
int pos = typeName.lastIndexOf('.');
return JavascriptKeywords.CONSTRUCTOR + typeName.substring(pos);
}
private JS writeAnnotationValue(WriterVisitor visitor, ExpressionTree expr, GenerationContext context) {
if (expr instanceof NewArrayTree) {
// special case for array initializer
List items = new ArrayList();
for (ExpressionTree item : ((NewArrayTree) expr).getInitializers()) {
items.add(visitor.scan(item, context));
}
return context.js().array(items);
}
return visitor.scan(expr, context);
}
private JS getAnnotationForElement(AnnotationTree ann, WriterVisitor visitor, GenerationContext context) {
// build "annotationType": {arg1, arg2, ...}
// XXX: should use here type names?
if (AnnotationHelper.getRetentionType(ann.getAnnotationType()) == RetentionPolicy.SOURCE) {
return null;
}
String annEntryKey = ann.getAnnotationType().toString();
if (!context.getConfiguration().getAnnotations().contains(annEntryKey)) {
return null;
}
List> annotationArgsDesc = new ArrayList>();
for (ExpressionTree arg : ann.getArguments()) {
AssignmentTree assign = (AssignmentTree) arg;
annotationArgsDesc
.add(NameValue.of(assign.getVariable().toString(), writeAnnotationValue(visitor, assign.getExpression(), context)));
}
return context.js().object(annotationArgsDesc);
}
private void addAnnotationsForElement(String name, List> props, WriterVisitor visitor,
List annotations, GenerationContext context) {
if (annotations.isEmpty()) {
return;
}
List> annotationsDesc = new ArrayList>();
for (AnnotationTree ann : annotations) {
JS annotationArgs = getAnnotationForElement(ann, visitor, context);
if (annotationArgs != null) {
// XXX: hack here to quote the type name - to change when using the type names
String annEntryKey = ann.getAnnotationType().toString();
annotationsDesc.add(NameValue.of("\"" + annEntryKey + "\"", annotationArgs));
}
}
if (!annotationsDesc.isEmpty()) {
props.add(NameValue.of(name, context.js().object(annotationsDesc)));
}
}
private void addAnnotationsForMethod(MethodTree method, List> props, WriterVisitor visitor, GenerationContext context) {
String name = method.getName().toString();
if ("".equals(name)) {
name = "_init_";
}
addAnnotationsForElement(name, props, visitor, method.getModifiers().getAnnotations(), context);
for (int i = 0; i < method.getParameters().size(); ++i) {
addAnnotationsForElement(method.getName().toString() + "$" + i, props, visitor, method.getParameters().get(i).getModifiers()
.getAnnotations(), context);
}
}
/**
* build the annotation description element
*
*
* $annotations : {
* _: {....}
* field1: {...}
* method1: {...}
* method1$0: {...}
* method1$1: {...}...
* }
*
*
* for each annotation list you have:
*
*
* {
* "annotationType1": [expr1, expr2, expr3],
* "annotationType2": []
* }
*
*/
private JS getAnnotationDescription(WriterVisitor visitor, ClassTree classTree, GenerationContext context) {
List> props = new ArrayList>();
addAnnotationsForElement("_", props, visitor, classTree.getModifiers().getAnnotations(), context);
for (Tree member : classTree.getMembers()) {
if (MemberWriters.shouldSkip(context.getCurrentWrapper().child(member))) {
continue;
}
if (member instanceof VariableTree) {
VariableTree field = (VariableTree) member;
addAnnotationsForElement(field.getName().toString(), props, visitor, field.getModifiers().getAnnotations(), context);
} else if (member instanceof MethodTree) {
addAnnotationsForMethod((MethodTree) member, props, visitor, context);
}
}
return context.js().object(props);
}
@SuppressWarnings("unused")
private boolean generareEnum(WriterVisitor visitor, ClassTree tree, GenerationContext context, List stmts) {
Element type = TreeUtils.elementFromDeclaration(tree);
if (type.getKind() != ElementKind.ENUM) {
return false;
}
JavaScriptBuilder js = context.js();
// add all anum entries
List enumEntries = new ArrayList();
for (Element member : ElementUtils.getAllFieldsIn((TypeElement) type)) {
if (member.getKind() == ElementKind.ENUM_CONSTANT) {
enumEntries.add(js.string(member.getSimpleName().toString()));
}
}
JS enumConstructor = js.functionCall(js.property(js.name(GeneratorConstants.STJS), "enumeration"), enumEntries);
String typeName = context.getNames().getTypeName(context, type, DependencyType.EXTENDS);
if (typeName.contains(".")) {
// inner class or namespace
boolean innerClass = type.getEnclosingElement().getKind() != ElementKind.PACKAGE;
String leftSide = innerClass ? replaceFullNameWithConstructor(typeName) : typeName;
stmts.add(js.expressionStatement(js.assignment(AssignOperator.ASSIGN, js.name(leftSide), enumConstructor)));
} else {
// regular class
stmts.add(js.variableDeclaration(true, Collections.singleton(NameValue.of(typeName, enumConstructor))));
}
return true;
}
/**
* Special generation for classes marked with {@link org.stjs.javascript.annotation.GlobalScope}. The name of the
* class must appear nowhere.
*/
private boolean generateGlobal(WriterVisitor visitor, ClassTree tree, GenerationContext context, List stmts) {
if (!context.getCurrentWrapper().isGlobal()) {
return false;
}
// print members
List nonConstructors = getAllMembersExceptConstructors(tree);
for (Tree member : nonConstructors) {
stmts.add(visitor.scan(member, context));
}
addStaticInitializers(visitor, tree, context, stmts);
addMainMethodCall(tree, stmts, context);
return true;
}
private void addConstructorStatement(WriterVisitor visitor, ClassTree tree, GenerationContext context, List stmts) {
boolean anonymousClass = tree.getSimpleName().length() == 0;
if (anonymousClass) {
// anonymous class - nothing to do the constructor will be added directly
return;
}
JavaScriptBuilder js = context.js();
Element type = TreeUtils.elementFromDeclaration(tree);
String typeName = context.getNames().getTypeName(context, type, DependencyType.EXTENDS);
if (typeName.contains(".")) {
// inner class or namespace
// generate [ns.]typeName = function() {...}
boolean innerClass = type.getEnclosingElement().getKind() != ElementKind.PACKAGE;
String leftSide = innerClass ? replaceFullNameWithConstructor(typeName) : typeName;
stmts.add(js.expressionStatement(js.assignment(AssignOperator.ASSIGN, js.name(leftSide), getConstructor(visitor, tree, context))));
} else {
// regular class
// generate var typeName = function() {...}
stmts.add(js.variableDeclaration(true, typeName, getConstructor(visitor, tree, context)));
}
}
@Override
public JS visit(WriterVisitor visitor, ClassTree tree, GenerationContext context) {
JavaScriptBuilder js = context.js();
List stmts = new ArrayList();
if (generateGlobal(visitor, tree, context, stmts)) {
// special construction for globals
return js.statements(stmts);
}
addNamespace(tree, context, stmts);
if (generareEnum(visitor, tree, context, stmts)) {
// special construction for enums
return js.statements(stmts);
}
Element type = TreeUtils.elementFromDeclaration(tree);
String typeName = context.getNames().getTypeName(context, type, DependencyType.EXTENDS);
JS name = js.name(typeName);
JS superClazz = getSuperClass(tree, context);
JS interfaces = getInterfaces(tree, context);
JS members = getMembers(visitor, tree, context);
JS typeDesc = getTypeDescription(visitor, tree, context);
JS annotationDesc = getAnnotationDescription(visitor, tree, context);
boolean anonymousClass = tree.getSimpleName().length() == 0;
if (anonymousClass) {
// anonymous class
name = getConstructor(visitor, tree, context);
}
addConstructorStatement(visitor, tree, context, stmts);
@SuppressWarnings("unchecked")
JS extendsCall = js.functionCall(js.property(js.name(GeneratorConstants.STJS), "extend"),
Arrays.asList(name, superClazz, interfaces, members, typeDesc, annotationDesc));
if (anonymousClass) {
stmts.add(extendsCall);
} else {
stmts.add(context.withPosition(tree, js.expressionStatement(extendsCall)));
}
addStaticInitializers(visitor, tree, context, stmts);
addMainMethodCall(tree, stmts, context);
return js.statements(stmts);
}
}