diff options
Diffstat (limited to 'src/lombok/core')
-rw-r--r-- | src/lombok/core/AST.java | 177 | ||||
-rw-r--r-- | src/lombok/core/AnnotationValues.java | 95 | ||||
-rw-r--r-- | src/lombok/core/PrintAST.java | 36 | ||||
-rw-r--r-- | src/lombok/core/SpiLoadUtil.java | 60 | ||||
-rw-r--r-- | src/lombok/core/TransformationsUtil.java | 73 | ||||
-rw-r--r-- | src/lombok/core/TypeLibrary.java | 40 | ||||
-rw-r--r-- | src/lombok/core/TypeResolver.java | 33 | ||||
-rw-r--r-- | src/lombok/core/Version.java | 30 |
8 files changed, 522 insertions, 22 deletions
diff --git a/src/lombok/core/AST.java b/src/lombok/core/AST.java index 24e128b2..ef752d1a 100644 --- a/src/lombok/core/AST.java +++ b/src/lombok/core/AST.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import static lombok.Lombok.sneakyThrow; @@ -14,7 +35,15 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +/** + * Lombok wraps the AST produced by a target platform into its own AST system, mostly because both eclipse and javac + * do not allow upward traversal (from a method to its owning type, for example). + * + * @param N The common type of all AST nodes in the internal representation of the target platform. + * For example, JCTree for javac, and ASTNode for eclipse. + */ public abstract class AST<N> { + /** The kind of node represented by a given AST.Node object. */ public enum Kind { COMPILATION_UNIT, TYPE, FIELD, INITIALIZER, METHOD, ANNOTATION, ARGUMENT, LOCAL, STATEMENT; } @@ -28,29 +57,52 @@ public abstract class AST<N> { this.fileName = fileName == null ? "(unknown).java" : fileName; } + /** Set the node object that wraps the internal Compilation Unit node. */ protected void setTop(Node top) { this.top = top; } + /** + * Return the content of the package declaration on this AST's top (Compilation Unit) node. + * + * Example: "java.util". + */ public abstract String getPackageDeclaration(); + /** + * Return the contents of each non-static import statement on this AST's top (Compilation Unit) node. + * + * Example: "java.util.IOException". + */ public abstract Collection<String> getImportStatements(); - protected <T extends Node> T putInMap(T parent) { - nodeMap.put(parent.get(), parent); - identityDetector.put(parent.get(), null); - return parent; + /** + * Puts the given node in the map so that javac/eclipse's own internal AST object can be translated to + * an AST.Node object. Also registers the object as visited to avoid endless loops. + */ + protected <T extends Node> T putInMap(T node) { + nodeMap.put(node.get(), node); + identityDetector.put(node.get(), null); + return node; } + /** Returns the node map, that can map javac/eclipse internal AST objects to AST.Node objects. */ protected Map<N, Node> getNodeMap() { return nodeMap; } + /** Clears the registry that avoids endless loops, and empties the node map. The existing node map + * object is left untouched, and instead a new map is created. */ protected void clearState() { identityDetector = new IdentityHashMap<N, Void>(); nodeMap = new IdentityHashMap<N, Node>(); } + /** + * Marks the stated node as handled (to avoid endless loops if 2 nodes refer to each other, or a node + * refers to itself). Will then return true if it was already set as handled before this call - in which + * case you should do nothing lest the AST build process loops endlessly. + */ protected boolean setAndGetAsHandled(N node) { if ( identityDetector.containsKey(node) ) return true; identityDetector.put(node, null); @@ -61,10 +113,12 @@ public abstract class AST<N> { return fileName; } + /** The AST.Node object representing the Compilation Unit. */ public Node top() { return top; } + /** Maps a javac/eclipse internal AST Node to the appropriate AST.Node object. */ public Node get(N node) { return nodeMap.get(node); } @@ -86,14 +140,33 @@ public abstract class AST<N> { return targetNode; } + /** An instance of this class wraps an eclipse/javac internal node object. */ public abstract class Node { protected final Kind kind; protected final N node; protected final List<? extends Node> children; protected Node parent; + + /** This flag has no specified meaning; you can set and retrieve it. + * + * In practice, for annotation nodes it means: Some AnnotationHandler finished whatever changes were required, + * and for all other nodes it means: This node was made by a lombok operation. + */ protected boolean handled; + + /** structurally significant are those nodes that can be annotated in java 1.6 or are method-like toplevels, + * so fields, local declarations, method arguments, methods, types, the Compilation Unit itself, and initializers. */ protected boolean isStructurallySignificant; + /** + * Creates a new Node object that represents the provided node. + * + * Make sure you manually set the parent correctly. + * + * @param node The AST object in the target parser's own internal AST tree that this node object will represent. + * @param children A list of child nodes. Passing in null results in the children list being empty, not null. + * @param kind The kind of node represented by this object. + */ protected Node(N node, List<? extends Node> children, Kind kind) { this.kind = kind; this.node = node; @@ -102,29 +175,57 @@ public abstract class AST<N> { this.isStructurallySignificant = calculateIsStructurallySignificant(); } + /** {@inheritDoc} */ @Override public String toString() { return String.format("NODE %s (%s) %s%s", kind, node == null ? "(NULL)" : node.getClass(), handled ? "[HANDLED]" : "", node == null ? "" : node); } + /** + * Convenient shortcut to the owning JavacAST object's getPackageDeclaration method. + * + * @see AST#getPackageDeclaration() + */ public String getPackageDeclaration() { return AST.this.getPackageDeclaration(); } + /** + * Convenient shortcut to the owning JavacAST object's getImportStatements method. + * + * @see AST#getImportStatements() + */ public Collection<String> getImportStatements() { return AST.this.getImportStatements(); } + /** + * See {@link #isStructurallySignificant}. + */ protected abstract boolean calculateIsStructurallySignificant(); + /** + * Convenient shortcut to the owning JavacAST object's getNodeFor method. + * + * @see AST#getNodeFor() + */ public Node getNodeFor(N obj) { return AST.this.get(obj); } + /** + * @return The javac/eclipse internal AST object wrapped by this AST.Node object. + */ public N get() { return node; } + /** + * Replaces the AST node represented by this node object with the provided node. This node must + * have a parent, obviously, for this to work. + * + * Also affects the underlying (eclipse/javac) AST. + */ @SuppressWarnings("unchecked") public Node replaceWith(N newN, Kind kind) { Node newNode = buildTree(newN, kind); @@ -137,6 +238,11 @@ public abstract class AST<N> { return newNode; } + /** + * Replaces the stated node with a new one. The old node must be a direct child of this node. + * + * Also affects the underlying (eclipse/javac) AST. + */ public void replaceChildNode(N oldN, N newN) { replaceStatementInNode(get(), oldN, newN); } @@ -169,27 +275,58 @@ public abstract class AST<N> { return parent; } + /** + * Returns all children nodes. + * + * A copy is created, so changing the list has no effect. Also, while iterating through this list, + * you may add, remove, or replace children without causing ConcurrentModificationExceptions. + */ public Collection<? extends Node> down() { return new ArrayList<Node>(children); } + /** + * returns the value of the 'handled' flag. + * + * @see #handled + */ public boolean isHandled() { return handled; } + /** + * Sets the handled flag, then returns 'this'. + * + * @see #handled + */ public Node setHandled() { this.handled = true; return this; } + /** + * Convenient shortcut to the owning JavacAST object's top method. + * + * @see AST#top() + */ public Node top() { return top; } + /** + * Convenient shortcut to the owning JavacAST object's getFileName method. + * + * @see AST#getFileName() + */ public String getFileName() { return fileName; } + /** + * Adds the stated node as a direct child of this node. + * + * Does not change the underlying (javac/eclipse) AST, only the wrapped view. + */ @SuppressWarnings("unchecked") public Node add(N newChild, Kind kind) { Node n = buildTree(newChild, kind); if ( n == null ) return null; @@ -221,18 +358,30 @@ public abstract class AST<N> { nodeMap.remove(get()); } + /** + * Removes the stated node, which must be a direct child of this node, from the AST. + * + * Does not change the underlying (javac/eclipse) AST, only the wrapped view. + */ public void removeChild(Node child) { children.remove(child); } + /** + * Sets the handled flag on this node, and all child nodes, then returns this. + * + * @see #handled + */ public Node recursiveSetHandled() { this.handled = true; for ( Node child : children ) child.recursiveSetHandled(); return this; } + /** Generate a compiler error on this node. */ public abstract void addError(String message); + /** Generate a compiler warning on this node. */ public abstract void addWarning(String message); /** @@ -245,10 +394,16 @@ public abstract class AST<N> { } } + /** Build an AST.Node object for the stated internal (javac/eclipse) AST Node object. */ protected abstract Node buildTree(N item, Kind kind); + /** + * Represents a field that contains AST children. + */ protected static class FieldAccess { + /** The actual field. */ public final Field field; + /** Dimensions of the field. Works for arrays, or for java.util.collections. */ public final int dim; FieldAccess(Field field, int dim) { @@ -258,6 +413,11 @@ public abstract class AST<N> { } private static Map<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new HashMap<Class<?>, Collection<FieldAccess>>(); + + /** Returns FieldAccess objects for the stated class. Each field that contains objects of the kind returned by + * {@link #getStatementTypes()}, either directly or inside of an array or java.util.collection (or array-of-arrays, + * or collection-of-collections, etcetera), is returned. + */ protected Collection<FieldAccess> fieldsOf(Class<?> c) { Collection<FieldAccess> fields = fieldsOfASTClasses.get(c); if ( fields != null ) return fields; @@ -305,14 +465,23 @@ public abstract class AST<N> { } else return Object.class; } + /** + * The supertypes which are considered AST Node children. Usually, the Statement, and the Expression, + * though some platforms (such as eclipse) group these under one common supertype. */ protected abstract Collection<Class<? extends N>> getStatementTypes(); + /** + * buildTree implementation that uses reflection to find all child nodes by way of inspecting + * the fields. */ protected <T extends Node> Collection<T> buildWithField(Class<T> nodeType, N statement, FieldAccess fa) { List<T> list = new ArrayList<T>(); buildWithField0(nodeType, statement, fa, list); return list; } + /** + * Uses reflection to find the given direct child on the given statement, and replace it with a new child. + */ protected boolean replaceStatementInNode(N statement, N oldN, N newN) { for ( FieldAccess fa : fieldsOf(statement.getClass()) ) { if ( replaceStatementInField(fa, statement, oldN, newN) ) return true; diff --git a/src/lombok/core/AnnotationValues.java b/src/lombok/core/AnnotationValues.java index 432d507f..ee434f1f 100644 --- a/src/lombok/core/AnnotationValues.java +++ b/src/lombok/core/AnnotationValues.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import java.lang.annotation.Annotation; @@ -10,13 +31,23 @@ import java.util.Collections; import java.util.List; import java.util.Map; +/** + * Represents a single annotation in a source file and can be used to query the parameters present on it. + */ public class AnnotationValues<A extends Annotation> { private final Class<A> type; private final Map<String, AnnotationValue> values; private final AST<?>.Node ast; - + + /** + * Represents a single method on the annotation class. For example, the value() method on the Getter annotation. + */ public static class AnnotationValue { + /** A list of the raw expressions. List is size 1 unless an array is provided. */ public final List<String> raws; + + /** Guesses for each raw expression. If the raw expression is a literal expression, the guess will + * likely be right. If not, it'll be wrong. */ public final List<Object> valueGuesses; private final AST<?>.Node node; @@ -33,7 +64,9 @@ public class AnnotationValues<A extends Annotation> { this.valueGuesses = Collections.singletonList(valueGuess); } - /** When the value is an array type. */ + /** + * Like the other constructor, but used for when the annotation method is initialized with an array value. + */ public AnnotationValue(AST<?>.Node node, List<String> raws, List<Object> valueGuesses) { this.node = node; this.raws = raws; @@ -52,21 +85,39 @@ public class AnnotationValues<A extends Annotation> { node.addError(message); } + /** {@inheritDoc} */ @Override public String toString() { return "raws: " + raws + " valueGuesses: " + valueGuesses; } } + /** + * Creates a new AnnotationValues. + * + * @param type The annotation type. For example, "Getter.class" + * @param values a Map of method names to AnnotationValue instances, for example 'value -> annotationValue instance'. + * @param ast The Annotation node. + */ public AnnotationValues(Class<A> type, Map<String, AnnotationValue> values, AST<?>.Node ast) { this.type = type; this.values = values; this.ast = ast; } + /** + * Thrown on the fly if an actual annotation instance procured via the {@link #getInstance()} method is queried + * for a method for which this AnnotationValues instance either doesn't have a guess or can't manage to fit + * the guess into the required data type. + */ public static class AnnotationValueDecodeFail extends RuntimeException { private static final long serialVersionUID = 1L; + /** The index into an array initializer (e.g. if the second value in an array initializer is + * an integer constant expression like '5+SomeOtherClass.CONSTANT', this exception will be thrown, + * and you'll get a '1' for idx. */ public final int idx; + + /** The AnnotationValue object that goes with the annotation method for which the failure occurred. */ public final AnnotationValue owner; public AnnotationValueDecodeFail(AnnotationValue owner, String msg, int idx) { @@ -83,8 +134,15 @@ public class AnnotationValues<A extends Annotation> { private A cachedInstance = null; + /** + * Creates an actual annotation instance. You can use this to query any annotation methods, except for + * those annotation methods with class literals, as those can most likely not be turned into Class objects. + * + * If some of the methods cannot be implemented, this method still works; it's only when you call a method + * that has a problematic value that an AnnotationValueDecodeFail exception occurs. + */ @SuppressWarnings("unchecked") - public A getInstance() throws AnnotationValueDecodeFail { + public A getInstance() { if ( cachedInstance != null ) return cachedInstance; InvocationHandler invocations = new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -217,16 +275,32 @@ public class AnnotationValues<A extends Annotation> { "Can't translate a " + guess.getClass() + " to the expected " + expected, pos); } + /** + * Returns the raw expressions used for the provided annotationMethodName. + * + * You should use this method for annotation methods that return Class objects. Remember that + * class literals end in ".class" which you probably want to strip off. + */ public List<String> getRawExpressions(String annotationMethodName) { AnnotationValue v = values.get(annotationMethodName); return v == null ? Collections.<String>emptyList() : v.raws; } + /** + * Convenience method to return the first result in a {@link getRawExpressions(String)} call. + * + * You should use this method if the annotation method is not an array type. + */ public String getRawExpression(String annotationMethodName) { List<String> l = getRawExpressions(annotationMethodName); return l.isEmpty() ? null : l.get(0); } + /** + * Attempts to translate class literals to their fully qualified names, such as 'Throwable.class' to 'java.lang.Throwable'. + * + * This process is at best a guess, but it will take into account import statements. + */ public List<String> getProbableFQTypes(String annotationMethodName) { List<String> result = new ArrayList<String>(); AnnotationValue v = values.get(annotationMethodName); @@ -236,6 +310,16 @@ public class AnnotationValues<A extends Annotation> { return result; } + /** + * Convenience method to return the first result in a {@link getProbableFQType(String)} call. + * + * You should use this method if the annotation method is not an array type. + */ + public String getProbableFQType(String annotationMethodName) { + List<String> l = getProbableFQTypes(annotationMethodName); + return l.isEmpty() ? null : l.get(0); + } + private String toFQ(String typeName) { Class<?> c; boolean fqn = typeName.indexOf('.') > -1; @@ -279,9 +363,4 @@ public class AnnotationValues<A extends Annotation> { return null; } } - - public String getProbableFQType(String annotationMethodName) { - List<String> l = getProbableFQTypes(annotationMethodName); - return l.isEmpty() ? null : l.get(0); - } } diff --git a/src/lombok/core/PrintAST.java b/src/lombok/core/PrintAST.java index 6d1eaafd..943bf298 100644 --- a/src/lombok/core/PrintAST.java +++ b/src/lombok/core/PrintAST.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import java.lang.annotation.ElementType; @@ -5,14 +26,25 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Will print the tree structure of annotated node and all its children. + * + * This annotation is useful only for those working on Lombok, for example to test if a Lombok handlers is doing its + * job correctly, or to see what the imagined endresult of a transformation is supposed to look like. + */ @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.SOURCE) public @interface PrintAST { + /** + * Normally, the AST is printed to standard out, but you can pick a filename instead. Useful for many IDEs + * which don't have a console unless you start them from the command line. + */ String outfile() default ""; /** - * Normally, the printer will print each node focusing on the node (E.g. classname, and such). By setting printContent to true, - * methods, initializers, and other statement-containing elements actually print their java code instead of element class names. + * Normally, the printer will print each node focusing on the node (E.g. classname, and such). + * By setting printContent to true, methods, initializers, and other statement-containing elements + * actually print their java code instead of a tree view of internal AST nodes. */ boolean printContent() default false; } diff --git a/src/lombok/core/SpiLoadUtil.java b/src/lombok/core/SpiLoadUtil.java index b26dd747..b3b70f27 100644 --- a/src/lombok/core/SpiLoadUtil.java +++ b/src/lombok/core/SpiLoadUtil.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import java.io.BufferedReader; @@ -16,13 +37,44 @@ import java.util.Set; import lombok.Lombok; +/** + * The java core libraries have a SPI discovery system, but it works only in Java 1.6 and up. For at least eclipse, + * lombok actually works in java 1.5, so we've rolled our own SPI discovery system. + * + * It is not API compatible with <code>ServiceLoader</code>. + * + * @see java.util.ServiceLoader + */ public class SpiLoadUtil { - private SpiLoadUtil() {} + private SpiLoadUtil() { + //Prevent instantiation + } + /** + * Returns an iterator of instances that, at least according to the spi discovery file, are implementations + * of the stated class. + * + * Like ServiceLoader, each listed class is turned into an instance by calling the public no-args constructor. + * + * Convenience method that calls the more elaborate {@link #findServices(Class, ClassLoader)} method with + * this {@link java.lang.Thread}'s context class loader as <code>ClassLoader</code>. + * + * @param target class to find implementations for. + */ public static <C> Iterator<C> findServices(Class<C> target) throws IOException { return findServices(target, Thread.currentThread().getContextClassLoader()); } + /** + * Returns an iterator of class objects that, at least according to the spi discovery file, are implementations + * of the stated class. + * + * Like ServiceLoader, each listed class is turned into an instance by calling the public no-args constructor. + * + * @param target class to find implementations for. + * @param loader The classloader object to use to both the spi discovery files, as well as the loader to use + * to make the returned instances. + */ public static <C> Iterator<C> findServices(final Class<C> target, final ClassLoader loader) throws IOException { Enumeration<URL> resources = loader.getResources("META-INF/services/" + target.getName()); final Set<String> entries = new LinkedHashSet<String>(); @@ -70,6 +122,12 @@ public class SpiLoadUtil { } } + /** + * This method will find the <code>T</code> in <code>public class Foo extends BaseType<T>. + * + * It returns an annotation type because it is used exclusively to figure out which annotations are + * being handled by {@link lombok.eclipse.EclipseAnnotationHandler} and {@link lombok.javac.JavacAnnotationHandler}. + */ @SuppressWarnings("unchecked") public static Class<? extends Annotation> findAnnotationClass(Class<?> c, Class<?> base) { if ( c == Object.class || c == null ) return null; diff --git a/src/lombok/core/TransformationsUtil.java b/src/lombok/core/TransformationsUtil.java index 8aea09a4..6b454b51 100644 --- a/src/lombok/core/TransformationsUtil.java +++ b/src/lombok/core/TransformationsUtil.java @@ -1,8 +1,50 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; +/** + * Container for static utility methods useful for some of the standard lombok transformations, regardless of + * target platform (e.g. useful for both javac and eclipse lombok implementations). + */ public class TransformationsUtil { - private TransformationsUtil() {} + private TransformationsUtil() { + //Prevent instantiation + } + /** + * Generates a getter name from a given field name. + * + * Strategy: + * + * First, pick a prefix. 'get' normally, but 'is' if <code>isBoolean</code> is true. + * + * Then, check if the first character of the field is lowercase. If so, check if the second character + * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character. + * + * return the prefix plus the possibly title/uppercased first character, and the rest of the field name. + * + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide <code>false</code>. + */ public static String toGetterName(CharSequence fieldName, boolean isBoolean) { final String prefix = isBoolean ? "is" : "get"; @@ -11,16 +53,33 @@ public class TransformationsUtil { return buildName(prefix, fieldName.toString()); } + /** + * Generates a getter name from a given field name. + * + * Strategy: + * + * Check if the first character of the field is lowercase. If so, check if the second character + * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character. + * + * return "set" plus the possibly title/uppercased first character, and the rest of the field name. + * + * @param fieldName the name of the field. + */ + public static String toSetterName(CharSequence fieldName) { + return buildName("set", fieldName.toString()); + } + private static String buildName(String prefix, String suffix) { if ( suffix.length() == 0 ) return prefix; char first = suffix.charAt(0); - if ( Character.isLowerCase(first) ) - suffix = String.format("%s%s", Character.toTitleCase(first), suffix.subSequence(1, suffix.length())); + if ( Character.isLowerCase(first) ) { + boolean useUpperCase = suffix.length() > 2 && + (Character.isTitleCase(suffix.charAt(1)) || Character.isUpperCase(suffix.charAt(1))); + suffix = String.format("%s%s", + useUpperCase ? Character.toUpperCase(first) : Character.toTitleCase(first), + suffix.subSequence(1, suffix.length())); + } return String.format("%s%s", prefix, suffix); } - - public static String toSetterName(CharSequence fieldName) { - return buildName("set", fieldName.toString()); - } } diff --git a/src/lombok/core/TypeLibrary.java b/src/lombok/core/TypeLibrary.java index c6789d7e..41b70a68 100644 --- a/src/lombok/core/TypeLibrary.java +++ b/src/lombok/core/TypeLibrary.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import java.util.Collection; @@ -7,9 +28,23 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +/** + * Library of types, which can be used to look up potential matching types. + * + * For example, if you put 'foo.Spork' and 'bar.Spork' into the library, and then ask for + * all compatible types given the type 'Spork', you'll get both of them, but you'll only + * get the one if you ask for compatible types given 'foo.Spork'. + * + * Useful to 'guess' if a given annotation AST node matches an annotation handler's target annotation. + */ public class TypeLibrary { private final Map<String, Set<String>> simpleToQualifiedMap = new HashMap<String, Set<String>>(); + /** + * Add a type to the library. + * + * @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'. + */ public void addType(String fullyQualifiedTypeName) { int idx = fullyQualifiedTypeName.lastIndexOf('.'); if ( idx == -1 ) throw new IllegalArgumentException( @@ -32,6 +67,11 @@ public class TypeLibrary { return this; } + /** + * Returns all items in the type library that may be a match to the provided type. + * + * @param typeReference something like 'String' or even 'java.lang.String'. + */ public Collection<String> findCompatible(String typeReference) { Set<String> result = simpleToQualifiedMap.get(typeReference); return result == null ? Collections.<String>emptySet() : result; diff --git a/src/lombok/core/TypeResolver.java b/src/lombok/core/TypeResolver.java index 1e356f89..3adc82ce 100644 --- a/src/lombok/core/TypeResolver.java +++ b/src/lombok/core/TypeResolver.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import java.util.Collection; @@ -7,10 +28,17 @@ import java.util.Set; import lombok.core.AST.Kind; +/** + * Capable of resolving a simple type name such as 'String' into 'java.lang.String'. + */ public class TypeResolver { private final TypeLibrary library; private Collection<String> imports; + /** + * Creates a new TypeResolver that can be used to resolve types in a given library, encountered in + * a source file with the provided package and import statements. + */ public TypeResolver(TypeLibrary library, String packageString, Collection<String> importStrings) { this.library = library; this.imports = makeImportList(packageString, importStrings); @@ -23,6 +51,11 @@ public class TypeResolver { return imports; } + /** + * Finds type matches for the stated type reference. The provided context is scanned for local type names + * that shadow type names listed in import statements. If such a shadowing occurs, no matches are returned + * for any shadowed types, as you would expect. + */ public Collection<String> findTypeMatches(AST<?>.Node context, String typeRef) { Collection<String> potentialMatches = library.findCompatible(typeRef); if ( potentialMatches.isEmpty() ) return Collections.emptyList(); diff --git a/src/lombok/core/Version.java b/src/lombok/core/Version.java index cb349aaf..119dcc2e 100644 --- a/src/lombok/core/Version.java +++ b/src/lombok/core/Version.java @@ -1,5 +1,29 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; +/** + * This class just holds lombok's current version. + */ public class Version { private static final String VERSION = "0.4"; @@ -7,10 +31,16 @@ public class Version { //Prevent instantiation } + /** + * Prints the version followed by a newline, and exits. + */ public static void main(String[] args) { System.out.println(VERSION); } + /** + * Get the current Lombok version. + */ public static String getVersion() { return VERSION; } |