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;

Encapsulate a field or a method annotated with @Parameter or @DynamicParameter
/** * Encapsulate a field or a method annotated with @Parameter or @DynamicParameter */
public class Parameterized { // Either a method or a field private Field field; private Method method; private Method getter; // Either of these two 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; }
Recursive handler for describing the set of classes while using the setOfClasses parameter as a collector
Params:
  • inputClass – the class to analyze
  • setOfClasses – the set collector to collect the results
/** * Recursive handler for describing the set of classes while * using the setOfClasses parameter as a collector * * @param inputClass the class to analyze * @param setOfClasses the set collector to collect the results */
private static void describeClassTree(Class<?> inputClass, Set<Class<?>> setOfClasses) { // can't map null class if(inputClass == null) { return; } // don't further analyze a class that has been analyzed already if(Object.class.equals(inputClass) || setOfClasses.contains(inputClass)) { return; } // add to analysis set setOfClasses.add(inputClass); // perform super class analysis describeClassTree(inputClass.getSuperclass(), setOfClasses); // perform analysis on interfaces for(Class<?> hasInterface : inputClass.getInterfaces()) { describeClassTree(hasInterface, setOfClasses); } }
Given an object return the set of classes that it extends or implements.
Params:
  • inputClass – object to describe
Returns:set of classes that are implemented or extended by that object
/** * Given an object return the set of classes that it extends * or implements. * * @param inputClass object to describe * @return set of classes that are implemented or extended by that object */
private static Set<Class<?>> describeClassTree(Class<?> inputClass) { if(inputClass == null) { return Collections.emptySet(); } // create result collector Set<Class<?>> classes = Sets.newLinkedHashSet(); // describe tree describeClassTree(inputClass, classes); return classes; } public static List<Parameterized> parseArg(Object arg) { List<Parameterized> result = Lists.newArrayList(); Class<?> rootClass = arg.getClass(); // get the list of types that are extended or implemented by the root class // and all of its parent types Set<Class<?>> types = describeClassTree(rootClass); // analyze each type for(Class<?> cls : types) { // check fields 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)); } } // check methods 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) { // Try to find a field 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) { // ignore } return result; } } private void setGetter(Object object) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if(Boolean.class.getSimpleName().toLowerCase().equals(getType().getName())){ // try is<fieldname> notation try { getter = object.getClass() .getMethod("is" + method.getName().substring(3)); // we have found a is<fieldname> getter we can return return; } catch (NoSuchMethodException n){ // if not found ignore exception and try with default get<fieldname> below } } 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 a ParameterException was thrown, don't wrap it into another one 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(); }
Returns:the generic type of the collection for this field, or null if not applicable.
/** * @return the generic type of the collection for this field, or null if not applicable. */
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; } }