/*
* Copyright 2011 ArcBees Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.gwtplatform.dispatch.annotation.processor;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import com.google.web.bindery.requestfactory.shared.Locator;
import com.gwtplatform.dispatch.annotation.GenProxy;
import com.gwtplatform.dispatch.annotation.UseProxy;
import com.gwtplatform.dispatch.annotation.UseProxyName;
import com.gwtplatform.dispatch.annotation.helper.InterfaceGenerationHelper;
import com.gwtplatform.dispatch.annotation.helper.ReflectionHelper;
/**
* Processes {@link GenProxy} annotations.
* <p/>
* {@link GenProxyProcessor} should only ever be called by tool infrastructure.
* See {@link javax.annotation.processing.Processor} for more details.
*/
@SupportedAnnotationTypes("com.gwtplatform.dispatch.annotation.GenProxy")
public class GenProxyProcessor extends GenProcessor {
@Override
public void process(final Element proxyElement) {
GenProxy genProxy = proxyElement.getAnnotation(GenProxy.class);
generateProxy(
proxyElement,
genProxy.targetPackage(),
genProxy.filterSetter(),
genProxy.filterGetter(),
genProxy.isEmbeddedType(),
getLocatorTypeMirror(genProxy)
);
}
protected void generateProxy(Element proxyElement, String targetPackage, String[] filterSetter,
String[] filterGetter, boolean isEmbeddedType, TypeMirror locatorType) {
InterfaceGenerationHelper writer = null;
try {
ReflectionHelper reflection = new ReflectionHelper(getEnvironment(), (TypeElement) proxyElement);
String proxyElementSimpleName = reflection.getSimpleClassName();
String proxyElementClassName = reflection.getClassName();
String proxyElementPackage = reflection.getPackageName();
// Magic: By default proxies should be available for client and server side.
String preparedProxyElementClassName = proxyElementClassName.replace(".server", ".shared");
String preparedProxyElementPackage = proxyElementPackage.replace(".server", ".shared");
if (targetPackage != null && !targetPackage.isEmpty()) {
// Prepare user defined proxy target package and do not replace server with shared.
preparedProxyElementClassName = targetPackage + "." + proxyElementSimpleName;
preparedProxyElementPackage = targetPackage;
}
String proxySimpleName = proxyElementSimpleName + "Proxy";
String proxyClassName = preparedProxyElementClassName + "Proxy";
@SuppressWarnings("resource")
Writer sourceWriter = getEnvironment().getFiler().createSourceFile(proxyClassName,
proxyElement).openWriter();
writer = new InterfaceGenerationHelper(sourceWriter);
writer.generatePackageDeclaration(preparedProxyElementPackage);
if (isEmbeddedType) {
generateValueProxyHeader(writer, reflection, proxyElementClassName, proxySimpleName);
} else {
generateEntityProxyHeader(writer, reflection, proxyElementClassName, proxySimpleName, locatorType);
}
writer.println();
Collection<VariableElement> allFields = reflection.getNonConstantFields();
// Generate getters.
Collection<VariableElement> getterFields = reflection.filterFields(allFields, filterGetter);
for (VariableElement getterField : getterFields) {
generateGetter(writer, getterField);
}
// Generate setters.
Collection<VariableElement> setterFields = reflection.filterFields(allFields, filterSetter);
for (VariableElement setterField : setterFields) {
generateSetter(writer, setterField);
}
writer.println();
writer.generateFooter();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (writer != null) {
writer.close();
}
}
}
protected void generateEntityProxyHeader(InterfaceGenerationHelper writer, ReflectionHelper reflection,
String proxyElementClassName, String proxySimpleName, TypeMirror locatorType) {
writer.generateImports(
"com.google.web.bindery.requestfactory.shared.ProxyFor",
"com.google.web.bindery.requestfactory.shared.EntityProxy",
"com.google.web.bindery.requestfactory.shared.EntityProxyId"
);
writer.println();
// Check for locator.
if (locatorType == null || Locator.class.getName().equals(locatorType.toString())) {
writer.generateAnnotation("ProxyFor", proxyElementClassName + ".class");
} else {
writer.println("@{0}(value = {1}.class, locator = {2}.class)",
"ProxyFor", proxyElementClassName,
locatorType.toString());
}
writer.generateInterfaceHeader(proxySimpleName, reflection.getClassRepresenter().getModifiers(),
"EntityProxy"
);
writer.println();
writer.generateEmptyMethodBody("stableId", "EntityProxyId<" + proxySimpleName + ">");
}
protected void generateValueProxyHeader(InterfaceGenerationHelper writer, ReflectionHelper reflection,
String proxyElementClassName, String proxySimpleName) {
writer.generateImports(
"com.google.web.bindery.requestfactory.shared.ProxyFor",
"com.google.web.bindery.requestfactory.shared.ValueProxy"
);
writer.println();
writer.generateAnnotation("ProxyFor", proxyElementClassName + ".class");
writer.generateInterfaceHeader(proxySimpleName, reflection.getClassRepresenter().getModifiers(), "ValueProxy");
}
protected void generateGetter(InterfaceGenerationHelper writer, VariableElement getterField) {
// Check for embedded types.
UseProxy useProxyAnnotation = getterField.getAnnotation(UseProxy.class);
UseProxyName useProxyNameAnnotation = getterField.getAnnotation(UseProxyName.class);
if (useProxyAnnotation != null) {
writer.generateGetter(getterField.getSimpleName().toString(), getProxyTypeMirrorName(useProxyAnnotation,
getterField.asType()));
} else if (useProxyNameAnnotation != null) {
writer.generateGetter(getterField.getSimpleName().toString(), useProxyNameAnnotation.value());
} else {
writer.generateGetter(getterField);
}
}
protected void generateSetter(InterfaceGenerationHelper writer, VariableElement setterField) {
// Check for embedded types.
UseProxy useProxyAnnotation = setterField.getAnnotation(UseProxy.class);
UseProxyName useProxyNameAnnotation = setterField.getAnnotation(UseProxyName.class);
if (useProxyAnnotation != null) {
writer.generateSetter(setterField.getSimpleName().toString(), getProxyTypeMirrorName(useProxyAnnotation,
setterField.asType()));
} else if (useProxyNameAnnotation != null) {
writer.generateSetter(setterField.getSimpleName().toString(), useProxyNameAnnotation.value());
} else {
writer.generateSetter(setterField);
}
}
/**
* Workaround for MirroredTypeException (Attempt to access Class object for TypeMirror).
*
* @see <a href="http://goo.gl/7ee2R">Getting class values from annotations in an annotationprocessor</a>
*/
protected final String getProxyTypeMirrorName(UseProxy useProxyAnnotation, TypeMirror originalTypeMirror) {
TypeMirror mirror = null;
try {
useProxyAnnotation.value();
} catch (MirroredTypeException e) {
mirror = e.getTypeMirror();
}
return nestIntoCollectionIfNecessary(mirror, originalTypeMirror);
}
private String nestIntoCollectionIfNecessary(TypeMirror mirror, TypeMirror originalTypeMirror) {
String originalTypeDeclaration = originalTypeMirror.toString();
// Shortcut if no generics can be found in the type declaration
if (!(originalTypeDeclaration.contains("<") && originalTypeDeclaration.contains(">"))) {
return mirror.toString();
}
String collectionClassName = originalTypeDeclaration.substring(0, originalTypeDeclaration.indexOf("<"));
Class collectionClass = tryRetrieveCollectionClass(collectionClassName);
StringBuilder builder = new StringBuilder();
if (collectionClass != null && (isAssignableToSet(collectionClass) || isAssignableToList(collectionClass))) {
builder.append(collectionClassName).append("<").append(mirror.toString()).append(">");
} else {
builder.append(mirror.toString());
}
return builder.toString();
}
private Class tryRetrieveCollectionClass(String collectionClassName) {
try {
return Class.forName(collectionClassName);
} catch (ClassNotFoundException e) {
printMessage("Potential collection class " + collectionClassName + " could not be found.");
}
return null;
}
private boolean isAssignableToSet(Class collectionClass) {
if (collectionClass.equals(Set.class)) {
return true;
}
for (Class interfaceClass : collectionClass.getInterfaces()) {
if (interfaceClass.equals(Set.class)) {
return true;
}
}
return false;
}
private boolean isAssignableToList(Class collectionClass) {
if (collectionClass.equals(List.class)) {
return true;
}
for (Class interfaceClass : collectionClass.getInterfaces()) {
if (interfaceClass.equals(List.class)) {
return true;
}
}
return false;
}
/**
* @see #getProxyTypeMirrorName(com.gwtplatform.dispatch.annotation.UseProxy, javax.lang.model.type.TypeMirror)
*/
protected final TypeMirror getLocatorTypeMirror(GenProxy genProxyAnnotation) {
TypeMirror mirror = null;
try {
genProxyAnnotation.locator();
} catch (MirroredTypeException e) {
mirror = e.getTypeMirror();
}
return mirror;
}
}