aboutsummaryrefslogtreecommitdiff
path: root/src/lombok/core/AnnotationValues.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/lombok/core/AnnotationValues.java')
-rw-r--r--src/lombok/core/AnnotationValues.java419
1 files changed, 0 insertions, 419 deletions
diff --git a/src/lombok/core/AnnotationValues.java b/src/lombok/core/AnnotationValues.java
deleted file mode 100644
index 0408de85..00000000
--- a/src/lombok/core/AnnotationValues.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * 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;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-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.
- *
- * @param A The annotation that this class represents, such as {@code lombok.Getter}
- */
-public class AnnotationValues<A extends Annotation> {
- private final Class<A> type;
- private final Map<String, AnnotationValue> values;
- private final LombokNode<?, ?, ?> 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 LombokNode<?, ?, ?> node;
- private final boolean isExplicit;
-
- /**
- * 'raw' should be the exact expression, for example '5+7', 'AccessLevel.PUBLIC', or 'int.class'.
- * 'valueGuess' should be a likely guess at the real value intended.
- *
- * For classes, supply the class name (qualified or not) as a string.<br />
- * For enums, supply the simple name part (everything after the last dot) as a string.<br />
- */
- public AnnotationValue(LombokNode<?, ?, ?> node, String raw, Object valueGuess, boolean isExplicit) {
- this.node = node;
- this.raws = Collections.singletonList(raw);
- this.valueGuesses = Collections.singletonList(valueGuess);
- this.isExplicit = isExplicit;
- }
-
- /**
- * Like the other constructor, but used for when the annotation method is initialized with an array value.
- */
- public AnnotationValue(LombokNode<?, ?, ?> node, List<String> raws, List<Object> valueGuesses, boolean isExplicit) {
- this.node = node;
- this.raws = raws;
- this.valueGuesses = valueGuesses;
- this.isExplicit = isExplicit;
- }
-
- /**
- * Override this if you want more specific behaviour (to get the source position just right).
- *
- * @param message English message with the problem.
- * @param valueIdx The index into the values for this annotation key that caused the problem.
- * -1 for a problem that applies to all values, otherwise the 0-based index into an array of values.
- * If there is no array for this value (e.g. value=1 instead of value={1,2}), then always -1 or 0.
- */
- public void setError(String message, int valueIdx) {
- node.addError(message);
- }
-
- /**
- * Override this if you want more specific behaviour (to get the source position just right).
- *
- * @param message English message with the problem.
- * @param valueIdx The index into the values for this annotation key that caused the problem.
- * -1 for a problem that applies to all values, otherwise the 0-based index into an array of values.
- * If there is no array for this value (e.g. value=1 instead of value={1,2}), then always -1 or 0.
- */
- public void setWarning(String message, int valueIdx) {
- node.addError(message);
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return "raws: " + raws + " valueGuesses: " + valueGuesses;
- }
-
- public boolean isExplicit() {
- return isExplicit;
- }
- }
-
- /**
- * 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, LombokNode<?, ?, ?> 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) {
- super(msg);
- this.idx = idx;
- this.owner = owner;
- }
- }
-
- private static AnnotationValueDecodeFail makeNoDefaultFail(AnnotationValue owner, Method method) {
- return new AnnotationValueDecodeFail(owner,
- "No value supplied but " + method.getName() + " has no default either.", -1);
- }
-
- 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() {
- if (cachedInstance != null) return cachedInstance;
- InvocationHandler invocations = new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- AnnotationValue v = values.get(method.getName());
- if (v == null) {
- Object defaultValue = method.getDefaultValue();
- if (defaultValue != null) return defaultValue;
- throw makeNoDefaultFail(v, method);
- }
-
- boolean isArray = false;
- Class<?> expected = method.getReturnType();
- Object array = null;
- if (expected.isArray()) {
- isArray = true;
- expected = expected.getComponentType();
- array = Array.newInstance(expected, v.valueGuesses.size());
- }
-
- if (!isArray && v.valueGuesses.size() > 1) {
- throw new AnnotationValueDecodeFail(v,
- "Expected a single value, but " + method.getName() + " has an array of values", -1);
- }
-
- if (v.valueGuesses.size() == 0 && !isArray) {
- Object defaultValue = method.getDefaultValue();
- if (defaultValue == null) throw makeNoDefaultFail(v, method);
- return defaultValue;
- }
-
- int idx = 0;
- for (Object guess : v.valueGuesses) {
- Object result = guess == null ? null : guessToType(guess, expected, v, idx);
- if (!isArray) {
- if (result == null) {
- Object defaultValue = method.getDefaultValue();
- if (defaultValue == null) throw makeNoDefaultFail(v, method);
- return defaultValue;
- }
- return result;
- }
- if (result == null) {
- if (v.valueGuesses.size() == 1) {
- Object defaultValue = method.getDefaultValue();
- if (defaultValue == null) throw makeNoDefaultFail(v, method);
- return defaultValue;
- }
- throw new AnnotationValueDecodeFail(v,
- "I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
- }
- Array.set(array, idx++, result);
- }
-
- return array;
- }
- };
-
- return cachedInstance = (A) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, invocations);
- }
-
- private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) {
- if (expected == int.class) {
- if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- return ((Number)guess).intValue();
- }
- }
-
- if (expected == long.class) {
- if (guess instanceof Long || guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- return ((Number)guess).longValue();
- }
- }
-
- if (expected == short.class) {
- if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- int intVal = ((Number)guess).intValue();
- int shortVal = ((Number)guess).shortValue();
- if (shortVal == intVal) return shortVal;
- }
- }
-
- if (expected == byte.class) {
- if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- int intVal = ((Number)guess).intValue();
- int byteVal = ((Number)guess).byteValue();
- if (byteVal == intVal) return byteVal;
- }
- }
-
- if (expected == double.class) {
- if (guess instanceof Number) return ((Number)guess).doubleValue();
- }
-
- if (expected == float.class) {
- if (guess instanceof Number) return ((Number)guess).floatValue();
- }
-
- if (expected == boolean.class) {
- if (guess instanceof Boolean) return ((Boolean)guess).booleanValue();
- }
-
- if (expected == char.class) {
- if (guess instanceof Character) return ((Character)guess).charValue();
- }
-
- if (expected == String.class) {
- if (guess instanceof String) return guess;
- }
-
- if (Enum.class.isAssignableFrom(expected) ) {
- if (guess instanceof String) {
- for (Object enumConstant : expected.getEnumConstants()) {
- String target = ((Enum<?>)enumConstant).name();
- if (target.equals(guess)) return enumConstant;
- }
- throw new AnnotationValueDecodeFail(v,
- "Can't translate " + guess + " to an enum of type " + expected, pos);
- }
- }
-
- if (Class.class == expected) {
- if (guess instanceof String) try {
- return Class.forName(toFQ((String)guess));
- } catch (ClassNotFoundException e) {
- throw new AnnotationValueDecodeFail(v,
- "Can't translate " + guess + " to a class object.", pos);
- }
- }
-
- throw new AnnotationValueDecodeFail(v,
- "Can't translate a " + guess.getClass() + " to the expected " + expected, pos);
- }
-
- /**
- * Returns the raw expressions used for the provided {@code annotationMethodName}.
- *
- * You should use this method for annotation methods that return {@code 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;
- }
-
- public boolean isExplicit(String annotationMethodName) {
- AnnotationValue annotationValue = values.get(annotationMethodName);
- return annotationValue != null && annotationValue.isExplicit();
- }
-
- /**
- * 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);
- }
-
- /** Generates an error message on the stated annotation value (you should only call this method if you know it's there!) */
- public void setError(String annotationMethodName, String message) {
- setError(annotationMethodName, message, -1);
- }
-
- /** Generates a warning message on the stated annotation value (you should only call this method if you know it's there!) */
- public void setWarning(String annotationMethodName, String message) {
- setWarning(annotationMethodName, message, -1);
- }
-
- /** Generates an error message on the stated annotation value, which must have an array initializer.
- * The index-th item in the initializer will carry the error (you should only call this method if you know it's there!) */
- public void setError(String annotationMethodName, String message, int index) {
- AnnotationValue v = values.get(annotationMethodName);
- if (v == null) return;
- v.setError(message, index);
- }
-
- /** Generates a warning message on the stated annotation value, which must have an array initializer.
- * The index-th item in the initializer will carry the error (you should only call this method if you know it's there!) */
- public void setWarning(String annotationMethodName, String message, int index) {
- AnnotationValue v = values.get(annotationMethodName);
- if (v == null) return;
- v.setWarning(message, index);
- }
-
- /**
- * 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);
- if (v == null) return Collections.emptyList();
-
- for (Object o : v.valueGuesses) result.add(o == null ? null : toFQ(o.toString()));
- 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;
- String prefix = fqn ? typeName.substring(0, typeName.indexOf('.')) : typeName;
-
- for (String im : ast.getImportStatements()) {
- int idx = im.lastIndexOf('.');
- String simple = im;
- if (idx > -1) simple = im.substring(idx+1);
- if (simple.equals(prefix)) {
- return im + typeName.substring(prefix.length());
- }
- }
-
- c = tryClass(typeName);
- if (c != null) return c.getName();
-
- c = tryClass("java.lang." + typeName);
- if (c != null) return c.getName();
-
- //Try star imports
- for (String im : ast.getImportStatements()) {
- if (im.endsWith(".*")) {
- c = tryClass(im.substring(0, im.length() -1) + typeName);
- if (c != null) return c.getName();
- }
- }
-
- if (!fqn) {
- String pkg = ast.getPackageDeclaration();
- if (pkg != null) return pkg + "." + typeName;
- }
-
- return null;
- }
-
- private Class<?> tryClass(String name) {
- try {
- return Class.forName(name);
- } catch (ClassNotFoundException e) {
- return null;
- }
- }
-}