/*
* Copyright 2012 Agorava
*
* 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 org.agorava.utils.solder.reflection.annotated;
import org.agorava.utils.solder.support.SolderMessages;
import org.apache.deltaspike.core.api.exclude.annotation.Exclude;
import org.apache.deltaspike.core.util.ReflectionUtils;
import org.apache.deltaspike.core.util.securitymanaged.SetAccessiblePrivilegedAction;
import javax.enterprise.inject.spi.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.security.AccessController;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
//import org.jboss.solder.messages.Messages;
/**
* Builder to aid in creation of a new {@link AnnotatedType} for use in CDI life cycle events.
* Using the builder is typically done by reading the annotations from a {@link Class} or an
* {@link AnnotatedType}. Once the starting class or type has been added all of annotations
* can be modified: constructor, parameter, class, method and fields.
* <p/>
* The AnnotatedTypeBuilder is not thread safe and shall not be used concurrently!
* <p/>
* Based on DS 0.3-incubating:
* - added SolderMessages
* - added redefined support that is only used by this class (these are dependent classes):
* -- AnnotatedTypeImpl
* -- AnnotatedCallableImpl
* -- AnnotatedMemberImpl
* -- AnnotatedConstructorImpl
* -- AnnotatedImpl
* -- AnnotatedParameterImpl
* -- AnnotatedMethodImpl
* -- AnnotatedFieldImpl
*
* @author Ove Ranheim
*/
@Exclude
public class SolderAnnotatedTypeBuilder<X> {
//private transient static SolderMessages messages = Messages.getBundle(SolderMessages.class);
private SolderMessages messages;
private Class<X> javaClass;
private final AnnotationBuilder typeAnnotations;
private final Map<Constructor<?>, AnnotationBuilder> constructors;
private final Map<Constructor<?>, Map<Integer, AnnotationBuilder>> constructorParameters;
private final Map<Constructor<?>, Map<Integer, Type>> constructorParameterTypes;
private final Map<Field, AnnotationBuilder> fields;
private final Map<Field, Type> fieldTypes;
private final Map<Method, AnnotationBuilder> methods;
private final Map<Method, Map<Integer, AnnotationBuilder>> methodParameters;
private final Map<Method, Map<Integer, Type>> methodParameterTypes;
/**
* Create a new builder. A new builder has no annotations and no members.
*
* @see #readFromType(AnnotatedType)
* @see #readFromType(Class)
* @see #readFromType(AnnotatedType, boolean)
* @see #readFromType(Class, boolean)
*/
public SolderAnnotatedTypeBuilder() {
typeAnnotations = new AnnotationBuilder();
constructors = new HashMap<Constructor<?>, AnnotationBuilder>();
constructorParameters = new HashMap<Constructor<?>, Map<Integer, AnnotationBuilder>>();
constructorParameterTypes = new HashMap<Constructor<?>, Map<Integer, Type>>();
fields = new HashMap<Field, AnnotationBuilder>();
fieldTypes = new HashMap<Field, Type>();
methods = new HashMap<Method, AnnotationBuilder>();
methodParameters = new HashMap<Method, Map<Integer, AnnotationBuilder>>();
methodParameterTypes = new HashMap<Method, Map<Integer, Type>>();
// todo: deltaspike port of SolderMessages
messages = new SolderMessages();
}
/**
* Add an annotation to the type declaration.
*
* @param annotation the annotation instance to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToClass(Annotation annotation) {
typeAnnotations.add(annotation);
return this;
}
/**
* Remove an annotation from the type
*
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType
*/
public SolderAnnotatedTypeBuilder<X> removeFromClass(Class<? extends Annotation> annotationType) {
typeAnnotations.remove(annotationType);
return this;
}
/**
* Add an annotation to the specified field. If the field is not already
* present, it will be added.
*
* @param field the field to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToField(Field field, Annotation annotation) {
if (fields.get(field) == null) {
fields.put(field, new AnnotationBuilder());
}
fields.get(field).add(annotation);
return this;
}
/**
* Add an annotation to the specified field. If the field is not already
* present, it will be added.
*
* @param field the field to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToField(AnnotatedField<? super X> field, Annotation annotation) {
return addToField(field.getJavaMember(), annotation);
}
/**
* Remove an annotation from the specified field.
*
* @param field the field to remove the annotation from
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null or if the
* field is not currently declared on the type
*/
public SolderAnnotatedTypeBuilder<X> removeFromField(Field field, Class<? extends Annotation> annotationType) {
if (fields.get(field) == null) {
throw new IllegalArgumentException("field " + field + " not present on class " + getJavaClass());
} else {
fields.get(field).remove(annotationType);
}
return this;
}
/**
* Remove an annotation from the specified field.
*
* @param field the field to remove the annotation from
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null or if the
* field is not currently declared on the type
*/
public SolderAnnotatedTypeBuilder<X> removeFromField(AnnotatedField<? super X> field,
Class<? extends Annotation> annotationType) {
return removeFromField(field.getJavaMember(), annotationType);
}
/**
* Add an annotation to the specified method. If the method is not already
* present, it will be added.
*
* @param method the method to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToMethod(Method method, Annotation annotation) {
if (methods.get(method) == null) {
methods.put(method, new AnnotationBuilder());
}
methods.get(method).add(annotation);
return this;
}
/**
* Add an annotation to the specified method. If the method is not already
* present, it will be added.
*
* @param method the method to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToMethod(AnnotatedMethod<? super X> method, Annotation annotation) {
return addToMethod(method.getJavaMember(), annotation);
}
/**
* Remove an annotation from the specified method.
*
* @param method the method to remove the annotation from
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null or if the
* method is not currently declared on the type
*/
public SolderAnnotatedTypeBuilder<X> removeFromMethod(Method method, Class<? extends Annotation> annotationType) {
if (methods.get(method) == null) {
throw new IllegalArgumentException("Method " + method + " not present on class" + getJavaClass());
} else {
methods.get(method).remove(annotationType);
}
return this;
}
/**
* Remove an annotation from the specified method.
*
* @param method the method to remove the annotation from
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null or if the
* method is not currently declared on the type
*/
public SolderAnnotatedTypeBuilder<X> removeFromMethod(AnnotatedMethod<? super X> method,
Class<? extends Annotation> annotationType) {
return removeFromMethod(method.getJavaMember(), annotationType);
}
/**
* Add an annotation to the specified method parameter. If the method is not
* already present, it will be added. If the method parameter is not already
* present, it will be added.
*
* @param method the method to add the annotation to
* @param position the position of the parameter to add
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToMethodParameter(Method method, int position, Annotation annotation) {
if (!methods.containsKey(method)) {
methods.put(method, new AnnotationBuilder());
}
if (methodParameters.get(method) == null) {
methodParameters.put(method, new HashMap<Integer, AnnotationBuilder>());
}
if (methodParameters.get(method).get(position) == null) {
methodParameters.get(method).put(position, new AnnotationBuilder());
}
methodParameters.get(method).get(position).add(annotation);
return this;
}
/**
* Remove an annotation from the specified method parameter.
*
* @param method the method to remove the annotation from
* @param position the position of the parameter to remove
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null, if the
* method is not currently declared on the type or if the
* parameter is not declared on the method
*/
public SolderAnnotatedTypeBuilder<X> removeFromMethodParameter(Method method,
int position, Class<? extends Annotation> annotationType) {
if (methods.get(method) == null) {
throw new IllegalArgumentException("Method " + method + " not present on class " + getJavaClass());
} else {
if (methodParameters.get(method).get(position) == null) {
throw new IllegalArgumentException(
String.format("parameter %s not present on method %s declared on class %s",
method, position, getJavaClass()));
} else {
methodParameters.get(method).get(position).remove(annotationType);
}
}
return this;
}
/**
* Add an annotation to the specified constructor. If the constructor is not
* already present, it will be added.
*
* @param constructor the constructor to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToConstructor(Constructor<X> constructor, Annotation annotation) {
if (constructors.get(constructor) == null) {
constructors.put(constructor, new AnnotationBuilder());
}
constructors.get(constructor).add(annotation);
return this;
}
/**
* Add an annotation to the specified constructor. If the constructor is not
* already present, it will be added.
*
* @param constructor the constructor to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToConstructor(AnnotatedConstructor<X> constructor, Annotation annotation) {
return addToConstructor(constructor.getJavaMember(), annotation);
}
/**
* Remove an annotation from the specified constructor.
*
* @param constructor the constructor to add the annotation to
* @param annotationType the annotation to add
* @throws IllegalArgumentException if the annotationType is null or if the
* constructor is not currently declared on the type
*/
public SolderAnnotatedTypeBuilder<X> removeFromConstructor(Constructor<X> constructor,
Class<? extends Annotation> annotationType) {
if (constructors.get(constructor) != null) {
constructors.get(constructor).remove(annotationType);
}
return this;
}
/**
* Remove an annotation from the specified constructor.
*
* @param constructor the constructor to add the annotation to
* @param annotationType the annotation to add
* @throws IllegalArgumentException if the annotationType is null, if the
* annotation does not exist on the type or if the constructor is
* not currently declared on the type
*/
public SolderAnnotatedTypeBuilder<X> removeFromConstructor(AnnotatedConstructor<X> constructor,
Class<? extends Annotation> annotationType) {
return removeFromConstructor(constructor.getJavaMember(), annotationType);
}
/**
* Add an annotation to the specified constructor parameter. If the
* constructor is not already present, it will be added. If the constructor
* parameter is not already present, it will be added.
*
* @param constructor the constructor to add the annotation to
* @param position the position of the parameter to add
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null
*/
public SolderAnnotatedTypeBuilder<X> addToConstructorParameter(Constructor<X> constructor,
int position,
Annotation annotation) {
if (!constructors.containsKey(constructor)) {
constructors.put(constructor, new AnnotationBuilder());
}
if (constructorParameters.get(constructor) == null) {
constructorParameters.put(constructor, new HashMap<Integer, AnnotationBuilder>());
}
if (constructorParameters.get(constructor).get(position) == null) {
constructorParameters.get(constructor).put(position, new AnnotationBuilder());
}
constructorParameters.get(constructor).get(position).add(annotation);
return this;
}
/**
* Remove an annotation from the specified constructor parameter.
*
* @param constructor the constructor to remove the annotation from
* @param position the position of the parameter to remove
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null, if the
* constructor is not currently declared on the type or if the
* parameter is not declared on the constructor
*/
public SolderAnnotatedTypeBuilder<X> removeFromConstructorParameter(Constructor<X> constructor,
int position,
Class<? extends Annotation> annotationType) {
if (constructorParameters.get(constructor) != null &&
constructorParameters.get(constructor).get(position) != null) {
constructorParameters.get(constructor).get(position).remove(annotationType);
}
return this;
}
/**
* Remove an annotation from the specified parameter.
*
* @param parameter the parameter to remove the annotation from
* @param annotationType the annotation type to remove
* @throws IllegalArgumentException if the annotationType is null, if the
* callable which declares the parameter is not currently declared
* on the type or if the parameter is not declared on either a
* constructor or a method
*/
public SolderAnnotatedTypeBuilder<X> removeFromParameter(AnnotatedParameter<? super X> parameter,
Class<? extends Annotation> annotationType) {
if (parameter.getDeclaringCallable().getJavaMember() instanceof Method) {
Method method = (Method) parameter.getDeclaringCallable().getJavaMember();
return removeFromMethodParameter(method, parameter.getPosition(), annotationType);
}
if (parameter.getDeclaringCallable().getJavaMember() instanceof Constructor<?>) {
@SuppressWarnings("unchecked")
Constructor<X> constructor = (Constructor<X>) parameter.getDeclaringCallable().getJavaMember();
return removeFromConstructorParameter(constructor, parameter.getPosition(), annotationType);
} else {
throw new IllegalArgumentException("Cannot remove from parameter " + parameter +
" - cannot operate on member " + parameter.getDeclaringCallable().getJavaMember());
}
}
/**
* Add an annotation to the specified parameter. If the callable which
* declares the parameter is not already present, it will be added. If the
* parameter is not already present on the callable, it will be added.
*
* @param parameter the parameter to add the annotation to
* @param annotation the annotation to add
* @throws IllegalArgumentException if the annotation is null or if the
* parameter is not declared on either a constructor or a method
*/
public SolderAnnotatedTypeBuilder<X> addToParameter(AnnotatedParameter<? super X> parameter, Annotation annotation) {
if (parameter.getDeclaringCallable().getJavaMember() instanceof Method) {
Method method = (Method) parameter.getDeclaringCallable().getJavaMember();
return addToMethodParameter(method, parameter.getPosition(), annotation);
}
if (parameter.getDeclaringCallable().getJavaMember() instanceof Constructor<?>) {
@SuppressWarnings("unchecked")
Constructor<X> constructor = (Constructor<X>) parameter.getDeclaringCallable().getJavaMember();
return addToConstructorParameter(constructor, parameter.getPosition(), annotation);
} else {
throw new IllegalArgumentException("Cannot remove from parameter " + parameter +
" - cannot operate on member " + parameter.getDeclaringCallable().getJavaMember());
}
}
/**
* Remove annotations from the type, and all of it's members. If an
* annotation of the specified type appears on the type declaration, or any
* of it's members it will be removed.
*
* @param annotationType the type of annotation to remove
* @throws IllegalArgumentException if the annotationType is null
*/
public SolderAnnotatedTypeBuilder<X> removeFromAll(Class<? extends Annotation> annotationType) {
if (annotationType == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "annotationType"));
}
removeFromClass(annotationType);
for (Map.Entry<Field, AnnotationBuilder> field : fields.entrySet()) {
field.getValue().remove(annotationType);
}
for (Map.Entry<Method, AnnotationBuilder> method : methods.entrySet()) {
method.getValue().remove(annotationType);
if (methodParameters.get(method.getKey()) != null) {
for (Map.Entry<Integer, AnnotationBuilder> parameter : methodParameters.get(method.getKey()).entrySet()) {
parameter.getValue().remove(annotationType);
}
}
}
for (Map.Entry<Constructor<?>, AnnotationBuilder> constructor : constructors.entrySet()) {
constructor.getValue().remove(annotationType);
if (constructorParameters.get(constructor.getKey()) != null) {
for (Map.Entry<Integer, AnnotationBuilder> parameter :
constructorParameters.get(constructor.getKey()).entrySet()) {
parameter.getValue().remove(annotationType);
}
}
}
return this;
}
/**
* Reads in from an existing AnnotatedType. Any elements not present are
* added. The javaClass will be read in. If the annotation already exists on
* that element in the builder the read annotation will be used.
*
* @param type the type to read from
* @throws IllegalArgumentException if type is null
*/
public SolderAnnotatedTypeBuilder<X> readFromType(AnnotatedType<X> type) {
return readFromType(type, true);
}
/**
* Reads in from an existing AnnotatedType. Any elements not present are
* added. The javaClass will be read in if overwrite is true. If the
* annotation already exists on that element in the builder, overwrite
* determines whether the original or read annotation will be used.
*
* @param type the type to read from
* @param overwrite if true, the read annotation will replace any existing
* annotation
* @throws IllegalArgumentException if type is null
*/
public SolderAnnotatedTypeBuilder<X> readFromType(AnnotatedType<X> type, boolean overwrite) {
if (type == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "type"));
}
if (javaClass == null || overwrite) {
javaClass = type.getJavaClass();
}
mergeAnnotationsOnElement(type, overwrite, typeAnnotations);
for (AnnotatedField<? super X> field : type.getFields()) {
if (fields.get(field.getJavaMember()) == null) {
fields.put(field.getJavaMember(), new AnnotationBuilder());
}
mergeAnnotationsOnElement(field, overwrite, fields.get(field.getJavaMember()));
}
for (AnnotatedMethod<? super X> method : type.getMethods()) {
if (methods.get(method.getJavaMember()) == null) {
methods.put(method.getJavaMember(), new AnnotationBuilder());
}
mergeAnnotationsOnElement(method, overwrite, methods.get(method.getJavaMember()));
for (AnnotatedParameter<? super X> p : method.getParameters()) {
if (methodParameters.get(method.getJavaMember()) == null) {
methodParameters.put(method.getJavaMember(), new HashMap<Integer, AnnotationBuilder>());
}
if (methodParameters.get(method.getJavaMember()).get(p.getPosition()) == null) {
methodParameters.get(method.getJavaMember()).put(p.getPosition(), new AnnotationBuilder());
}
mergeAnnotationsOnElement(
p, overwrite, methodParameters.get(method.getJavaMember()).get(p.getPosition()));
}
}
for (AnnotatedConstructor<? super X> constructor : type.getConstructors()) {
if (constructors.get(constructor.getJavaMember()) == null) {
constructors.put(constructor.getJavaMember(), new AnnotationBuilder());
}
mergeAnnotationsOnElement(constructor, overwrite, constructors.get(constructor.getJavaMember()));
for (AnnotatedParameter<? super X> p : constructor.getParameters()) {
if (constructorParameters.get(
constructor.getJavaMember()) == null) {
constructorParameters.put(
constructor.getJavaMember(), new HashMap<Integer, AnnotationBuilder>());
}
if (constructorParameters.get(
constructor.getJavaMember()).get(p.getPosition()) == null) {
constructorParameters.get(
constructor.getJavaMember()).put(p.getPosition(), new AnnotationBuilder());
}
mergeAnnotationsOnElement(
p, overwrite, constructorParameters.get(constructor.getJavaMember()).get(p.getPosition()));
}
}
return this;
}
/**
* Reads the annotations from an existing java type. Annotations already
* present will be overwritten
*
* @param type the type to read from
* @throws IllegalArgumentException if type is null
*/
public SolderAnnotatedTypeBuilder<X> readFromType(Class<X> type) {
return readFromType(type, true);
}
/**
* Reads the annotations from an existing java type. If overwrite is true
* then existing annotations will be overwritten
*
* @param type the type to read from
* @param overwrite if true, the read annotation will replace any existing
* annotation
*/
public SolderAnnotatedTypeBuilder<X> readFromType(Class<X> type, boolean overwrite) {
if (type == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "type"));
}
if (javaClass == null || overwrite) {
javaClass = type;
}
for (Annotation annotation : type.getAnnotations()) {
if (overwrite || !typeAnnotations.isAnnotationPresent(annotation.annotationType())) {
typeAnnotations.add(annotation);
}
}
for (Field field : ReflectionUtils.getAllDeclaredFields(type)) {
AnnotationBuilder annotationBuilder = fields.get(field);
if (annotationBuilder == null) {
annotationBuilder = new AnnotationBuilder();
fields.put(field, annotationBuilder);
}
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new SetAccessiblePrivilegedAction(field));
} else {
field.setAccessible(true);
}
for (Annotation annotation : field.getAnnotations()) {
if (overwrite || !annotationBuilder.isAnnotationPresent(annotation.annotationType())) {
annotationBuilder.add(annotation);
}
}
}
for (Method method : ReflectionUtils.getAllDeclaredMethods(type)) {
AnnotationBuilder annotationBuilder = methods.get(method);
if (annotationBuilder == null) {
annotationBuilder = new AnnotationBuilder();
methods.put(method, annotationBuilder);
}
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new SetAccessiblePrivilegedAction(method));
} else {
method.setAccessible(true);
}
for (Annotation annotation : method.getAnnotations()) {
if (overwrite || !annotationBuilder.isAnnotationPresent(annotation.annotationType())) {
annotationBuilder.add(annotation);
}
}
Map<Integer, AnnotationBuilder> parameters = methodParameters.get(method);
if (parameters == null) {
parameters = new HashMap<Integer, AnnotationBuilder>();
methodParameters.put(method, parameters);
}
for (int i = 0; i < method.getParameterTypes().length; ++i) {
AnnotationBuilder parameterAnnotationBuilder = parameters.get(i);
if (parameterAnnotationBuilder == null) {
parameterAnnotationBuilder = new AnnotationBuilder();
parameters.put(i, parameterAnnotationBuilder);
}
for (Annotation annotation : method.getParameterAnnotations()[i]) {
if (overwrite || !parameterAnnotationBuilder.isAnnotationPresent(annotation.annotationType())) {
parameterAnnotationBuilder.add(annotation);
}
}
}
}
for (Constructor<?> constructor : type.getDeclaredConstructors()) {
AnnotationBuilder annotationBuilder = constructors.get(constructor);
if (annotationBuilder == null) {
annotationBuilder = new AnnotationBuilder();
constructors.put(constructor, annotationBuilder);
}
constructor.setAccessible(true);
for (Annotation annotation : constructor.getAnnotations()) {
if (overwrite || !annotationBuilder.isAnnotationPresent(annotation.annotationType())) {
annotationBuilder.add(annotation);
}
}
Map<Integer, AnnotationBuilder> mparams = constructorParameters.get(constructor);
if (mparams == null) {
mparams = new HashMap<Integer, AnnotationBuilder>();
constructorParameters.put(constructor, mparams);
}
for (int i = 0; i < constructor.getParameterTypes().length; ++i) {
AnnotationBuilder parameterAnnotationBuilder = mparams.get(i);
if (parameterAnnotationBuilder == null) {
parameterAnnotationBuilder = new AnnotationBuilder();
mparams.put(i, parameterAnnotationBuilder);
}
for (Annotation annotation : constructor.getParameterAnnotations()[i]) {
if (overwrite || !parameterAnnotationBuilder.isAnnotationPresent(annotation.annotationType())) {
annotationBuilder.add(annotation);
}
}
}
}
return this;
}
protected void mergeAnnotationsOnElement(Annotated annotated,
boolean overwriteExisting,
AnnotationBuilder typeAnnotations) {
for (Annotation annotation : annotated.getAnnotations()) {
if (typeAnnotations.getAnnotation(annotation.annotationType()) != null) {
if (overwriteExisting) {
typeAnnotations.remove(annotation.annotationType());
typeAnnotations.add(annotation);
}
} else {
typeAnnotations.add(annotation);
}
}
}
/**
* Create an {@link AnnotatedType}. Any public members present on the
* underlying class and not overridden by the builder will be automatically
* added.
*/
public AnnotatedType<X> create() {
Map<Constructor<?>, Map<Integer, AnnotationStore>> constructorParameterAnnotations =
new HashMap<Constructor<?>, Map<Integer, AnnotationStore>>();
Map<Constructor<?>, AnnotationStore> constructorAnnotations =
new HashMap<Constructor<?>, AnnotationStore>();
Map<Method, Map<Integer, AnnotationStore>> methodParameterAnnotations =
new HashMap<Method, Map<Integer, AnnotationStore>>();
Map<Method, AnnotationStore> methodAnnotations =
new HashMap<Method, AnnotationStore>();
Map<Field, AnnotationStore> fieldAnnotations =
new HashMap<Field, AnnotationStore>();
for (Map.Entry<Field, AnnotationBuilder> field : fields.entrySet()) {
fieldAnnotations.put(field.getKey(), field.getValue().create());
}
for (Map.Entry<Method, AnnotationBuilder> method : methods.entrySet()) {
methodAnnotations.put(method.getKey(), method.getValue().create());
}
for (Map.Entry<Method, Map<Integer, AnnotationBuilder>> parameters : methodParameters.entrySet()) {
Map<Integer, AnnotationStore> parameterAnnotations = new HashMap<Integer, AnnotationStore>();
methodParameterAnnotations.put(parameters.getKey(), parameterAnnotations);
for (Map.Entry<Integer, AnnotationBuilder> parameter : parameters.getValue().entrySet()) {
parameterAnnotations.put(parameter.getKey(), parameter.getValue().create());
}
}
for (Map.Entry<Constructor<?>, AnnotationBuilder> constructor : constructors.entrySet()) {
constructorAnnotations.put(constructor.getKey(), constructor.getValue().create());
}
for (Map.Entry<Constructor<?>, Map<Integer, AnnotationBuilder>> parameters : constructorParameters.entrySet()) {
Map<Integer, AnnotationStore> parameterAnnotations = new HashMap<Integer, AnnotationStore>();
constructorParameterAnnotations.put(parameters.getKey(), parameterAnnotations);
for (Map.Entry<Integer, AnnotationBuilder> parameter : parameters.getValue().entrySet()) {
parameterAnnotations.put(parameter.getKey(), parameter.getValue().create());
}
}
return new AnnotatedTypeImpl<X>(javaClass, typeAnnotations.create(), fieldAnnotations, methodAnnotations,
methodParameterAnnotations, constructorAnnotations, constructorParameterAnnotations, fieldTypes,
methodParameterTypes, constructorParameterTypes);
}
/**
* Override the declared type of a field
*
* @param field the field to override the type on
* @param type the new type of the field
* @throws IllegalArgumentException if field or type is null
*/
public void overrideFieldType(Field field, Type type) {
if (field == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "field"));
}
if (type == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "type"));
}
fieldTypes.put(field, type);
}
/**
* Override the declared type of a field
*
* @param field the field to override the type on
* @param type the new type of the field
* @throws IllegalArgumentException if field or type is null
*/
public void overrideFieldType(AnnotatedField<? super X> field, Type type) {
overrideFieldType(field.getJavaMember(), type);
}
/**
* Override the declared type of a method parameter
*
* @param method the method to override the parameter type on
* @param position the position of the parameter to override the type on
* @param type the new type of the parameter
* @throws IllegalArgumentException if parameter or type is null
*/
public SolderAnnotatedTypeBuilder<X> overrideMethodParameterType(Method method, int position, Type type) {
if (method == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "method"));
}
if (type == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "type"));
}
if (methodParameterTypes.get(method) == null) {
methodParameterTypes.put(method, new HashMap<Integer, Type>());
}
methodParameterTypes.get(method).put(position, type);
return this;
}
/**
* Override the declared type of a constructor parameter
*
* @param constructor the constructor to override the parameter type on
* @param position the position of the parameter to override the type on
* @param type the new type of the parameter
* @throws IllegalArgumentException if parameter or type is null
*/
public SolderAnnotatedTypeBuilder<X> overrideConstructorParameterType(Constructor<X> constructor, int position, Type type) {
if (constructor == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "constructor"));
}
if (type == null) {
throw new IllegalArgumentException(String.format("%s parameter must not be null", "type"));
}
if (constructorParameterTypes.get(constructor) == null) {
constructorParameterTypes.put(constructor, new HashMap<Integer, Type>());
}
constructorParameterTypes.get(constructor).put(position, type);
return this;
}
/**
* Override the declared type of a parameter.
*
* @param parameter the parameter to override the type on
* @param type the new type of the parameter
* @throws IllegalArgumentException if parameter or type is null
*/
public SolderAnnotatedTypeBuilder<X> overrideParameterType(AnnotatedParameter<? super X> parameter, Type type) {
if (parameter.getDeclaringCallable().getJavaMember() instanceof Method) {
Method method = (Method) parameter.getDeclaringCallable().getJavaMember();
return overrideMethodParameterType(method, parameter.getPosition(), type);
}
if (parameter.getDeclaringCallable().getJavaMember() instanceof Constructor<?>) {
@SuppressWarnings("unchecked")
Constructor<X> constructor = (Constructor<X>) parameter.getDeclaringCallable().getJavaMember();
return overrideConstructorParameterType(constructor, parameter.getPosition(), type);
} else {
throw new IllegalArgumentException("Cannot remove from parameter " + parameter +
" - cannot operate on member " + parameter.getDeclaringCallable().getJavaMember());
}
}
/**
* getter for the class
*/
public Class<X> getJavaClass() {
return javaClass;
}
/**
* setter for the class
*/
public SolderAnnotatedTypeBuilder<X> setJavaClass(Class<X> javaClass) {
this.javaClass = javaClass;
return this;
}
/**
* Redefine any annotations of the specified type. The redefinition callback
* will be invoked for any annotation on the type definition or any of it's
* members.
*
* @param annotationType the type of the annotation for which to call the
* redefinition
* @param redefinition the redefiniton callback
* @throws IllegalArgumentException if the annotationType or redefinition is
* null
*/
public <A extends Annotation> SolderAnnotatedTypeBuilder<X> redefine(Class<A> annotationType, AnnotationRedefiner<A> redefinition) {
if (annotationType == null) {
throw new IllegalArgumentException(messages.parameterMustNotBeNull("annotationType"));
}
if (redefinition == null) {
throw new IllegalArgumentException(messages.parameterMustNotBeNull("redefinition"));
}
redefineAnnotationBuilder(annotationType, redefinition, javaClass, javaClass, typeAnnotations, null);
for (Entry<Field, AnnotationBuilder> field : fields.entrySet()) {
redefineAnnotationBuilder(annotationType, redefinition, field.getKey(), field.getKey().getGenericType(), field.getValue(), field.getKey().getName());
}
for (Entry<Method, AnnotationBuilder> method : methods.entrySet()) {
redefineAnnotationBuilder(annotationType, redefinition, method.getKey(), method.getKey().getGenericReturnType(), method.getValue(), method.getKey().getName());
}
for (Entry<Constructor<?>, AnnotationBuilder> constructor : constructors.entrySet()) {
redefineAnnotationBuilder(annotationType, redefinition, constructor.getKey(), constructor.getKey().getDeclaringClass(), constructor.getValue(), null);
}
for (Entry<Method, AnnotationBuilder> method : methods.entrySet()) {
if (methodParameters.get(method.getKey()) != null) {
for (Entry<Integer, AnnotationBuilder> parameter : methodParameters.get(method.getKey()).entrySet()) {
Parameter<?> p = Parameter.create(method.getKey(), parameter.getKey());
redefineAnnotationBuilder(annotationType, redefinition, p, p.getBaseType(), parameter.getValue(), null);
}
}
}
for (Entry<Constructor<?>, AnnotationBuilder> constructor : constructors.entrySet()) {
if (constructorParameters.get(constructor.getKey()) != null) {
for (Entry<Integer, AnnotationBuilder> parameter : constructorParameters.get(constructor.getKey()).entrySet()) {
Parameter<?> p = Parameter.create(constructor.getKey(), parameter.getKey());
redefineAnnotationBuilder(annotationType, redefinition, p, p.getBaseType(), parameter.getValue(), null);
}
}
}
return this;
}
protected <A extends Annotation> void redefineAnnotationBuilder(Class<A> annotationType, AnnotationRedefiner<A> redefinition, AnnotatedElement annotated, Type baseType, AnnotationBuilder builder, String elementName) {
if (builder.isAnnotationPresent(annotationType)) {
redefinition.redefine(new RedefinitionContext<A>(annotated, baseType, builder, elementName));
}
}
}