package com.beust.jcommander;
import com.beust.jcommander.internal.Lists;
import com.beust.jcommander.internal.Sets;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class Parameterized {
private Field field;
private Method method;
private Method getter;
private WrappedParameter wrappedParameter;
private ParametersDelegate parametersDelegate;
public Parameterized(WrappedParameter wp, ParametersDelegate pd,
Field field, Method method) {
wrappedParameter = wp;
this.method = method;
this.field = field;
if (this.field != null) {
if(pd == null) {
setFieldAccessible(this.field);
} else {
setFieldAccessibleWithoutFinalCheck(this.field);
}
}
parametersDelegate = pd;
}
private static void describeClassTree(Class<?> inputClass, Set<Class<?>> setOfClasses) {
if(inputClass == null) {
return;
}
if(Object.class.equals(inputClass) || setOfClasses.contains(inputClass)) {
return;
}
setOfClasses.add(inputClass);
describeClassTree(inputClass.getSuperclass(), setOfClasses);
for(Class<?> hasInterface : inputClass.getInterfaces()) {
describeClassTree(hasInterface, setOfClasses);
}
}
private static Set<Class<?>> describeClassTree(Class<?> inputClass) {
if(inputClass == null) {
return Collections.emptySet();
}
Set<Class<?>> classes = Sets.newLinkedHashSet();
describeClassTree(inputClass, classes);
return classes;
}
public static List<Parameterized> parseArg(Object arg) {
List<Parameterized> result = Lists.newArrayList();
Class<?> rootClass = arg.getClass();
Set<Class<?>> types = describeClassTree(rootClass);
for(Class<?> cls : types) {
for (Field f : cls.getDeclaredFields()) {
Annotation annotation = f.getAnnotation(Parameter.class);
Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
if (annotation != null) {
result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
f, null));
} else if (dynamicParameter != null) {
result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
f, null));
} else if (delegateAnnotation != null) {
result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
f, null));
}
}
for (Method m : cls.getDeclaredMethods()) {
m.setAccessible(true);
Annotation annotation = m.getAnnotation(Parameter.class);
Annotation delegateAnnotation = m.getAnnotation(ParametersDelegate.class);
Annotation dynamicParameter = m.getAnnotation(DynamicParameter.class);
if (annotation != null) {
result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
null, m));
} else if (dynamicParameter != null) {
result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
null, m));
} else if (delegateAnnotation != null) {
result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
null, m));
}
}
}
return result;
}
public WrappedParameter getWrappedParameter() {
return wrappedParameter;
}
public Class<?> getType() {
if (method != null) {
return method.getParameterTypes()[0];
} else {
return field.getType();
}
}
public String getName() {
if (method != null) {
return method.getName();
} else {
return field.getName();
}
}
public Object get(Object object) {
try {
if (method != null) {
if (getter == null) {
setGetter(object);
}
return getter.invoke(object);
} else {
return field.get(object);
}
} catch (SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new ParameterException(e);
} catch (NoSuchMethodException e) {
String name = method.getName();
String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
Object result = null;
try {
Field field = method.getDeclaringClass().getDeclaredField(fieldName);
if (field != null) {
setFieldAccessible(field);
result = field.get(object);
}
} catch(NoSuchFieldException | IllegalAccessException ex) {
}
return result;
}
}
private void setGetter(Object object) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
if(Boolean.class.getSimpleName().toLowerCase().equals(getType().getName())){
try {
getter = object.getClass()
.getMethod("is" + method.getName().substring(3));
return;
} catch (NoSuchMethodException n){
}
}
getter = object.getClass()
.getMethod("g" + method.getName().substring(1));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((field == null) ? 0 : field.hashCode());
result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Parameterized other = (Parameterized) obj;
if (field == null) {
if (other.field != null)
return false;
} else if (!field.equals(other.field))
return false;
if (method == null) {
if (other.method != null)
return false;
} else if (!method.equals(other.method))
return false;
return true;
}
public boolean isDynamicParameter(Field field) {
if (method != null) {
return method.getAnnotation(DynamicParameter.class) != null;
} else {
return this.field.getAnnotation(DynamicParameter.class) != null;
}
}
private static void setFieldAccessible(Field f) {
if (Modifier.isFinal(f.getModifiers())) {
throw new ParameterException(
"Cannot use final field " + f.getDeclaringClass().getName() + "#" + f.getName() + " as a parameter;"
+ " compile-time constant inlining may hide new values written to it.");
}
f.setAccessible(true);
}
private static void setFieldAccessibleWithoutFinalCheck(Field f) {
f.setAccessible(true);
}
private static String errorMessage(Method m, Exception ex) {
return "Could not invoke " + m + "\n Reason: " + ex.getMessage();
}
public void set(Object object, Object value) {
try {
if (method != null) {
method.invoke(object, value);
} else {
field.set(object, value);
}
} catch (IllegalAccessException | IllegalArgumentException ex) {
throw new ParameterException(errorMessage(method, ex));
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof ParameterException) {
throw (ParameterException) ex.getTargetException();
} else {
throw new ParameterException(errorMessage(method, ex));
}
}
}
public ParametersDelegate getDelegateAnnotation() {
return parametersDelegate;
}
public Type getGenericType() {
if (method != null) {
return method.getGenericParameterTypes()[0];
} else {
return field.getGenericType();
}
}
public Parameter getParameter() {
return wrappedParameter.getParameter();
}
public Type findFieldGenericType() {
if (method != null) {
return null;
} else {
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) field.getGenericType();
Type cls = p.getActualTypeArguments()[0];
if (cls instanceof Class) {
return cls;
} else if ( cls instanceof WildcardType) {
WildcardType wildcardType = (WildcardType)cls;
if (wildcardType.getLowerBounds().length > 0) {
return wildcardType.getLowerBounds()[0];
}
if (wildcardType.getUpperBounds().length > 0) {
return wildcardType.getUpperBounds()[0];
}
}
}
}
return null;
}
public boolean isDynamicParameter() {
return wrappedParameter.getDynamicParameter() != null;
}
}