/******************************************************************************* * Copyright (c) 2008 Scott Stanchfield. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Scott Stanchfield - initial API and implementation *******************************************************************************/ package com.javadude.annotation.processors; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import com.javadude.annotation.Expose; import com.javadude.annotation.ExtractInterface; import com.sun.mirror.apt.AnnotationProcessor; import com.sun.mirror.apt.AnnotationProcessorEnvironment; import com.sun.mirror.declaration.AnnotationTypeDeclaration; import com.sun.mirror.declaration.ClassDeclaration; import com.sun.mirror.declaration.Declaration; import com.sun.mirror.declaration.MethodDeclaration; import com.sun.mirror.declaration.Modifier; import com.sun.mirror.declaration.PackageDeclaration; import com.sun.mirror.declaration.ParameterDeclaration; import com.sun.mirror.type.ReferenceType; // TODO copy javadoc to generated interface // TODO superinterfaces -- allows bean.extractInterface + this extract interface public class ExtractInterfaceAnnotationProcessor implements AnnotationProcessor { private AnnotationProcessorEnvironment env_; public ExtractInterfaceAnnotationProcessor(AnnotationProcessorEnvironment env) { env_ = env; } @Override public void process() { AnnotationTypeDeclaration extractInterfaceAnnotationDeclaration = (AnnotationTypeDeclaration) env_.getTypeDeclaration(ExtractInterface.class.getName()); for (Declaration extractInterfaceDeclaration : env_.getDeclarationsAnnotatedWith(extractInterfaceAnnotationDeclaration)) { try { if (!(extractInterfaceDeclaration instanceof ClassDeclaration)) { env_.getMessager().printError(extractInterfaceDeclaration.getPosition(), "You can only annotate class declarations with @ExtractInterface"); return; } ClassDeclaration classDeclaration = (ClassDeclaration) extractInterfaceDeclaration; PackageDeclaration packageDeclaration = classDeclaration.getPackage(); ExtractInterface extractInterfaceAnnotation = extractInterfaceDeclaration.getAnnotation(ExtractInterface.class); PrintWriter interfaceFile = null; try { interfaceFile = env_.getFiler().createSourceFile(packageDeclaration.getQualifiedName() + "." + extractInterfaceAnnotation.name()); interfaceFile.println("package " + packageDeclaration.getQualifiedName() + ';'); interfaceFile.println(); if (!extractInterfaceAnnotation.nonPublic()) { interfaceFile.print("public "); } interfaceFile.print("interface " + extractInterfaceAnnotation.name()); String[] superinterfaces = extractInterfaceAnnotation.superinterfaces(); if (superinterfaces.length > 0) { interfaceFile.print(" extends "); boolean first = true; for (String superinterface : superinterfaces) { if (first) first = false; else interfaceFile.print(","); interfaceFile.print(superinterface); } } interfaceFile.println(" {"); Collection<Declaration> exposedMethods; AnnotationTypeDeclaration exposeAnnotationDeclaration = (AnnotationTypeDeclaration) env_.getTypeDeclaration(Expose.class.getName()); exposedMethods = env_.getDeclarationsAnnotatedWith(exposeAnnotationDeclaration); if (extractInterfaceAnnotation.exposeAllPublicMethods()) { if (!exposedMethods.isEmpty()) { for (Declaration declaration : exposedMethods) { // TODO set error message on @Expose annotation, not method name env_.getMessager().printError(declaration.getPosition(), "If @ExtractInterface.exposeAllPublicMethods==true, you cannot specify @Expose on individual methods"); } env_.getMessager().printError(extractInterfaceDeclaration.getPosition(), "If @ExtractInterface.exposeAllPublicMethods==true, you cannot specify @Expose on individual methods"); return; } // all non-static public methods should be exposed exposedMethods = new ArrayList<Declaration>(); Collection<MethodDeclaration> methods = classDeclaration.getMethods(); for (MethodDeclaration methodDeclaration : methods) { if (methodDeclaration.getModifiers().contains(Modifier.PUBLIC) && !methodDeclaration.getModifiers().contains(Modifier.STATIC)) { exposedMethods.add(methodDeclaration); } } } for (Declaration exposedDeclaration : exposedMethods) { // @Target forces these to only be on methods -- no extra check needed MethodDeclaration exposedMethod = (MethodDeclaration) exposedDeclaration; if (!exposedMethod.getModifiers().contains(Modifier.PUBLIC)) { env_.getMessager().printError(extractInterfaceDeclaration.getPosition(), "You can only @Expose public methods"); return; } interfaceFile.print(" " + Utils.getTypeName(exposedMethod.getReturnType()) + " " + exposedMethod.getSimpleName() + '('); boolean first = true; Collection<ParameterDeclaration> parameters = exposedMethod.getParameters(); for (ParameterDeclaration parameterDeclaration : parameters) { if (first) { first = false; } else { interfaceFile.print(", "); } interfaceFile.print(Utils.getTypeName(parameterDeclaration.getType()) + ' ' + parameterDeclaration.getSimpleName()); } interfaceFile.print(")"); Collection<ReferenceType> thrownTypes = exposedMethod.getThrownTypes(); first = true; if (!thrownTypes.isEmpty()) { interfaceFile.print(" throws "); for (ReferenceType thrownType : thrownTypes) { if (first) { first = false; } else { interfaceFile.print(", "); } interfaceFile.print(Utils.getTypeName(thrownType)); } } interfaceFile.println(";"); } interfaceFile.println("}"); } finally { if (interfaceFile != null) { interfaceFile.close(); } } } catch (ThreadDeath e) { throw e; } catch (Throwable t) { env_.getMessager().printError(extractInterfaceDeclaration.getPosition(), "Unexpected exception: " + t.getMessage()); } } } }