diff options
Diffstat (limited to 'src/main/java/com/beust/jcommander/Parameterized.java')
-rw-r--r-- | src/main/java/com/beust/jcommander/Parameterized.java | 223 |
1 files changed, 142 insertions, 81 deletions
diff --git a/src/main/java/com/beust/jcommander/Parameterized.java b/src/main/java/com/beust/jcommander/Parameterized.java index ff8753b..3264008 100644 --- a/src/main/java/com/beust/jcommander/Parameterized.java +++ b/src/main/java/com/beust/jcommander/Parameterized.java @@ -1,14 +1,18 @@ 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.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Collections; import java.util.List; +import java.util.Set; /** * Encapsulate a field or a method annotated with @Parameter or @DynamicParameter @@ -16,129 +20,175 @@ import java.util.List; public class Parameterized { // Either a method or a field - private Field m_field; - private Method m_method; - private Method m_getter; + private Field field; + private Method method; + private Method getter; // Either of these two - private WrappedParameter m_wrappedParameter; - private ParametersDelegate m_parametersDelegate; + private WrappedParameter wrappedParameter; + private ParametersDelegate parametersDelegate; public Parameterized(WrappedParameter wp, ParametersDelegate pd, Field field, Method method) { - m_wrappedParameter = wp; - m_method = method; - m_field = field; - if (m_field != null) { - m_field.setAccessible(true); + wrappedParameter = wp; + this.method = method; + this.field = field; + if (this.field != null) { + setFieldAccessible(this.field); } - m_parametersDelegate = pd; + parametersDelegate = pd; + } + + /** + * 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. + * + * @param arg 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<? extends Object> cls = arg.getClass(); - while (!Object.class.equals(cls)) { + 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)); + f, null)); } else if (dynamicParameter != null) { result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null, - f, null)); + f, null)); } else if (delegateAnnotation != null) { result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation, - f, null)); + f, null)); } } - cls = cls.getSuperclass(); - } - // Reassigning - cls = arg.getClass(); - while (!Object.class.equals(cls)) { + // 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)); + null, m)); } else if (dynamicParameter != null) { - result.add(new Parameterized(new WrappedParameter((DynamicParameter) annotation), null, - null, m)); + result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null, + null, m)); } else if (delegateAnnotation != null) { result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation, - null, m)); + null, m)); } } - cls = cls.getSuperclass(); } return result; } public WrappedParameter getWrappedParameter() { - return m_wrappedParameter; + return wrappedParameter; } public Class<?> getType() { - if (m_method != null) { - return m_method.getParameterTypes()[0]; + if (method != null) { + return method.getParameterTypes()[0]; } else { - return m_field.getType(); + return field.getType(); } } public String getName() { - if (m_method != null) { - return m_method.getName(); + if (method != null) { + return method.getName(); } else { - return m_field.getName(); + return field.getName(); } } public Object get(Object object) { try { - if (m_method != null) { - if (m_getter == null) { - m_getter = m_method.getDeclaringClass() - .getMethod("g" + m_method.getName().substring(1), - new Class[0]); + if (method != null) { + if (getter == null) { + getter = method.getDeclaringClass() + .getMethod("g" + method.getName().substring(1)); } - return m_getter.invoke(object); + return getter.invoke(object); } else { - return m_field.get(object); + return field.get(object); } - } catch (SecurityException e) { + } catch (SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { throw new ParameterException(e); } catch (NoSuchMethodException e) { // Try to find a field - String name = m_method.getName(); + String name = method.getName(); String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4); Object result = null; try { - Field field = m_method.getDeclaringClass().getDeclaredField(fieldName); + Field field = method.getDeclaringClass().getDeclaredField(fieldName); if (field != null) { - field.setAccessible(true); + setFieldAccessible(field); result = field.get(object); } - } catch(NoSuchFieldException ex) { - // ignore - } catch(IllegalAccessException ex) { + } catch(NoSuchFieldException | IllegalAccessException ex) { // ignore } return result; - } catch (IllegalArgumentException e) { - throw new ParameterException(e); - } catch (IllegalAccessException e) { - throw new ParameterException(e); - } catch (InvocationTargetException e) { - throw new ParameterException(e); } } @@ -146,8 +196,8 @@ public class Parameterized { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((m_field == null) ? 0 : m_field.hashCode()); - result = prime * result + ((m_method == null) ? 0 : m_method.hashCode()); + result = prime * result + ((field == null) ? 0 : field.hashCode()); + result = prime * result + ((method == null) ? 0 : method.hashCode()); return result; } @@ -160,73 +210,84 @@ public class Parameterized { if (getClass() != obj.getClass()) return false; Parameterized other = (Parameterized) obj; - if (m_field == null) { - if (other.m_field != null) + if (field == null) { + if (other.field != null) return false; - } else if (!m_field.equals(other.m_field)) + } else if (!field.equals(other.field)) return false; - if (m_method == null) { - if (other.m_method != null) + if (method == null) { + if (other.method != null) return false; - } else if (!m_method.equals(other.m_method)) + } else if (!method.equals(other.method)) return false; return true; } public boolean isDynamicParameter(Field field) { - if (m_method != null) { - return m_method.getAnnotation(DynamicParameter.class) != null; + if (method != null) { + return method.getAnnotation(DynamicParameter.class) != null; } else { - return m_field.getAnnotation(DynamicParameter.class) != null; + 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 String errorMessage(Method m, Exception ex) { + return "Could not invoke " + m + "\n Reason: " + ex.getMessage(); } public void set(Object object, Object value) { try { - if (m_method != null) { - m_method.invoke(object, value); + if (method != null) { + method.invoke(object, value); } else { - m_field.set(object, value); + field.set(object, value); } - } catch (IllegalArgumentException ex) { - throw new ParameterException(ex); - } catch (IllegalAccessException ex) { - throw new ParameterException(ex); + } 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(ex); + throw new ParameterException(errorMessage(method, ex)); } } } public ParametersDelegate getDelegateAnnotation() { - return m_parametersDelegate; + return parametersDelegate; } public Type getGenericType() { - if (m_method != null) { - return m_method.getGenericParameterTypes()[0]; + if (method != null) { + return method.getGenericParameterTypes()[0]; } else { - return m_field.getGenericType(); + return field.getGenericType(); } } public Parameter getParameter() { - return m_wrappedParameter.getParameter(); + return wrappedParameter.getParameter(); } /** * @return the generic type of the collection for this field, or null if not applicable. */ public Type findFieldGenericType() { - if (m_method != null) { + if (method != null) { return null; } else { - if (m_field.getGenericType() instanceof ParameterizedType) { - ParameterizedType p = (ParameterizedType) m_field.getGenericType(); + if (field.getGenericType() instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType) field.getGenericType(); Type cls = p.getActualTypeArguments()[0]; if (cls instanceof Class) { return cls; @@ -238,7 +299,7 @@ public class Parameterized { } public boolean isDynamicParameter() { - return m_wrappedParameter.getDynamicParameter() != null; + return wrappedParameter.getDynamicParameter() != null; } } |