1package com.beust.jcommander;
2
3import com.beust.jcommander.internal.Lists;
4
5import java.lang.annotation.Annotation;
6import java.lang.reflect.Field;
7import java.lang.reflect.InvocationTargetException;
8import java.lang.reflect.Method;
9import java.lang.reflect.ParameterizedType;
10import java.lang.reflect.Type;
11import java.util.List;
12
13/**
14 * Encapsulate a field or a method annotated with @Parameter or @DynamicParameter
15 */
16public class Parameterized {
17
18  // Either a method or a field
19  private Field m_field;
20  private Method m_method;
21  private Method m_getter;
22
23  // Either of these two
24  private WrappedParameter m_wrappedParameter;
25  private ParametersDelegate m_parametersDelegate;
26
27  public Parameterized(WrappedParameter wp, ParametersDelegate pd,
28      Field field, Method method) {
29    m_wrappedParameter = wp;
30    m_method = method;
31    m_field = field;
32    if (m_field != null) {
33      m_field.setAccessible(true);
34    }
35    m_parametersDelegate = pd;
36  }
37
38  public static List<Parameterized> parseArg(Object arg) {
39    List<Parameterized> result = Lists.newArrayList();
40
41    Class<? extends Object> cls = arg.getClass();
42    while (!Object.class.equals(cls)) {
43      for (Field f : cls.getDeclaredFields()) {
44        Annotation annotation = f.getAnnotation(Parameter.class);
45        Annotation delegateAnnotation = f.getAnnotation(ParametersDelegate.class);
46        Annotation dynamicParameter = f.getAnnotation(DynamicParameter.class);
47        if (annotation != null) {
48          result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
49              f, null));
50        } else if (dynamicParameter != null) {
51          result.add(new Parameterized(new WrappedParameter((DynamicParameter) dynamicParameter), null,
52              f, null));
53        } else if (delegateAnnotation != null) {
54          result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
55              f, null));
56        }
57      }
58      cls = cls.getSuperclass();
59    }
60
61    // Reassigning
62    cls = arg.getClass();
63    while (!Object.class.equals(cls)) {
64      for (Method m : cls.getDeclaredMethods()) {
65        Annotation annotation = m.getAnnotation(Parameter.class);
66        Annotation delegateAnnotation = m.getAnnotation(ParametersDelegate.class);
67        Annotation dynamicParameter = m.getAnnotation(DynamicParameter.class);
68        if (annotation != null) {
69          result.add(new Parameterized(new WrappedParameter((Parameter) annotation), null,
70              null, m));
71        } else if (dynamicParameter != null) {
72          result.add(new Parameterized(new WrappedParameter((DynamicParameter) annotation), null,
73              null, m));
74        } else if (delegateAnnotation != null) {
75          result.add(new Parameterized(null, (ParametersDelegate) delegateAnnotation,
76              null, m));
77        }
78      }
79      cls = cls.getSuperclass();
80    }
81
82    return result;
83  }
84
85  public WrappedParameter getWrappedParameter() {
86    return m_wrappedParameter;
87  }
88
89  public Class<?> getType() {
90    if (m_method != null) {
91      return m_method.getParameterTypes()[0];
92    } else {
93      return m_field.getType();
94    }
95  }
96
97  public String getName() {
98    if (m_method != null) {
99      return m_method.getName();
100    } else {
101      return m_field.getName();
102    }
103  }
104
105  public Object get(Object object) {
106    try {
107      if (m_method != null) {
108        if (m_getter == null) {
109            m_getter = m_method.getDeclaringClass()
110                .getMethod("g" + m_method.getName().substring(1),
111                new Class[0]);
112        }
113        return m_getter.invoke(object);
114      } else {
115        return m_field.get(object);
116      }
117    } catch (SecurityException e) {
118      throw new ParameterException(e);
119    } catch (NoSuchMethodException e) {
120      // Try to find a field
121      String name = m_method.getName();
122      String fieldName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
123      Object result = null;
124      try {
125        Field field = m_method.getDeclaringClass().getDeclaredField(fieldName);
126        if (field != null) {
127          field.setAccessible(true);
128          result = field.get(object);
129        }
130      } catch(NoSuchFieldException ex) {
131        // ignore
132      } catch(IllegalAccessException ex) {
133        // ignore
134      }
135      return result;
136    } catch (IllegalArgumentException e) {
137      throw new ParameterException(e);
138    } catch (IllegalAccessException e) {
139      throw new ParameterException(e);
140    } catch (InvocationTargetException e) {
141      throw new ParameterException(e);
142    }
143  }
144
145  @Override
146  public int hashCode() {
147    final int prime = 31;
148    int result = 1;
149    result = prime * result + ((m_field == null) ? 0 : m_field.hashCode());
150    result = prime * result + ((m_method == null) ? 0 : m_method.hashCode());
151    return result;
152  }
153
154  @Override
155  public boolean equals(Object obj) {
156    if (this == obj)
157      return true;
158    if (obj == null)
159      return false;
160    if (getClass() != obj.getClass())
161      return false;
162    Parameterized other = (Parameterized) obj;
163    if (m_field == null) {
164      if (other.m_field != null)
165        return false;
166    } else if (!m_field.equals(other.m_field))
167      return false;
168    if (m_method == null) {
169      if (other.m_method != null)
170        return false;
171    } else if (!m_method.equals(other.m_method))
172      return false;
173    return true;
174  }
175
176  public boolean isDynamicParameter(Field field) {
177    if (m_method != null) {
178      return m_method.getAnnotation(DynamicParameter.class) != null;
179    } else {
180      return m_field.getAnnotation(DynamicParameter.class) != null;
181    }
182  }
183
184  public void set(Object object, Object value) {
185    try {
186      if (m_method != null) {
187        m_method.invoke(object, value);
188      } else {
189          m_field.set(object, value);
190      }
191    } catch (IllegalArgumentException ex) {
192      throw new ParameterException(ex);
193    } catch (IllegalAccessException ex) {
194      throw new ParameterException(ex);
195    } catch (InvocationTargetException ex) {
196      // If a ParameterException was thrown, don't wrap it into another one
197      if (ex.getTargetException() instanceof ParameterException) {
198        throw (ParameterException) ex.getTargetException();
199      } else {
200        throw new ParameterException(ex);
201      }
202    }
203  }
204
205  public ParametersDelegate getDelegateAnnotation() {
206    return m_parametersDelegate;
207  }
208
209  public Type getGenericType() {
210    if (m_method != null) {
211      return m_method.getGenericParameterTypes()[0];
212    } else {
213      return m_field.getGenericType();
214    }
215  }
216
217  public Parameter getParameter() {
218    return m_wrappedParameter.getParameter();
219  }
220
221  /**
222   * @return the generic type of the collection for this field, or null if not applicable.
223   */
224  public Type findFieldGenericType() {
225    if (m_method != null) {
226      return null;
227    } else {
228      if (m_field.getGenericType() instanceof ParameterizedType) {
229        ParameterizedType p = (ParameterizedType) m_field.getGenericType();
230        Type cls = p.getActualTypeArguments()[0];
231        if (cls instanceof Class) {
232          return cls;
233        }
234      }
235    }
236
237    return null;
238  }
239
240  public boolean isDynamicParameter() {
241    return m_wrappedParameter.getDynamicParameter() != null;
242  }
243
244}
245