View Javadoc

1   package com.lexicalscope.fluentreflection;
2   
3   /*
4    * Copyright 2011 Tim Wood
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import static ch.lambdaj.Lambda.*;
20  import static com.lexicalscope.fluentreflection.Visibility.visibilityFromModifiers;
21  import static java.lang.String.format;
22  import static java.lang.System.arraycopy;
23  
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.lang.reflect.Modifier;
27  import java.lang.reflect.TypeVariable;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.List;
31  
32  import org.apache.commons.lang3.builder.EqualsBuilder;
33  import org.apache.commons.lang3.builder.HashCodeBuilder;
34  
35  import com.google.inject.TypeLiteral;
36  
37  final class FluentMethodImpl extends AbstractFluentAnnotated implements FluentMethod {
38      private final ReflectedTypeFactory reflectedTypeFactory;
39      private final FluentClass<?> reflectedClass;
40      private final TypeLiteral<?> typeLiteral;
41      private final Method method;
42  
43      public FluentMethodImpl(
44              final ReflectedTypeFactory reflectedTypeFactory,
45              final FluentClass<?> reflectedClass,
46              final TypeLiteral<?> typeLiteral,
47              final Method method) {
48          super(reflectedTypeFactory, method);
49          this.reflectedTypeFactory = reflectedTypeFactory;
50          this.reflectedClass = reflectedClass;
51          this.typeLiteral = typeLiteral;
52          this.method = method;
53      }
54  
55      @Override public String name() {
56          return method.getName();
57      }
58  
59      @Override public List<FluentClass<?>> args() {
60          final List<FluentClass<?>> result = new ArrayList<FluentClass<?>>();
61          result.addAll(convert(
62                  typeLiteral.getParameterTypes(method),
63                  new ConvertTypeLiteralToReflectedType(reflectedTypeFactory)));
64          return result;
65      }
66  
67      @Override public int argCount() {
68          final int parameterCount = method.getParameterTypes().length;
69          if (isStatic()) {
70              return parameterCount;
71          }
72          return parameterCount;
73      }
74  
75      @Override public FluentClass<?> declarer() {
76          return reflectedTypeFactory.reflect(typeLiteral);
77      }
78  
79      @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public FluentObject<?> call(final Object... args) {
80          final Object object = callRaw(args);
81          if(object == null) {
82              return null;
83          }
84          return reflectedTypeFactory.reflect((Class) object.getClass(), object);
85      }
86  
87      @Override public Object callRaw(final Object... args) {
88          if (isStatic()) {
89              return invokeMethod(null, args);
90          } else {
91              if (args.length < 1) {
92                  throw new IllegalArgumentException("target instance must be specified as first argument when calling "
93                          + method);
94              }
95  
96              final Object[] remainingArguments = new Object[args.length - 1];
97              arraycopy(args, 1, remainingArguments, 0, args.length - 1);
98              return invokeMethod(args[0], remainingArguments);
99          }
100     }
101 
102     private Object invokeMethod(final Object instance, final Object[] arguments) {
103         try {
104             if (!method.isAccessible()) {
105                 method.setAccessible(true);
106             }
107             return method.invoke(instance, arguments);
108         } catch (final IllegalArgumentException e) {
109             throw new IllegalArgumentRuntimeException(e, method, instance, arguments);
110         } catch (final IllegalAccessException e) {
111             throw new IllegalAccessRuntimeException(e, method);
112         } catch (final InvocationTargetException e) {
113             throw new InvocationTargetRuntimeException(e, method);
114         }
115     }
116 
117     @Override public boolean isStatic() {
118         return Modifier.isStatic(method.getModifiers());
119     }
120 
121     @Override public boolean isFinal() {
122         return Modifier.isFinal(method.getModifiers());
123     }
124 
125     @Override public <T> FluentCall<T> as(final Class<T> returnType) {
126         return new AbstractCall<T>(reflectedTypeFactory) {
127             @Override public T callRaw(final Object... args) {
128                 return returnType.cast(FluentMethodImpl.this.callRaw(args));
129             }
130         };
131     }
132 
133     @Override public FluentClass<?> type() {
134         final TypeLiteral<?> returnType = typeLiteral.getReturnType(method);
135         if (returnType == null) {
136             return null;
137         }
138         return reflectedTypeFactory.reflect(returnType);
139     }
140 
141     @Override public String property() {
142         final String name = name();
143 
144         if (name.length() > 2) {
145             if (name.length() > 3) {
146                 if (name.startsWith("get") || name.startsWith("set")) {
147                     return initialLowerCase(name.substring(3));
148                 }
149             }
150             if (name.startsWith("is")) {
151                 return initialLowerCase(name.substring(2));
152             }
153         }
154         return method.getName();
155     }
156 
157     private String initialLowerCase(final String substring) {
158         return substring.substring(0, 1).toLowerCase() + substring.substring(1);
159     }
160 
161     @Override public Method member() {
162         return method;
163     }
164 
165     @Override public Visibility visibility() {
166         return visibilityFromModifiers(method.getModifiers());
167     }
168 
169     private List<TypeVariable<Method>> typeParameters()
170     {
171         return Arrays.asList(method.getTypeParameters());
172     }
173 
174     @Override public String toString() {
175         final String visibility;
176         if (visibility().toString().isEmpty())
177         {
178             visibility = visibility().toString();
179         }
180         else
181         {
182             visibility = visibility().toString() + " ";
183         }
184 
185         final String staticModifier;
186         if (isStatic())
187         {
188             staticModifier = "static ";
189         }
190         else
191         {
192             staticModifier = "";
193         }
194 
195         final String finalModifier;
196         if (isFinal())
197         {
198             finalModifier = "final ";
199         }
200         else
201         {
202             finalModifier = "";
203         }
204 
205         final String typeParameters;
206         if (typeParameters().isEmpty())
207         {
208             typeParameters = "";
209         }
210         else
211         {
212             typeParameters = "<" + joinFrom(typeParameters(), ", ").toString() + "> ";
213         }
214 
215         final String arguments;
216         if (argCount() > 0) {
217             arguments = joinFrom(args(), ", ").toString();
218         } else {
219             arguments = "";
220         }
221 
222         return format(
223                 "%s%s%s%s%s %s(%s)",
224                 visibility,
225                 staticModifier,
226                 finalModifier,
227                 typeParameters,
228                 type(),
229                 method.getName(),
230                 arguments);
231     }
232 
233     @Override public boolean equals(final Object obj) {
234         if (obj != null && this.getClass().equals(obj.getClass())) {
235             final FluentMethodImpl that = (FluentMethodImpl) obj;
236             return new EqualsBuilder()
237                     .append(this.method, that.method)
238                     .append(this.typeLiteral, that.typeLiteral)
239                     .isEquals();
240         }
241         return false;
242     }
243 
244     @Override public int hashCode() {
245         return new HashCodeBuilder().append(method).append(typeLiteral).toHashCode();
246     }
247 }