/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2007-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License"). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at LICENSE.html or
* http://www.sun.com/cddl. See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this License Header Notice in each
* file.
*
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s): Alexandre (Shura) Iline. (shurymury@gmail.com)
*
* The Original Software is the Jemmy library. The Initial Developer of the
* Original Software is Alexandre Iline. All Rights Reserved.
*
*/
package org.jemmy.dock;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.*;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.AbstractAnnotationValueVisitor6;
import javax.lang.model.util.AbstractTypeVisitor6;
import static javax.lang.model.util.ElementFilter.methodsIn;
import javax.lang.model.util.SimpleAnnotationValueVisitor6;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import org.jemmy.control.*;
import org.jemmy.env.Environment;
import org.jemmy.interfaces.ControlInterface;
import org.jemmy.interfaces.Parent;
import org.jemmy.lookup.LookupCriteria;
/**
* Major inconvenience which a novice experiences with Jemmy usage is the fact
* that it is not clear from API what kind of operation you would be able to
* perform with what kind of controls. Similarly it is not at first clear what
* kind of lookup criteria one could use for a particular controls kind and also
* what properties a control type provides.<br/> This annotation processor
* generates another layer of API to access the same functionality which Jemmy
* provides but differently - through a set of lookup constructors, property
* getters and
* <code>as*</code> interfaces. All of that is generated from a set of
* annotations which are added to classes which extend
* <code>org.jemmy.control.Wrap</code> class.<br> Check
* <code>*.*Dock</code> classes and the corresponding
* <code>Wrap</code> extentions within JemmyAWT code.
*
* @deprecated Please use a generator from JemmySupport
* @author shura
*/
@Deprecated
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class DockGenerator extends AbstractProcessor {
public static final String CONTROL_PLACEHOLDER = "$CONTROL$";
public static final String PACKAGE_PLACEHOLDER = "$PACKAGE$";
public static final String SUPERDOCK_PLACEHOLDER = "$SUPERDOCK$";
public static final String WRAP_PLACEHOLDER = "$WRAP$";
private static final String DOCK_PLACEHOLDER = "$DOCK$";
private static final String body_source = "/**\n"
+ "This is a convenience class generated by information available through annotations in class $WRAP$\n"
+ "*/\n"
+ "package $PACKAGE$;\n"
+ "import " + Wrap.class.getName() + ";\n"
+ "import " + Parent.class.getName() + ";\n"
+ "import " + LookupCriteria.class.getName() + ";\n"
+ "import " + Environment.class.getName() + ";\n"
+ "public class $DOCK$ extends $SUPERDOCK$ {\n"
+ "$CONSTRUCTORS$"
+ "$WRAP_GETTER$"
+ "$INTERFACES$"
+ "$PROPERTIES$"
+ "}";
private static final String control_constructor_source =
"\t/**Creates dock for a previously found control*/\n"
+ "\tpublic $DOCK$(Environment env, $CONTROL$ control) {\n"
+ "\t\tsuper($WRAP_METHOD$(env, $CONTROL$.class, control));\n"
+ "\t}\n";
private static final String wrap_constructor_source =
"\t/**Creates dock for a wrapped control*/\n"
+ "\t$PUBLIC$ $DOCK$(Wrap<? extends $CONTROL$> wrap) {\n"
+ "\t\tsuper(wrap);\n"
+ "\t}\n";
private static final String criteria_constructors_source =
"\t/**Looks for an <code>index</code>'th <code>$CONTROL$</code> by a criteria"
+ " within <code>parent</code>*/\n"
+ "\tpublic $DOCK$(Parent<? super $CONTROL$> parent, int index, LookupCriteria<$CONTROL$>... criteria) {\n"
+ "\t\tthis(lookup(parent, $CONTROL$.class, index, criteria));\n"
+ "\t}\n"
+ "\t/**Looks for a <code>$CONTROL$</code> by a criteria"
+ " within <code>parent</code>*/\n"
+ "\tpublic $DOCK$(Parent<? super $CONTROL$> parent, LookupCriteria<$CONTROL$>... criteria) {\n"
+ "\t\tthis(parent, 0, criteria);\n"
+ "\t}\n";
private static final String object_lookup_constructors_source =
"\t/**Looks for an <code>index</code>'th <code>$CONTROL$</code> $LOOKUP_DESCRIPTION$"
+ " within <code>parent</code>*/\n"
+ "\tpublic $DOCK$(Parent<? super $CONTROL$> parent, int index$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis(parent, index, $USED_PARAMETERS$);\n"
+ "\t}\n"
+ "\t/**Looks for a <code>$CONTROL$</code> $LOOKUP_DESCRIPTION$"
+ " within <code>parent</code>*/\n"
+ "\tpublic $DOCK$(Parent<? super $CONTROL$> parent$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis(parent, $USED_PARAMETERS$);\n"
+ "\t}\n";
private static final String default_parent_criteria_constructors_source =
"\t/**Looks for a <code>$CONTROL$</code> by a criteria"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic $DOCK$(LookupCriteria<$CONTROL$>... criteria) {\n"
+ "\t\tthis(0, criteria);\n"
+ "\t}\n"
+ "\t/**Looks for an <code>index</code>'th <code>$CONTROL$</code> by a criteria"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic $DOCK$(int index, LookupCriteria<$CONTROL$>... criteria) {\n"
+ "\t\tthis($LOOKUP_METHOD$($CONTROL$.class), index, criteria);\n"
+ "\t}\n";
private static final String default_parent_object_lookup_constructors_source =
"\t/**Looks for a <code>$CONTROL$</code> $LOOKUP_DESCRIPTION$"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic $DOCK$($DECLARED_PARAMETERS$) {\n"
+ "\t\tthis(0, $USED_PARAMETERS$);\n"
+ "\t}\n"
+ "\t/**Looks for an <code>index</code>'th <code>$CONTROL$</code> $LOOKUP_DESCRIPTION$"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic $DOCK$(int index$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis($LOOKUP_METHOD$($CONTROL$.class), index, $USED_PARAMETERS$);\n"
+ "\t}\n";
private static final String criteria_subclass_constructors_source =
"\t/**Looks for an <code>index</code>'th <code>SUBCLASS</code> by a criteria"
+ " within <code>parent</code>*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Parent<? super $CONTROL$> parent, Class<SUBCLASS> cls, int index, LookupCriteria<SUBCLASS>... criteria) {\n"
+ "\t\tthis(lookup(parent, cls, index, criteria));\n"
+ "\t}\n"
+ "\t/**Looks for a <code>SUBCLASS</code> by a criteria"
+ " within <code>parent</code>*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Parent<? super $CONTROL$> parent, Class<SUBCLASS> cls, LookupCriteria<SUBCLASS>... criteria) {\n"
+ "\t\tthis(parent, cls, 0, criteria);\n"
+ "\t}\n";
private static final String object_lookup_subclass_constructors_source =
"\t/**Looks for an <code>index</code>'th <code>SUBCLASS</code> $LOOKUP_DESCRIPTION$"
+ " within <code>parent</code>*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Parent<? super $CONTROL$> parent, Class<SUBCLASS> cls, int index$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis(parent, cls, index, $USED_PARAMETERS$);\n"
+ "\t}\n"
+ "\t/**Looks for a <code>SUBCLASS</code> $LOOKUP_DESCRIPTION$"
+ " within <code>parent</code>*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Parent<? super $CONTROL$> parent, Class<SUBCLASS> cls$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis(parent, cls, $USED_PARAMETERS$);\n"
+ "\t}\n";
private static final String default_parent_criteria_subclass_constructors_source =
"\t/**Looks for a <code>SUBCLASS</code> by a criteria"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Class<SUBCLASS> cls, LookupCriteria<SUBCLASS>... criteria) {\n"
+ "\t\tthis(cls, 0, criteria);\n"
+ "\t}\n"
+ "\t/**Looks for an <code>index</code>'th <code>SUBCLASS</code> by a criteria"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Class<SUBCLASS> cls, int index, LookupCriteria<SUBCLASS>... criteria) {\n"
+ "\t\tthis($LOOKUP_METHOD$(cls), cls, index, criteria);\n"
+ "\t}\n";
private static final String default_parent_object_lookup_subclass_constructors_source =
"\t/**Looks for a <code>SUBCLASS</code> $LOOKUP_DESCRIPTION$"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Class<SUBCLASS> cls$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis(cls, 0, $USED_PARAMETERS$);\n"
+ "\t}\n"
+ "\t/**Looks for an <code>index</code>'th <code>SUBCLASS</code> $LOOKUP_DESCRIPTION$"
+ " within $DEFAULT_PARENT_DESCRIPTION$*/\n"
+ "\tpublic <SUBCLASS extends $CONTROL$> $DOCK$(Class<SUBCLASS> cls, int index$COMMA$ $DECLARED_PARAMETERS$) {\n"
+ "\t\tthis($LOOKUP_METHOD$(cls), cls, index, $USED_PARAMETERS$);\n"
+ "\t}\n";
private static final String wrap_getter_source =
"\t/**Returns wrap*/\n"
+ "\t@Override\n"
+ "\tpublic $WRAP$<? extends $CONTROL$> wrap() {\n"
+ "\t\treturn ($WRAP$<? extends $CONTROL$>)super.wrap();\n"
+ "\t}\n";
private static final String anon_wrap_getter_source =
"\t/**Returns wrap*/\n"
+ "\t@Override\n"
+ "\tpublic Wrap<? extends $CONTROL$> wrap() {\n"
+ "\t\treturn (Wrap<? extends $CONTROL$>)super.wrap();\n"
+ "\t}\n";
private static final String interface_getter_source =
"\t/**Allows to use as <code>$INTERFACE$</code>*/\n"
+ "\tpublic $INTERFACE$ $INTERFACE_GETTER$() {\n"
+ "\t\treturn wrap().as($INTERFACE$.class);\n"
+ "\t}\n";
private static final String typed_interface_getter_source =
"\t/**Allows to use as <code>$INTERFACE$<$INTERFACE_TYPE$></code>*/\n"
+ "\tpublic $INTERFACE$<$INTERFACE_TYPE$> $INTERFACE_GETTER$() {\n"
+ "\t\treturn wrap().as($INTERFACE$.class, $INTERFACE_TYPE$.class);\n"
+ "\t}\n";
private static final String method_property_getter_source =
"\t/**Returns a result of <code>$PROP_NAME$()</code> method*/\n"
+ "\tpublic $PROP_TYPE$ $GETTER_NAME$() {\n"
+ "\t\treturn getProperty(new org.jemmy.action.GetAction<$PROP_TYPE$>() {\n"
+ "\t\t\t@Override\n"
+ "\t\t\tpublic void run(Object... parameters) throws Exception {\n"
+ "\t\t\t\tsetResult(wrap().getControl().$PROP_NAME$());\n"
+ "\t\t\t}\n"
+ "\t\t});\n"
+ "\t}\n";
private static final String field_property_getter_source =
"\t/**Returns a value of <code>$PROP_NAME$</code> property*/\n"
+ "\tpublic $PROP_TYPE$ $GETTER_NAME$() {\n"
+ "\t\treturn getProperty(new org.jemmy.action.GetAction<$PROP_TYPE$>() {\n"
+ "\t\t\t@Override\n"
+ "\t\t\tpublic void run(Object... parameters) throws Exception {\n"
+ "\t\t\t\tsetResult(wrap().getControl().$PROP_NAME$);\n"
+ "\t\t\t}\n"
+ "\t\t});\n"
+ "\t}\n";
private static final String declared_property_getter_source =
"\t/**Returns $PROP_NAME$ property*/\n"
+ "\tpublic $PROP_TYPE$ $GETTER_NAME$() {\n"
+ "\t\treturn wrap().$METHOD_NAME$();\n"
+ "\t}\n";
private static final String anon_declared_property_getter_source =
"\t/**Returns $PROP_NAME$ property*/\n"
+ "\tpublic $PROP_TYPE$ $GETTER_NAME$() {\n"
+ "\t\treturn wrap().getProperty($PROP_TYPE$.class, \"$PROP_NAME$\");\n"
+ "\t}\n";
private static final String shortcut_methods_source =
"\t/**Calls <code>wrap().as($INTERFACE$.class).$INVOKE_SETTER$;</code>*/\n"
+ "\tpublic $RETURN_TYPE$ $INTERFACE_SETTER$ {\n"
+ "\t\t $RETURN_STATEMENT$wrap().as($INTERFACE$.class).$INVOKE_SETTER$;\n"
+ "\t}\n";
private HashMap<String, String> primitiveTypesSubstitutions =
new HashMap<String, String>();
{
primitiveTypesSubstitutions.put(int.class.getName(), Integer.class.getName());
primitiveTypesSubstitutions.put(long.class.getName(), Long.class.getName());
primitiveTypesSubstitutions.put(float.class.getName(), Float.class.getName());
primitiveTypesSubstitutions.put(double.class.getName(), Double.class.getName());
primitiveTypesSubstitutions.put(boolean.class.getName(), Boolean.class.getName());
}
Set<String> types;
public DockGenerator() {
types = new HashSet<String>();
types.add(ControlType.class.getName());
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return types;
}
private StringBuilder replace(String b, String... replacements) {
return replace(new StringBuilder(b), replacements);
}
private StringBuilder replace(StringBuilder b, String... replacements) {
assert replacements.length % 2 == 0;
for (int j = 0; j < replacements.length / 2; j++) {
int i;
while ((i = b.indexOf(replacements[j * 2])) > -1) {
b.replace(i, i + replacements[j * 2].length(), replacements[j * 2 + 1]);
}
}
return b;
}
private String generateDockName(Element e, String controlClassName) {
DockInfo dn = e.getAnnotation(DockInfo.class);
if (dn != null && dn.name().length() > 0) {
return dn.name();
} else {
return generateDockName(((TypeElement) e).getQualifiedName().toString(),
getSimpleName(controlClassName));
}
}
private String generateDockName(String wrapClassName, String controlClassName) {
if (wrapClassName.equals(Wrap.class.getName())) {
return Dock.class.getName();
} else {
return getPackageName(wrapClassName) + "." + getSimpleName(controlClassName) + "Dock";
}
}
private String substitutePrimitiveType(String orig) {
if (primitiveTypesSubstitutions.containsKey(orig)) {
return primitiveTypesSubstitutions.get(orig);
} else {
return orig;
}
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment re) {
for (Element e : re.getElementsAnnotatedWith(ControlType.class)) {
String wrapClass = ((TypeElement) e).getQualifiedName().toString();
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Wrapper: " + wrapClass);
//Lets assume there is only one ControlType notification, ok?
AnnotationMirror controlTypeAnn = findAnnotations(e, ControlType.class).get(0);
for (String controlClassName : getElementValue(controlTypeAnn, "value").accept(new ClassArrayValueGetter(), null)) {
String dockClassName = generateDockName(e, controlClassName);
String dockPackage = getPackageName(dockClassName);
String dockShortName = getSimpleName(dockClassName);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Generating " + dockPackage + "." + dockShortName + " for " + controlClassName);
//superclass
String wrapSuperClass = ((TypeElement) e).getSuperclass().accept(new TypeNameGetter(), null);
String superControlClassName = ((TypeElement) e).getSuperclass().
accept(new SuperWrapClassNamesGetter(), null);
String superDockClassName = generateDockName(((DeclaredType) ((TypeElement) e).getSuperclass()).asElement(), superControlClassName);
//header
StringBuilder body = replace(body_source,
PACKAGE_PLACEHOLDER, dockPackage,
DOCK_PLACEHOLDER, dockShortName,
SUPERDOCK_PLACEHOLDER, superDockClassName,
WRAP_PLACEHOLDER, wrapClass);
TypeMirror mirror = e.asType();
String defaultWrapperMethod = null;
String defaultParentMethod = null;
String defaultParentDescription = null;
boolean anonymous = false;
while (!mirror.accept(new TypeNameGetter(), null).equals(Wrap.class.getName())) {
Element typeEl = ((DeclaredType) mirror).asElement();
if (!anonymous) {
DockInfo di = e.getAnnotation(DockInfo.class);
if (di != null) {
anonymous = di.anonymous();
}
}
for (Element el : typeEl.getEnclosedElements()) {
if (defaultParentMethod == null) {
DefaultParent defaultParentAnn = el.getAnnotation(DefaultParent.class);
if (defaultParentAnn != null) {
defaultParentMethod = ((TypeElement) ((DeclaredType) mirror).asElement()).getQualifiedName() + "." + el.getSimpleName();
defaultParentDescription = defaultParentAnn.value();
}
}
if (defaultWrapperMethod == null) {
DefaultWrapper defaultWrapperAnn = el.getAnnotation(DefaultWrapper.class);
if (defaultWrapperAnn != null) {
defaultWrapperMethod = ((TypeElement) ((DeclaredType) mirror).asElement()).getQualifiedName() + "." + el.getSimpleName();
}
}
if (defaultParentMethod != null && defaultWrapperMethod != null) {
break;
}
}
mirror = (DeclaredType) ((TypeElement) ((DeclaredType) mirror).asElement()).getSuperclass();
}
//constructors
StringBuilder constructors = new StringBuilder();
if (defaultWrapperMethod != null) {
constructors.append(replace(control_constructor_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$WRAP_METHOD$", defaultWrapperMethod));
}
constructors.append(replace(wrap_constructor_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$PUBLIC$", anonymous ? "protected" : "public"));
constructors.append(replace(criteria_constructors_source, DOCK_PLACEHOLDER, dockShortName, CONTROL_PLACEHOLDER, controlClassName));
boolean needsSubtypeLookups = (e.getAnnotation(DockInfo.class) != null) && e.getAnnotation(DockInfo.class).generateSubtypeLookups();
if (needsSubtypeLookups) {
constructors.append(replace(criteria_subclass_constructors_source, DOCK_PLACEHOLDER, dockShortName, CONTROL_PLACEHOLDER, controlClassName));
}
if (defaultParentMethod != null) {
constructors.append(replace(default_parent_criteria_constructors_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$LOOKUP_METHOD$", defaultParentMethod,
"$DEFAULT_PARENT_DESCRIPTION$", defaultParentDescription));
if (needsSubtypeLookups) {
constructors.append(replace(default_parent_criteria_subclass_constructors_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$LOOKUP_METHOD$", defaultParentMethod,
"$DEFAULT_PARENT_DESCRIPTION$", defaultParentDescription));
}
}
//object lookup constructors
ObjectLookupTable olt = new ObjectLookupTable((TypeElement) e);
for (int i = 0; i < olt.names.size(); i++) {
String mn = olt.methodNames.get(i);
StringBuilder declaredParameters = new StringBuilder();
StringBuilder usedParameters =
new StringBuilder(mn + "($LOOKUP_SUBTYPE$");
// StringBuilder usedParameters =
// new StringBuilder(mn + "(" + controlClassName + ".class");
for (int j = 0; j < olt.types.get(i).size(); j++) {
usedParameters.append(", ");
if (j > 0) {
declaredParameters.append(", ");
}
usedParameters.append(olt.names.get(i).get(j));
declaredParameters.append(olt.types.get(i).get(j)).
append(" ").append(olt.names.get(i).get(j));
}
usedParameters.append(")");
String comma = (olt.types.get(i).size() > 0) ? "," : "";
constructors.append(replace(object_lookup_constructors_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$DECLARED_PARAMETERS$", declaredParameters.toString(),
"$USED_PARAMETERS$", usedParameters.toString(),
"$LOOKUP_SUBTYPE$", controlClassName + ".class",
"$LOOKUP_DESCRIPTION$", olt.comments.get(i),
"$COMMA$", comma));
if (needsSubtypeLookups) {
constructors.append(replace(object_lookup_subclass_constructors_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$DECLARED_PARAMETERS$", declaredParameters.toString(),
"$USED_PARAMETERS$", usedParameters.toString(),
"$LOOKUP_SUBTYPE$", "cls",
"$LOOKUP_DESCRIPTION$", olt.comments.get(i),
"$COMMA$", comma));
}
if (defaultParentMethod != null) {
constructors.append(replace(default_parent_object_lookup_constructors_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$LOOKUP_METHOD$", defaultParentMethod,
"$DEFAULT_PARENT_DESCRIPTION$", defaultParentDescription,
"$DECLARED_PARAMETERS$", declaredParameters.toString(),
"$USED_PARAMETERS$", usedParameters.toString(),
"$LOOKUP_SUBTYPE$", controlClassName + ".class",
"$LOOKUP_DESCRIPTION$", olt.comments.get(i),
"$COMMA$", comma));
if (needsSubtypeLookups) {
constructors.append(replace(default_parent_object_lookup_subclass_constructors_source,
DOCK_PLACEHOLDER, dockShortName,
CONTROL_PLACEHOLDER, controlClassName,
"$LOOKUP_METHOD$", defaultParentMethod,
"$DEFAULT_PARENT_DESCRIPTION$", defaultParentDescription,
"$DECLARED_PARAMETERS$", declaredParameters.toString(),
"$USED_PARAMETERS$", usedParameters.toString(),
"$LOOKUP_SUBTYPE$", "cls",
"$LOOKUP_DESCRIPTION$", olt.comments.get(i),
"$COMMA$", comma));
}
}
}
replace(body, "$CONSTRUCTORS$", constructors.toString());
//wrapper
replace(body, "$WRAP_GETTER$", replace(
anonymous ? anon_wrap_getter_source : wrap_getter_source,
CONTROL_PLACEHOLDER, controlClassName,
WRAP_PLACEHOLDER, wrapClass).toString());
//interfaces
StringBuilder interfaces = new StringBuilder();
// List<AnnotationMirror> controlInterfaceTypeAnns = findAnnotations(e, ControlInterfaceType.class);
List<AnnotationMirror> controlInterfacesAnns = findAnnotations(e, ControlInterfaces.class);
//visitedNodes - hashset to avoid duplicates, when different interfaces/classes/enums comes in the same ControlInterface node
HashSet<TypeElement> visitedNodes = new HashSet<TypeElement>();
for (AnnotationMirror am : controlInterfacesAnns) {
List<String> intrfcs = getElementValue(am, "value").accept(new ClassArrayValueGetter(), null);
List<String> inner = null;
AnnotationValue av = getElementValue(am, "encapsulates");
if (av != null) {
inner = av.accept(new ClassArrayValueGetter(), null);
}
List name = null;
av = getElementValue(am, "name");
if (av != null) {
name = (List) av.getValue();
}
for (int i = 0; i < intrfcs.size(); i++) {
String innerType = (inner != null && inner.size() > i) ? inner.get(i) : null;
StringBuilder one_interface_getter = replace(
(innerType != null) ? typed_interface_getter_source : interface_getter_source,
"$INTERFACE$", intrfcs.get(i));
if (innerType != null) {
replace(one_interface_getter, "$INTERFACE_TYPE$", innerType);
}
String nm;
if (name != null && name.size() > i) {
nm = (String) ((AnnotationValue) name.get(i)).getValue();
} else {
nm = "as" + getSimpleName(intrfcs.get(i));
}
replace(one_interface_getter, "$INTERFACE_GETTER$", nm);
interfaces.append(one_interface_getter);
}
interfaces.append(generateInterfaceShorcuts(am, visitedNodes));
}
visitedNodes.clear();
// for (AnnotationMirror am : controlInterfacesAnns) {
// String interfaceClass = getElementValue(am, "value").accept(new ClassArrayValueGetter(), null).get(0);
// String type = null;
// for (AnnotationMirror ami : controlInterfaceTypeAnns) {
// if (getElementValue(ami, "intrfc").accept(new ClassValueGetter(), null).equals(interfaceClass)) {
// type = getElementValue(ami, "incapsulates").accept(new ClassValueGetter(), null);
// }
// }
// StringBuilder one_interface_getter = replace(
// (type != null) ? typed_interface_getter_source : interface_getter_source,
// "$INTERFACE$", interfaceClass,
// "$INTERFACE_GETTER$", "as" + getSimpleName(interfaceClass));
// if (type != null) {
// replace(one_interface_getter, "$INTERFACE_TYPE$", type);
// }
// interfaces.append(one_interface_getter);
// }
replace(body, "$INTERFACES$", interfaces.toString());
//properties
StringBuilder properties = new StringBuilder();
//method properties
addReflectionProperties(properties, e, MethodProperties.class, method_property_getter_source);
addReflectionProperties(properties, e, FieldProperties.class, field_property_getter_source);
//declared properties
Map<String, String> declaresProperties = new HashMap<String, String>();
for (Element ie : ((TypeElement) e).getEnclosedElements()) {
for (AnnotationMirror am : findAnnotations(ie, Property.class)) {
declaresProperties.put((String) getElementValue(am, "value").getValue(),
((ExecutableElement) ie).getReturnType().toString());
String propString = (String) getElementValue(am, "value").getValue();
properties.append(replace(
anonymous ? anon_declared_property_getter_source : declared_property_getter_source,
"$PROP_NAME$", propString,
"$PROP_TYPE$", substitutePrimitiveType(((ExecutableElement) ie).getReturnType().toString()),
"$GETTER_NAME$", createGetterName(propString),
"$METHOD_NAME$", ((ExecutableElement) ie).getSimpleName().toString()));
}
}
replace(body, "$PROPERTIES$", properties.toString());
try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(dockPackage + "." + dockShortName);
Writer w = f.openWriter();
PrintWriter pw = new PrintWriter(w);
pw.print(body);
pw.flush();
pw.close();
} catch (IOException ex) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, ex.getMessage());
}
}
}
return true;
}
private String generateInterfaceShorcuts(AnnotationMirror am, HashSet<TypeElement> visitedNodes) {
StringBuilder shortcuts = new StringBuilder();
List<? extends AnnotationValue> interfaces = (List<? extends AnnotationValue>) getElementValue(am, "value").getValue();
for (AnnotationValue iface : interfaces) {
TypeElement e = iface.accept(new SimpleAnnotationValueVisitor6<TypeElement, Object>() {
@Override
public TypeElement visitType(TypeMirror t, Object p) {
return (TypeElement) ((DeclaredType) t).asElement();
}
}, null);
if(e.getKind() == ElementKind.CLASS){
shortcutsClassesBypass(e, shortcuts, visitedNodes);
}
else if(e.getKind() == ElementKind.INTERFACE || e.getKind() == ElementKind.ENUM)
shortcutsInterfacesBypass(e, shortcuts, visitedNodes);
}
return shortcuts.toString();
}
private void shortcutsClassesBypass(TypeElement e, StringBuilder shortcuts, HashSet<TypeElement> visitedNodes){
if(e == null)
return;
TypeMirror supperC = e.getSuperclass();
if(supperC.getKind() != TypeKind.NONE){
shortcutsClassesBypass((TypeElement) processingEnv.getTypeUtils().asElement(supperC), shortcuts, visitedNodes);
shortcutsInterfacesBypass(e, shortcuts, visitedNodes);
}else{ //in case when class directly extends ControlInterface and to handle classes with no super class
shortcutsInterfacesBypass(e, shortcuts, visitedNodes);
}
}
private void shortcutsInterfacesBypass(TypeElement e, StringBuilder shortcuts, HashSet<TypeElement> visitedNodes){
if(e == null)
return;
List<? extends TypeMirror> innerFaces = e.getInterfaces();
for(TypeMirror face : innerFaces){
if(IsAppropriateNode(face)){
shortcutsInterfacesBypass((TypeElement) processingEnv.getTypeUtils().asElement(face), shortcuts, visitedNodes);
}
}
if(IsAppropriateNode(e.asType())){
if(visitedNodes.add(e)){
handleInterfaceShorcuts(e, shortcuts);
}
}
}
private boolean IsAppropriateNode(TypeMirror eM){
return processingEnv.getTypeUtils().isSubtype(eM, processingEnv.getElementUtils().getTypeElement(ControlInterface.class.getName()).asType()) &&
!processingEnv.getTypeUtils().isSameType(eM, processingEnv.getElementUtils().getTypeElement(ControlInterface.class.getName()).asType());
}
private void handleInterfaceShorcuts(TypeElement e, StringBuilder shortcuts){
for (Element method : methodsIn(processingEnv.getElementUtils().getAllMembers(e))) {
//first condition is to define whether method is inherited or declared directly
if (method.getEnclosingElement() == e && method.getAnnotation(Shortcut.class) != null) {
StringBuilder setter = new StringBuilder().append(method.getAnnotation(Shortcut.class).name().equals("") ? ((ExecutableElement) method).getSimpleName() : method.getAnnotation(Shortcut.class).name()).append('(');
StringBuilder invokeSetter = new StringBuilder().append(((ExecutableElement) method).getSimpleName()).append('(');
final TypeMirror returnType = ((ExecutableElement) method).getReturnType();
String returnTypeString;
if(returnType.getKind() == TypeKind.TYPEVAR) {
returnTypeString = "Object";
} else {
returnTypeString = returnType.toString();
int genericStartIndex = returnType.toString().indexOf("<");
if(genericStartIndex > -1) {
returnTypeString = returnTypeString.substring(0, genericStartIndex) + "<?>";
}
}
boolean firstParameter = true;
for (VariableElement ve : ((ExecutableElement) method).getParameters()) {
if (!firstParameter) {
setter.append(", ");
invokeSetter.append(", ");
}
setter.append(ve.asType().toString()).append(" ").append(ve.getSimpleName());
invokeSetter.append(ve.getSimpleName());
firstParameter = false;
}
setter.append(")");
invokeSetter.append(")");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "generating setter " + setter);
shortcuts.append(replace(shortcut_methods_source,
"$RETURN_TYPE$", returnTypeString, "$RETURN_STATEMENT$", returnTypeString.equals("void") ? "" : "return ",
"$INTERFACE_SETTER$", setter.toString(), "$INVOKE_SETTER$", invokeSetter.toString(),
"$INTERFACE$", ((TypeElement) e).getQualifiedName().toString()));
}
}
}
private void addReflectionProperties(StringBuilder source, Element e, Class annClass, String template) {
List<AnnotationMirror> propsAnn = findAnnotations(e, annClass);
if (propsAnn.size() > 0) {
//List<?> props = (List<?>) getElementValue(propsAnn.get(0), "value").getValue();
List<String> props = getElementValue(propsAnn.get(0), "value").
accept(new StringArrayValueGetter(), null);
List<String> propsTypes;
AnnotationValue typesValue = getElementValue(propsAnn.get(0), "types");
if (typesValue != null) {
propsTypes = typesValue.accept(new ClassArrayValueGetter(), null);
} else {
propsTypes = new LinkedList<String>();
}
for (int i = 0; i < props.size(); i++) {
String propString = props.get(i);
String propType = (propsTypes.size() > i) ? propsTypes.get(i) : Object.class.getName();
source.append(replace(template,
"$PROP_NAME$", props.get(i),
"$PROP_TYPE$", substitutePrimitiveType(propType),
"$GETTER_NAME$", createGetterName(propString)));
}
}
}
private String toLowerCaseCamel(String camel) {
return camel.substring(0, 1).toLowerCase() + camel.substring(1);
}
private String toUpperCaseCamel(String camel) {
StringBuilder res = new StringBuilder(camel);
res.replace(0, 1, res.substring(0, 1).toUpperCase());
int dot = res.indexOf(".");
while (dot > 0) {
res.delete(dot, dot + 1);
if (res.length() > dot) {
res.replace(dot, dot + 1, res.substring(dot, dot + 1).toUpperCase());
}
dot = res.indexOf(".");
}
return res.toString();
}
private String unQuote(Object data) {
return data.toString().substring(1, data.toString().length() - 1);
}
private AnnotationValue getElementValue(AnnotationMirror am, String name) {
Map<? extends ExecutableElement, ? extends AnnotationValue> values = am.getElementValues();
for (ExecutableElement ee : values.keySet()) {
if (ee.getSimpleName().contentEquals(name)) {
return values.get(ee);
}
}
return null;
}
private List<AnnotationMirror> findAnnotations(Element e, Class annotationType) {
List<AnnotationMirror> res = new LinkedList<AnnotationMirror>();
for (AnnotationMirror am : e.getAnnotationMirrors()) {
if (am.getAnnotationType().toString().equals(annotationType.getName())) {
res.add(am);
}
}
return res;
}
private String getPackageName(String fullName) {
return fullName.substring(0, fullName.lastIndexOf("."));
}
private String getSimpleName(String className) {
return className.substring(className.lastIndexOf(".") + 1);
}
private String createGetterName(String propString) {
String result;
if (!propString.startsWith("get") && !propString.startsWith("is")) {
result = "get" + toUpperCaseCamel(propString);
} else {
result = propString;
}
return result.replace('.', '_');
}
private class ObjectLookupTable {
List<List<String>> types = new ArrayList<List<String>>();
List<List<String>> names = new ArrayList<List<String>>();
List<String> methodNames = new ArrayList<String>();
List<String> comments = new ArrayList<String>();
public ObjectLookupTable(TypeElement wrap) {
TypeMirror mirror = wrap.asType();
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "looking for object lookups");
while (!mirror.accept(new TypeNameGetter(), null).equals(Wrap.class.getName())) {
TypeElement te = (TypeElement) ((DeclaredType) mirror).asElement();
addObjectLookups(te);
mirror = (DeclaredType) te.getSuperclass();
}
}
private void addObjectLookups(TypeElement wrap) {
for (Element me : wrap.getEnclosedElements()) {
ObjectLookup ol = me.getAnnotation(ObjectLookup.class);
if (ol != null) {
boolean foundSameSignature = false;
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, me.getSimpleName());
List<String> tps = new ArrayList<String>();
List<String> nms = new ArrayList<String>();
List<? extends VariableElement> params = ((ExecutableElement) me).getParameters();
if (params.isEmpty()
|| !params.get(0).asType().toString().startsWith(Class.class.getName())) {
throw new IllegalStateException("Expect first parameter to ba a class but found "
+ (params.isEmpty() ? "none" : params.get(0).asType().toString()));
}
for (int i = 1; i < ((ExecutableElement) me).getParameters().size(); i++) {
VariableElement param = ((ExecutableElement) me).getParameters().get(i);
tps.add(param.asType().toString());
nms.add(param.getSimpleName().toString());
}
for (List<String> oneTps : types) {
foundSameSignature = compareParamTypesList(oneTps, tps);
if (foundSameSignature) {
break;
}
}
if (!foundSameSignature) {
types.add(tps);
names.add(nms);
methodNames.add(wrap.getQualifiedName() + "." + me.getSimpleName());
comments.add((ol.value().length() > 0) ? "by " + ol.value() : "");
}
}
}
}
private boolean compareParamTypesList(List<String> oneTps, List<String> tps) {
if (oneTps.size() != tps.size()) {
return false;
}
for (int i = 0; i < oneTps.size(); i++) {
if (!oneTps.get(i).equals(tps.get(i))) {
return false;
}
}
return true;
}
}
private class ClassValueGetter implements AnnotationValueVisitor<String, Object> {
public String visit(AnnotationValue av, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visit(AnnotationValue av) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitBoolean(boolean bln, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitByte(byte b, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitChar(char c, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitDouble(double d, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitFloat(float f, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitInt(int i, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitLong(long l, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitShort(short s, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitString(String string, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitType(TypeMirror tm, Object p) {
return tm.toString();
}
public String visitEnumConstant(VariableElement ve, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitAnnotation(AnnotationMirror am, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitArray(List<? extends AnnotationValue> list, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitUnknown(AnnotationValue av, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
private class ClassArrayValueGetter implements AnnotationValueVisitor<List<String>, Object> {
public List<String> visitArray(List<? extends AnnotationValue> list, Object p) {
List<String> result = new ArrayList<String>();
for (AnnotationValue av : list) {
result.add(av.accept(new ClassValueGetter(), null));
}
return result;
}
public List<String> visitBoolean(boolean bln, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitByte(byte b, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitChar(char c, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitDouble(double d, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitFloat(float f, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitInt(int i, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitLong(long l, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitShort(short s, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitString(String string, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitType(TypeMirror tm, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitEnumConstant(VariableElement ve, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitAnnotation(AnnotationMirror am, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visit(AnnotationValue av, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visit(AnnotationValue av) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitUnknown(AnnotationValue av, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
private static class TypeNameGetter extends AbstractTypeVisitor6<String, Object> {
public TypeNameGetter() {
}
public String visitDeclared(DeclaredType dt, Object p) {
return ((TypeElement) dt.asElement()).getQualifiedName().toString();
}
public String visitPrimitive(PrimitiveType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitNull(NullType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitArray(ArrayType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitError(ErrorType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitTypeVariable(TypeVariable t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitWildcard(WildcardType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitExecutable(ExecutableType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitNoType(NoType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
private static class StringArrayValueGetter extends AbstractAnnotationValueVisitor6<List<String>, Object> {
public StringArrayValueGetter() {
}
public List<String> visitArray(List<? extends AnnotationValue> list, Object p) {
List<String> res = new ArrayList<String>();
for (AnnotationValue av : list) {
res.add(av.getValue().toString());
}
return res;
}
public List<String> visitBoolean(boolean b, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitByte(byte b, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitChar(char c, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitDouble(double d, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitFloat(float f, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitInt(int i, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitLong(long i, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitShort(short s, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitString(String s, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitType(TypeMirror t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitEnumConstant(VariableElement c, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<String> visitAnnotation(AnnotationMirror a, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
private class SuperWrapClassNamesGetter extends AbstractTypeVisitor6<String, Object> {
public SuperWrapClassNamesGetter() {
}
public String visitDeclared(DeclaredType dt, Object p) {
AnnotationMirror controlTypeAnn = findAnnotations(dt.asElement(), ControlType.class).get(0);
return getElementValue(controlTypeAnn, "value").accept(new ClassArrayValueGetter(), null).get(0);
}
public String visitPrimitive(PrimitiveType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitNull(NullType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitArray(ArrayType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitError(ErrorType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitTypeVariable(TypeVariable t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitWildcard(WildcardType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitExecutable(ExecutableType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
public String visitNoType(NoType t, Object p) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
}