package org.timepedia.exporter.rebind; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import org.timepedia.exporter.client.Export; import org.timepedia.exporter.client.ExportAfterCreateMethod; import org.timepedia.exporter.client.ExportJsInitMethod; import org.timepedia.exporter.client.ExportPackage; import org.timepedia.exporter.client.ExporterUtil; import org.timepedia.exporter.client.SType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JConstructor; import com.google.gwt.core.ext.typeinfo.JField; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JType; /** * */ public class JExportableClassType implements JExportable, JExportableType { private static final String IMPL_EXTENSION = "ExporterImpl"; private ExportableTypeOracle exportableTypeOracle; private JClassType type; public JExportableClassType(ExportableTypeOracle exportableTypeOracle, JClassType type) { this.exportableTypeOracle = exportableTypeOracle; this.type = type; } public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JExportableClassType that = (JExportableClassType) o; return getQualifiedSourceName().equals(that.getQualifiedSourceName()); } public String[] getEnclosingClasses() { String[] enc = type.getName().split("\\."); String[] enclosingTypes = new String[enc.length - 1]; if (enc.length > 1) { System.arraycopy(enc, 0, enclosingTypes, 0, enclosingTypes.length); } return enclosingTypes; } public JExportableConstructor[] getExportableConstructors() { ArrayList<JExportableConstructor> exportableCons = new ArrayList<JExportableConstructor>(); for (JConstructor method : type.getConstructors()) { if (exportableTypeOracle.isExportable(method, this)) { exportableCons.add(new JExportableConstructor(this, method)); } } return exportableCons.toArray(new JExportableConstructor[0]); } public JExportableMethod[] getExportableFactoryMethods() { ArrayList<JExportableMethod> exportableMethods = new ArrayList<JExportableMethod>(); JType retClass = getTypeToExport(); for (JMethod method : type.getMethods()) { if (exportableTypeOracle.isExportableFactoryMethod(method, retClass)) { exportableMethods.add(new JExportableMethod(this, method)); } } return exportableMethods.toArray(new JExportableMethod[0]); } public JExportableField[] getExportableFields() { ArrayList<JExportableField> exportableFields = new ArrayList<JExportableField>(); for (JField field : type.getFields()) { if (exportableTypeOracle.isExportable(field)) { exportableFields.add(new JExportableField(this, field)); } } return exportableFields.toArray(new JExportableField[0]); } private JExportableMethod[] exportableMethods; private JExportableMethod jsInitMethod; private JExportableMethod afterCreateMethod; public JExportableMethod[] getExportableMethods() { if (exportableMethods == null) { ArrayList<JExportableMethod> ret = new ArrayList<JExportableMethod>(); // Create a set with all methods in this class HashSet<JMethod> classMethods = new HashSet<JMethod>(); classMethods.addAll(Arrays.asList(type.getMethods())); classMethods.addAll(Arrays.asList(type.getInheritableMethods())); for (JMethod method : classMethods) { if (exportableTypeOracle.isConstructor(method, this)) { continue; } if (exportableTypeOracle.isExportable(method, this)) { JExportableMethod m = new JExportableMethod(this, method); if (m.isExportJsInitMethod() && exportableTypeOracle.isJavaScriptObject(method.getReturnType()) ) { jsInitMethod = m; } if (m.isExportAfterCreateMethod()) { afterCreateMethod = m; } else { ret.add(m); } } } exportableMethods = ret.toArray(new JExportableMethod[0]); } return exportableMethods; } public JExportableMethod getJsInitMethod() { return jsInitMethod; } public JExportableMethod getAfterCreateMethod() { return afterCreateMethod; } public ExportableTypeOracle getExportableTypeOracle() { return exportableTypeOracle; } public String getExporterImplementationName() { return type.getSimpleSourceName() + IMPL_EXTENSION; } public String getHostedModeJsTypeCast() { return null; } public String getJsTypeOf() { return exportableTypeOracle.getJsTypeOf(getType()); } public String getJSConstructor() { String pkg = getJSExportPackage().trim(); if (!pkg.isEmpty()) { pkg += "."; } return pkg + getJSExportName(); } public String getJSExportName () { Export ann = type.getAnnotation(Export.class); JClassType type = getTypeToExport(); return ann != null && !ann.value().isEmpty() ? ann.value() : type.getName(); } public String getJSExportPackage() { String requestedPackageName = getPrefix(); ExportPackage ann = type.getAnnotation(ExportPackage.class); JClassType type = getTypeToExport(); if (ann != null) { requestedPackageName = ann.value(); } else if (type.getEnclosingType() != null) { JExportableClassType encType = exportableTypeOracle .findExportableClassType( type.getEnclosingType().getQualifiedSourceName()); if (encType != null) { return encType.getJSExportPackage(); } } return requestedPackageName; } public String getJSNIReference() { return type.getJNISignature(); } public String getJSQualifiedExportName() { return getJSConstructor(); } public String getPackageName() { return type.getPackage().getName(); } public String getPrefix() { String prefix = ""; boolean firstClientPackage = true; for (String pkg : type.getPackage().getName().split("\\.")) { if (firstClientPackage && pkg.equals("client")) { firstClientPackage = false; continue; } prefix += pkg; prefix += '.'; } // remove trailing . return prefix.substring(0, prefix.length() - 1); } public String getQualifiedExporterImplementationName() { return getPackageName() + "." + getExporterImplementationName(); } public String getQualifiedSourceName() { return getType().getQualifiedSourceName(); } public JStructuralTypeField[] getStructuralTypeFields() { if (!isStructuralType()) { return new JStructuralTypeField[0]; } else { ArrayList<JStructuralTypeField> fields = new ArrayList<JStructuralTypeField>(); for (JMethod method : type.getMethods()) { if (method.getName().startsWith("set") && Character.isUpperCase(method.getName().charAt(3)) && method.getParameters().length == 1 || method.getAnnotation(SType.class) != null) { fields.add(new JStructuralTypeField(this, method)); } } return fields.toArray(new JStructuralTypeField[0]); } } public JClassType getType() { return type; } public JClassType getRequestedType() { return type; } public JClassType getTypeToExport() { return exportableTypeOracle.isExportOverlay(type) ? exportableTypeOracle.getExportOverlayType(type) : type; } public String getWrapperFunc() { if (!needsExport()) { return null; } return "@" + ExporterUtil.class.getName() + "::wrap(Ljava/lang/Object;)"; } public int hashCode() { return getQualifiedSourceName().hashCode(); } public boolean isPrimitive() { return type.isPrimitive() != null; } public boolean isStructuralType() { return exportableTypeOracle.isStructuralType(this.getType()); } public boolean isTransparentType() { return exportableTypeOracle.isJavaScriptObject(this) || exportableTypeOracle.isString(this) || exportableTypeOracle.isDate(this) || exportableTypeOracle.isArray(this); } public String getJsniSigForArrays() { if (exportableTypeOracle.isJavaScriptObject(this)) { return "[Lcom/google/gwt/core/client/JavaScriptObject;"; } else if (isTransparentType()){ return "[" + type.getJNISignature(); } else { return "[Lorg/timepedia/exporter/client/Exportable;"; } } public boolean needsExport() { return !isPrimitive() && !isTransparentType(); } public boolean isInstantiable() { return exportableTypeOracle.isInstantiable(type); } }