diff options
| author | Reinier Zwitserloot <reinier@tipit.to> | 2009-06-08 22:26:30 +0200 |
|---|---|---|
| committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-06-08 22:26:30 +0200 |
| commit | 1677c4a52a0aea1b955f7c2c7d096903d4a8c5ce (patch) | |
| tree | 29de8d12d774f026c0f317e229532befaeb2d096 | |
| download | lombok-1677c4a52a0aea1b955f7c2c7d096903d4a8c5ce.tar.gz lombok-1677c4a52a0aea1b955f7c2c7d096903d4a8c5ce.tar.bz2 lombok-1677c4a52a0aea1b955f7c2c7d096903d4a8c5ce.zip | |
Initial commit. As a proof of concept, it already works in javac and eclipse!
29 files changed, 1466 insertions, 0 deletions
diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..f54ce91b --- /dev/null +++ b/.classpath @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="lib" path="lib/asm-3.1.jar" sourcepath="contrib/asm-3.1-src.zip"> + <attributes> + <attribute name="javadoc_location" value="jar:platform:/resource/lombok/contrib/asm-3.1-javadoc.zip!/"/> + </attributes> + </classpathentry> + <classpathentry kind="lib" path="deps/org.eclipse.jdt.apt.core_3.3.200.v20090528-1135.jar"/> + <classpathentry kind="lib" path="deps/org.eclipse.jdt.apt.pluggable.core_1.0.200.v20090526-2130.jar"/> + <classpathentry kind="lib" path="deps/org.eclipse.jdt.compiler.apt_1.0.200.v20090528-1135.jar"/> + <classpathentry kind="lib" path="deps/org.eclipse.jdt.core_3.5.0.v_963.jar"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e811f014 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin +build +dist + diff --git a/.project b/.project new file mode 100644 index 00000000..628b6001 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>lombok</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..fbe9d485 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +#Thu Jun 04 03:58:11 CEST 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/build.xml b/build.xml new file mode 100644 index 00000000..f2d1b635 --- /dev/null +++ b/build.xml @@ -0,0 +1,49 @@ +<project name="lombok" default="dist"> + <property name="build.compiler" value="javac1.6" /> + <path id="deps.path"> + <fileset dir="deps"> + <include name="**/*.jar" /> + </fileset> + </path> + <path id="libs.path"> + <fileset dir="lib"> + <include name="**/*.jar" /> + </fileset> + </path> + + <target name="clean"> + <delete dir="build" quiet="true" /> + <delete dir="dist" quiet="true" /> + </target> + + <target name="compile"> + <mkdir dir="build" /> + <javac srcdir="src" debug="on" destdir="build"> + <classpath refid="deps.path" /> + <classpath refid="libs.path" /> + </javac> + <mkdir dir="build/META-INF" /> + <mkdir dir="build/META-INF/services" /> + <echo file="build/META-INF/services/javax.annotation.processing.Processor">lombok.apt.Processor</echo> + </target> + + <target name="unpackLibs"> + <unjar dest="build"> + <path refid="libs.path" /> + </unjar> + </target> + + <target name="dist" depends="clean, compile, unpackLibs"> + <mkdir dir="dist" /> + <tstamp> + <format property="distTime" pattern="yyyyMMdd'T'hhmmss" locale="en,US" /> + </tstamp> + <jar basedir="build" destfile="dist/lombok-${distTime}.jar"> + <manifest> + <attribute name="Premain-Class" value="lombok.agent.eclipse.EclipseParserPatcher" /> + <attribute name="Can-Redefine-Classes" value="true" /> + </manifest> + </jar> + <copy file="dist/lombok-${distTime}.jar" tofile="dist/lombok.jar" /> + </target> +</project> diff --git a/contrib/asm-3.1-javadoc.zip b/contrib/asm-3.1-javadoc.zip Binary files differnew file mode 100644 index 00000000..91ce1569 --- /dev/null +++ b/contrib/asm-3.1-javadoc.zip diff --git a/contrib/asm-3.1-src.zip b/contrib/asm-3.1-src.zip Binary files differnew file mode 100644 index 00000000..4ad32d54 --- /dev/null +++ b/contrib/asm-3.1-src.zip diff --git a/deps/org.eclipse.jdt.apt.core_3.3.200.v20090528-1135.jar b/deps/org.eclipse.jdt.apt.core_3.3.200.v20090528-1135.jar Binary files differnew file mode 100644 index 00000000..e42dc67f --- /dev/null +++ b/deps/org.eclipse.jdt.apt.core_3.3.200.v20090528-1135.jar diff --git a/deps/org.eclipse.jdt.apt.pluggable.core_1.0.200.v20090526-2130.jar b/deps/org.eclipse.jdt.apt.pluggable.core_1.0.200.v20090526-2130.jar Binary files differnew file mode 100644 index 00000000..73286f15 --- /dev/null +++ b/deps/org.eclipse.jdt.apt.pluggable.core_1.0.200.v20090526-2130.jar diff --git a/deps/org.eclipse.jdt.compiler.apt_1.0.200.v20090528-1135.jar b/deps/org.eclipse.jdt.compiler.apt_1.0.200.v20090528-1135.jar Binary files differnew file mode 100644 index 00000000..5a66398a --- /dev/null +++ b/deps/org.eclipse.jdt.compiler.apt_1.0.200.v20090528-1135.jar diff --git a/deps/org.eclipse.jdt.core_3.5.0.v_963.jar b/deps/org.eclipse.jdt.core_3.5.0.v_963.jar Binary files differnew file mode 100644 index 00000000..0db68569 --- /dev/null +++ b/deps/org.eclipse.jdt.core_3.5.0.v_963.jar diff --git a/doc/PlannedExtensions.txt b/doc/PlannedExtensions.txt new file mode 100644 index 00000000..d6c5ffd7 --- /dev/null +++ b/doc/PlannedExtensions.txt @@ -0,0 +1,45 @@ +Planned lombok features +======================= + +## @Getter + +Put on any field; like so: + + private @Getter AnyType foo; + +This will generate the following method: + + public AnyType getFoo() { + return foo; + } + +Optionally you can generate a different access level by specifying the `AccessLevel` in the annotation, like so: + + private @Getter(AccessLevel.PROTECTED) AnyType foo; + +## @Setter + +Like @Getter, but creates setters. + +## @Data + +Creates getters, setters (for non-final fields), toString, equals, and hashCode, as well as a constructor, or, if you wish, +a 'static factory method'. + +## @Property + +## @AutoClose + +## @Synchronized + +## @Generator + +## @SneakyThrows + +# Maybes: + +## @RunInEDT + +## @SaneEquals + +## List Comprehensions diff --git a/lib/asm-3.1.jar b/lib/asm-3.1.jar Binary files differnew file mode 100644 index 00000000..b3baf3fe --- /dev/null +++ b/lib/asm-3.1.jar diff --git a/original_panno/com/hanhuy/panno/Property.java b/original_panno/com/hanhuy/panno/Property.java new file mode 100644 index 00000000..5e02a536 --- /dev/null +++ b/original_panno/com/hanhuy/panno/Property.java @@ -0,0 +1,71 @@ +/* + * Copyright 2007 Perry Nguyen <pfnguyen@hanhuy.com> 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.hanhuy.panno; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A simple javabean property annotation. Used to generate get/set pairs for + * field members automatically. Valid options are: 'name', 'readOnly', + * 'writeOnly', 'useGet', 'preCallMethod', and 'preCallThrows'. + * <p> + *'name' may be used to set an alternative name for the property instead of + * defaulting to the field's name. + * <p> + *'readOnly' and 'writeOnly' may be set to true to create only a getter or a + * setter, respectively. + * <p> + *'useGet' is used to generate a getX instead of isX method if the property is + * a boolean. + * <p> + *'preCallMethod' is the name of the method to be invoked prior to setting the + * actual property value. It may be used for validation, or firing off + * PropertyChangeEvents. This method will be called as + * <code>preCallMethod(this, String propertyName, oldValue, newValue)</code>; + * since this occurs at compile time, there is no interface, so the types are + * entirely up to you to choose and make work. The method may also be any + * method, so long as it's accessible to the bean. Thus, + * <code>preCallMethod=anotherObject.validateProperty</code> would be ok, so + * long as <code>anotherObject</code> is a field within the bean. The object + * navigation leading up to the preCallMethod <b>cannot</b> contain any method + * invocations; e.g. + * <code>preCallMethod=someBean.someMethod().myPreCallMethod</code> would be + * illegal and cause an error. + * <p> + *'preCallThrows' specifies what exceptions can possibly be thrown by the + * preCallMethod. Any non-RuntimeExceptions <b>must</b> be specified here, or + * else it will result in a compile-time error. This information cannot be + * determined reflectively, because at compile-time, there is no reflection + * (reflection is for runtime use). All exception names must be imported and + * they may not be referred by their qualified name, e.g. "Exception", not + * "java.lang.Exception" + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface Property { + String name() default ""; + + boolean readOnly() default false; + + boolean writeOnly() default false; + + boolean useGet() default false; + + String[] preCallThrows() default { /* nothing */}; + + String preCallMethod() default ""; +} diff --git a/original_panno/com/hanhuy/panno/processing/ASTTreeToSource.java b/original_panno/com/hanhuy/panno/processing/ASTTreeToSource.java new file mode 100644 index 00000000..2e7dc641 --- /dev/null +++ b/original_panno/com/hanhuy/panno/processing/ASTTreeToSource.java @@ -0,0 +1,320 @@ +/* + * Copyright 2007 Perry Nguyen <pfnguyen@hanhuy.com> 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.hanhuy.panno.processing; + +import java.util.Map; +import java.util.Stack; + +import javax.annotation.processing.Messager; +import javax.lang.model.element.Modifier; +import javax.tools.Diagnostic; + +import com.hanhuy.panno.Property; +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreeScanner; + +/** + * Old attempt to get the annotation working, have a better approach now. + */ +@Deprecated +class ASTTreeToSource extends TreeScanner<Void, StringBuilder> { + public final static String PROPERTY_ANNOTATION_FULL = "com.hanhuy.panno.Property"; + public final static String PROPERTY_ANNOTATION = "Property"; + + private final Stack<String> currentClassNameStack = new Stack<String>(); + private final Messager messager; + private final Map<String, Property> fieldMap; + private String currentClassName; + private String currentField; + private String currentType; + + ASTTreeToSource(Map<String, Property> fieldMap, Messager messager) { + this.fieldMap = fieldMap; + this.messager = messager; + } + + /** + * Handle initializer blocks, both static and non. + */ + @Override + public Void visitBlock(BlockTree node, StringBuilder p) { + p.append("\n"); + if ( node.isStatic() ) p.append("static "); + p.append("{\n"); + for ( StatementTree st : node.getStatements() ) { + p.append(st); + p.append(";"); + } + p.append("\n}\n"); + return null; + } + + /** + * Detect our annotation and add accessors if necessary. + */ + @Override + public Void visitAnnotation(AnnotationTree node, StringBuilder p) { + String anno = ((IdentifierTree)node.getAnnotationType()).getName().toString(); + if ( anno != null && PROPERTY_ANNOTATION.equals(anno.trim()) ) { + Property pAnno = fieldMap.get(currentClassName + "." + currentField); + + String propName = currentField; + if ( !"".equals(pAnno.name()) ) propName = pAnno.name(); + propName = PropertyProcessor.ucfirst(propName); + + if ( pAnno.writeOnly() && pAnno.readOnly() ) { + messager.printMessage(Diagnostic.Kind.ERROR, "@Property's writeOnly and readOnly may not both be true"); + } + + if ( !"boolean".equals(currentType) && pAnno.useGet() ) { + messager.printMessage(Diagnostic.Kind.WARNING, "@Property's useGet is only for booleans"); + } + + if ( !pAnno.writeOnly() ) { + // generate a getter + p.append("\n"); + p.append("public "); + p.append(currentType); + if ( !"boolean".equals(currentType) || pAnno.useGet() ) p.append(" get"); + else p.append(" is"); + p.append(propName); + p.append("() { return "); + p.append(currentField); + p.append("; }"); + } + if ( !pAnno.readOnly() ) { + // generate a setter + p.append("\n"); + p.append("public void set"); + p.append(propName); + p.append("("); + p.append(currentType); + p.append(" "); + p.append(currentField); + p.append(") { this."); + p.append(currentField); + p.append(" = "); + p.append(currentField); + p.append("; }"); + } + } + return null; + } + + /** + * Gather file information. Then continue to visit classes. + */ + @Override + public Void visitCompilationUnit(CompilationUnitTree node, StringBuilder p) { + new Exception("Visit CUT: " + node.getSourceFile()).printStackTrace(); + p.append("// Generated from: "); + p.append(node.getSourceFile()); + p.append("\n\npackage "); + p.append(node.getPackageName()); + p.append(";\n\n"); + + for ( ImportTree it : node.getImports() ) { + if ( PROPERTY_ANNOTATION_FULL.equals(it.getQualifiedIdentifier().toString().trim()) ) { + continue; + } + p.append("import "); + if ( it.isStatic() ) p.append("static "); + p.append(it.getQualifiedIdentifier()); + p.append(";\n"); + } + super.visitCompilationUnit(node, p); + return null; + } + + private void pushClass(String className) { + currentClassName = className; + currentClassNameStack.push(currentClassName); + } + + private void popClass() { + currentClassNameStack.pop(); + if ( currentClassNameStack.size() > 0 ) currentClassName = currentClassNameStack.peek(); + } + + /** + * Reproduce class declarations, and go on to handle methods, blocks and + * variables. + */ + @Override + public Void visitClass(ClassTree node, StringBuilder p) { + pushClass(node.getSimpleName().toString()); + + // Check to see if this is an enum + // I have no idea what will happen if we encounter an AST + // implementation that is not Sun's (or if this has changed in java7) + if ( node instanceof com.sun.tools.javac.tree.JCTree.JCClassDecl ) { + com.sun.tools.javac.tree.JCTree.JCClassDecl jcd = (com.sun.tools.javac.tree.JCTree.JCClassDecl)node; + if ( (jcd.mods.flags & 16384L) != 0L ) { + p.append(node.getModifiers()); + p.append(" enum "); + p.append(currentClassName); + appendEnumBody(jcd.defs, p); + + popClass(); + + return null; + } + } + + p.append("\n"); + p.append(node.getModifiers()); + p.append(" class "); + p.append(currentClassName); + if ( node.getTypeParameters().size() > 0 ) { + p.append("<"); + p.append(node.getTypeParameters()); + p.append(">"); + } + if ( node.getExtendsClause() != null ) { + p.append("\nextends "); + p.append(node.getExtendsClause()); + } + if ( node.getImplementsClause().size() > 0 ) { + p.append("\nimplements "); + p.append(node.getImplementsClause()); + } + + p.append("\n{"); + super.visitClass(node, p); + p.append("\n}\n"); + + popClass(); + + return null; + } + + /** + * Enums appear to be a special case. We must reconstitute its source in a + * different manner from the rest. Need to remove any 'super()' statements + * and print the lines appropriately. + */ + public void appendEnumBody(com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> list, StringBuilder p) { + p.append("{\n"); + boolean flag = true; + + for ( com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> list1 = list; list1.nonEmpty(); list1 = list1.tail ) { + + if ( !isEnumerator(list1.head) ) continue; + + if ( !flag ) p.append(",\n"); + + p.append(list1.head); + flag = false; + } + + p.append(";\n"); + for ( com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree> list2 = list; list2.nonEmpty(); list2 = list2.tail ) { + if ( !isEnumerator(list2.head) ) { + if ( list2.head instanceof MethodTree ) visitMethod((MethodTree)list2.head, p); + else p.append(list2.head); + p.append("\n"); + } + } + + p.append("}"); + } + + private boolean isEnumerator(com.sun.tools.javac.tree.JCTree jctree) { + return jctree.tag == 5 && (((com.sun.tools.javac.tree.JCTree.JCVariableDecl)jctree).mods.flags & 16384L) != 0L; + } + + /** + * Reconstitute methods. We could just do node.toString() here, but + * constructors need to be renamed from <init> to the class name, so + * handle all methods. + */ + @Override + public Void visitMethod(MethodTree node, StringBuilder p) { + p.append("\n"); + p.append(node.getModifiers()); + p.append(" "); + if ( node.getReturnType() != null ) p.append(node.getReturnType()); + if ( node.getTypeParameters().size() > 0 ) { + p.append(" <"); + p.append(node.getTypeParameters()); + p.append(">"); + } + p.append(" "); + if ( node.getName() != null && "<init>".equals(node.getName().toString().trim()) ) p.append(currentClassName); + else p.append(node.getName()); + + p.append("("); + p.append(node.getParameters()); + p.append(")"); + if ( node.getThrows().size() > 0 ) { + p.append("\nthrows "); + p.append(node.getThrows()); + } + + p.append(" {\n"); + // this needs to be done for enums, otherwise we'll get a compile error + // if we didn't need this case, we'd just do p.append(node.getBody()) + for ( StatementTree st : node.getBody().getStatements() ) { + if ( "super()".equals(st.toString().trim()) ) continue; + p.append(st); + if ( p.charAt(p.length() - 1) != ';' ) p.append(";\n"); + } + p.append("\n}\n"); + return null; + } + + /** + * Reconstitute class fields. While doing so, search for our annotation to + * perform processing. + */ + @Override + public Void visitVariable(VariableTree node, StringBuilder p) { + currentField = node.getName().toString(); + currentType = node.getType().toString(); + p.append("\n"); + + for ( AnnotationTree a : node.getModifiers().getAnnotations() ) { + String anno = ((IdentifierTree)a.getAnnotationType()).getName().toString(); + if ( !PROPERTY_ANNOTATION.equals(anno) ) { + p.append(a); + p.append("\n"); + } + } + + for ( Modifier m : node.getModifiers().getFlags() ) { + p.append(m.toString()); + p.append(" "); + } + p.append(" "); + p.append(currentType); + p.append(" "); + p.append(currentField); + + if ( node.getInitializer() != null ) { + p.append(" = "); + p.append(node.getInitializer()); + } + p.append(";"); + super.visitVariable(node, p); + return null; + } +} diff --git a/original_panno/com/hanhuy/panno/processing/PropertyProcessor.java b/original_panno/com/hanhuy/panno/processing/PropertyProcessor.java new file mode 100644 index 00000000..e7bb4826 --- /dev/null +++ b/original_panno/com/hanhuy/panno/processing/PropertyProcessor.java @@ -0,0 +1,293 @@ +/* + * Copyright 2007 Perry Nguyen <pfnguyen@hanhuy.com> 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.hanhuy.panno.processing; + +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Messager; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.tools.Diagnostic; + +import com.hanhuy.panno.Property; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.Trees; + +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedAnnotationTypes( { "com.hanhuy.panno.Property" }) +public class PropertyProcessor extends AbstractProcessor { + + private Messager messager; + + /** + * Annotation processing entry point. Currently, we only support javac 6 + * from Sun's JDK. Other IDE support will have to follow. + */ + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { + com.sun.tools.javac.util.Context ctx = null; + + if ( processingEnv instanceof com.sun.tools.javac.processing.JavacProcessingEnvironment ) { + ctx = ((com.sun.tools.javac.processing.JavacProcessingEnvironment)processingEnv).getContext(); + } else { + messager.printMessage(Diagnostic.Kind.ERROR, "@Property processing is not supported on this compiler."); + } + + Trees trees = Trees.instance(processingEnv); + messager = processingEnv.getMessager(); + + Set<? extends Element> annoElements = env.getElementsAnnotatedWith(Property.class); + + for ( Element e : annoElements ) { + processAnnotation(trees, ctx, e, e.getAnnotation(Property.class)); + } + return true; + } + + private void processAnnotation(Trees trees, com.sun.tools.javac.util.Context ctx, Element e, Property p) { + TypeElement type = (TypeElement)findEnclosingType(e); + + if ( type == null ) { + messager.printMessage(Diagnostic.Kind.ERROR, "@Property annotated element is not a member field.", e); + } + ClassTree cls = trees.getTree(type); + com.sun.tools.javac.tree.JCTree.JCClassDecl cd = (com.sun.tools.javac.tree.JCTree.JCClassDecl)cls; + Object t = trees.getTree(e); + if ( !(t instanceof VariableTree) ) { + messager.printMessage(Diagnostic.Kind.ERROR, "@Property can only be annotated on a member field", e); + return; + } + + com.sun.tools.javac.util.Name.Table tab = com.sun.tools.javac.util.Name.Table.instance(ctx); + + com.sun.tools.javac.tree.TreeMaker maker = com.sun.tools.javac.tree.TreeMaker.instance(ctx); + + MethodTree m; + String[] pcts = p.preCallThrows(); + String pcm = p.preCallMethod(); + if ( p.readOnly() && ((pcm != null && !"".equals(pcm.trim())) || (pcts != null && pcts.length > 0)) ) { + messager.printMessage(Diagnostic.Kind.ERROR, "preCallMethod and/or preCallThrows can" + + "not be used with readOnly", e, getAnnotationMirror(e)); + } + if ( p.writeOnly() && p.useGet() ) { + messager.printMessage(Diagnostic.Kind.ERROR, "useGet is only for use with readable boolean properties", e, + getAnnotationMirror(e)); + } + if ( !p.readOnly() ) { + m = createSetter(e, p, maker, tab, trees); + cd.defs = cd.defs.append((com.sun.tools.javac.tree.JCTree)m); + } + if ( !p.writeOnly() ) { + m = createGetter(e, p, maker, tab); + cd.defs = cd.defs.append((com.sun.tools.javac.tree.JCTree)m); + } + } + + private String getPropertyName(Element e, Property p) { + String pname = p.name(); + if ( pname == null || "".equals(p.name().trim()) ) pname = e.getSimpleName().toString(); + return pname; + } + + @SuppressWarnings("unchecked") + private MethodTree createGetter(Element e, Property p, com.sun.tools.javac.tree.TreeMaker maker, + com.sun.tools.javac.util.Name.Table tab) { + + String pname = getPropertyName(e, p); + + com.sun.tools.javac.util.List nil = com.sun.tools.javac.util.List.nil(); + + com.sun.tools.javac.util.List statements = com.sun.tools.javac.util.List.of(maker.Return(maker + .Ident((com.sun.tools.javac.code.Symbol.VarSymbol)e))); + + String getterPrefix = "get"; + // check if boolean, use "is", otherwise "get" if useGet + if ( e.asType().getKind() == TypeKind.BOOLEAN ) { + if ( !p.useGet() ) getterPrefix = "is"; + } else { + if ( p.useGet() ) messager.printMessage(Diagnostic.Kind.WARNING, + "@Property(useGet=true) is for boolean properties", e); + } + + com.sun.tools.javac.util.Name n = com.sun.tools.javac.util.Name.fromString(tab, getterPrefix + ucfirst(pname)); + + MethodTree m = maker.MethodDef(maker.Modifiers(1L, nil), n, maker.Type((com.sun.tools.javac.code.Type)e + .asType()), nil, nil, nil, maker.Block(0, statements), null); + + return m; + } + + @SuppressWarnings("unchecked") + private MethodTree createSetter(Element e, Property p, com.sun.tools.javac.tree.TreeMaker maker, + com.sun.tools.javac.util.Name.Table tab, Trees trees) { + +// String pname = getPropertyName(e, p); + int pos = getAnnotationPosition(e, trees); + + com.sun.tools.javac.util.List nil = com.sun.tools.javac.util.List.nil(); + + com.sun.tools.javac.util.Name vn = com.sun.tools.javac.util.Name.fromString(tab, "__panno_Generated_" + + e.getSimpleName().toString()); + + com.sun.tools.javac.tree.JCTree.JCVariableDecl param = maker.Param(vn, (com.sun.tools.javac.code.Type)e + .asType(), null); + + com.sun.tools.javac.util.List<com.sun.tools.javac.tree.JCTree.JCStatement> statements = com.sun.tools.javac.util.List + .of(maker.Assignment((com.sun.tools.javac.code.Symbol.VarSymbol)e, maker.Ident(param))); + +// com.sun.tools.javac.util.Name n = com.sun.tools.javac.util.Name.fromString(tab, "set" + ucfirst(pname)); + + com.sun.tools.javac.util.List throwList = nil; + MethodInvocationTree mit = createPreCall(e, p, param, maker, tab, pos); + + if ( mit != null ) { + statements = statements.prepend(maker.Exec((com.sun.tools.javac.tree.JCTree.JCMethodInvocation)mit)); + if ( p.preCallThrows() != null && p.preCallThrows().length > 0 ) { + for ( String thrown : p.preCallThrows() ) { + com.sun.tools.javac.util.Name tn = com.sun.tools.javac.util.Name.fromString(tab, thrown); + + com.sun.tools.javac.code.Symbol.ClassSymbol clssym = new com.sun.tools.javac.code.Symbol.ClassSymbol( + 0, tn, null); + throwList = throwList.append(maker.Type(clssym.type).setPos(pos)); + // setpos to munge line number + } + } + + } else if ( p.preCallThrows() != null && p.preCallThrows().length > 0 ) { + messager.printMessage(Diagnostic.Kind.ERROR, "preCallThrows is only to be used with preCallMethod", e, + getAnnotationMirror(e)); + } + + com.sun.tools.javac.tree.JCTree.JCMethodDecl m = null/*maker.MethodDef(maker.Modifiers(1L, nil), n, maker + .Type(com.sun.tools.javac.code.Symtab.voidType), nil, com.sun.tools.javac.util.List.of(param), + throwList, maker.Block(0, statements), null)*/; + + // munge line numbers +// m.mods.pos = pos; +// m.pos = pos; + return m; + } + + private MethodInvocationTree createPreCall(Element e, Property p, + com.sun.tools.javac.tree.JCTree.JCVariableDecl newParam, com.sun.tools.javac.tree.TreeMaker maker, + com.sun.tools.javac.util.Name.Table tab, int pos) { + String pcm = p.preCallMethod(); + String pname = getPropertyName(e |
