1 | 0 | package com.lexicalscope.fluentreflection; |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
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 | 7772 | super(reflectedTypeFactory, method); |
49 | 7772 | this.reflectedTypeFactory = reflectedTypeFactory; |
50 | 7772 | this.reflectedClass = reflectedClass; |
51 | 7772 | this.typeLiteral = typeLiteral; |
52 | 7772 | this.method = method; |
53 | 7772 | } |
54 | |
|
55 | |
@Override public String name() { |
56 | 7422 | return method.getName(); |
57 | |
} |
58 | |
|
59 | |
@Override public List<FluentClass<?>> args() { |
60 | 802 | final List<FluentClass<?>> result = new ArrayList<FluentClass<?>>(); |
61 | 1604 | result.addAll(convert( |
62 | 802 | typeLiteral.getParameterTypes(method), |
63 | 802 | new ConvertTypeLiteralToReflectedType(reflectedTypeFactory))); |
64 | 802 | return result; |
65 | |
} |
66 | |
|
67 | |
@Override public int argCount() { |
68 | 1756 | final int parameterCount = method.getParameterTypes().length; |
69 | 1756 | if (isStatic()) { |
70 | 24 | return parameterCount; |
71 | |
} |
72 | 1732 | return parameterCount; |
73 | |
} |
74 | |
|
75 | |
@Override public FluentClass<?> declarer() { |
76 | 104 | return reflectedTypeFactory.reflect(typeLiteral); |
77 | |
} |
78 | |
|
79 | |
@SuppressWarnings({ "unchecked", "rawtypes" }) @Override public FluentObject<?> call(final Object... args) { |
80 | 0 | final Object object = callRaw(args); |
81 | 0 | if(object == null) { |
82 | 0 | return null; |
83 | |
} |
84 | 0 | return reflectedTypeFactory.reflect((Class) object.getClass(), object); |
85 | |
} |
86 | |
|
87 | |
@Override public Object callRaw(final Object... args) { |
88 | 540 | if (isStatic()) { |
89 | 32 | return invokeMethod(null, args); |
90 | |
} else { |
91 | 508 | if (args.length < 1) { |
92 | 16 | throw new IllegalArgumentException("target instance must be specified as first argument when calling " |
93 | 8 | + method); |
94 | |
} |
95 | |
|
96 | 500 | final Object[] remainingArguments = new Object[args.length - 1]; |
97 | 500 | arraycopy(args, 1, remainingArguments, 0, args.length - 1); |
98 | 500 | return invokeMethod(args[0], remainingArguments); |
99 | |
} |
100 | |
} |
101 | |
|
102 | |
private Object invokeMethod(final Object instance, final Object[] arguments) { |
103 | |
try { |
104 | 532 | if (!method.isAccessible()) { |
105 | 476 | method.setAccessible(true); |
106 | |
} |
107 | 532 | return method.invoke(instance, arguments); |
108 | 8 | } catch (final IllegalArgumentException e) { |
109 | 8 | throw new IllegalArgumentRuntimeException(e, method, instance, arguments); |
110 | 0 | } catch (final IllegalAccessException e) { |
111 | 0 | throw new IllegalAccessRuntimeException(e, method); |
112 | 0 | } catch (final InvocationTargetException e) { |
113 | 0 | throw new InvocationTargetRuntimeException(e, method); |
114 | |
} |
115 | |
} |
116 | |
|
117 | |
@Override public boolean isStatic() { |
118 | 13560 | return Modifier.isStatic(method.getModifiers()); |
119 | |
} |
120 | |
|
121 | |
@Override public boolean isFinal() { |
122 | 144 | return Modifier.isFinal(method.getModifiers()); |
123 | |
} |
124 | |
|
125 | |
@Override public <T> FluentCall<T> as(final Class<T> returnType) { |
126 | 0 | return new AbstractCall<T>(reflectedTypeFactory) { |
127 | |
@Override public T callRaw(final Object... args) { |
128 | 0 | return returnType.cast(FluentMethodImpl.this.callRaw(args)); |
129 | |
} |
130 | |
}; |
131 | |
} |
132 | |
|
133 | |
@Override public FluentClass<?> type() { |
134 | 1608 | final TypeLiteral<?> returnType = typeLiteral.getReturnType(method); |
135 | 1608 | if (returnType == null) { |
136 | 0 | return null; |
137 | |
} |
138 | 1608 | return reflectedTypeFactory.reflect(returnType); |
139 | |
} |
140 | |
|
141 | |
@Override public String property() { |
142 | 1408 | final String name = name(); |
143 | |
|
144 | 1408 | if (name.length() > 2) { |
145 | 1408 | if (name.length() > 3) { |
146 | 1408 | if (name.startsWith("get") || name.startsWith("set")) { |
147 | 1376 | return initialLowerCase(name.substring(3)); |
148 | |
} |
149 | |
} |
150 | 32 | if (name.startsWith("is")) { |
151 | 0 | return initialLowerCase(name.substring(2)); |
152 | |
} |
153 | |
} |
154 | 32 | return method.getName(); |
155 | |
} |
156 | |
|
157 | |
private String initialLowerCase(final String substring) { |
158 | 1376 | return substring.substring(0, 1).toLowerCase() + substring.substring(1); |
159 | |
} |
160 | |
|
161 | |
@Override public Method member() { |
162 | 8 | return method; |
163 | |
} |
164 | |
|
165 | |
@Override public Visibility visibility() { |
166 | 320 | return visibilityFromModifiers(method.getModifiers()); |
167 | |
} |
168 | |
|
169 | |
private List<TypeVariable<Method>> typeParameters() |
170 | |
{ |
171 | 160 | return Arrays.asList(method.getTypeParameters()); |
172 | |
} |
173 | |
|
174 | |
@Override public String toString() { |
175 | |
final String visibility; |
176 | 144 | if (visibility().toString().isEmpty()) |
177 | |
{ |
178 | 120 | visibility = visibility().toString(); |
179 | |
} |
180 | |
else |
181 | |
{ |
182 | 24 | visibility = visibility().toString() + " "; |
183 | |
} |
184 | |
|
185 | |
final String staticModifier; |
186 | 144 | if (isStatic()) |
187 | |
{ |
188 | 16 | staticModifier = "static "; |
189 | |
} |
190 | |
else |
191 | |
{ |
192 | 128 | staticModifier = ""; |
193 | |
} |
194 | |
|
195 | |
final String finalModifier; |
196 | 144 | if (isFinal()) |
197 | |
{ |
198 | 8 | finalModifier = "final "; |
199 | |
} |
200 | |
else |
201 | |
{ |
202 | 136 | finalModifier = ""; |
203 | |
} |
204 | |
|
205 | |
final String typeParameters; |
206 | 144 | if (typeParameters().isEmpty()) |
207 | |
{ |
208 | 128 | typeParameters = ""; |
209 | |
} |
210 | |
else |
211 | |
{ |
212 | 16 | typeParameters = "<" + joinFrom(typeParameters(), ", ").toString() + "> "; |
213 | |
} |
214 | |
|
215 | |
final String arguments; |
216 | 144 | if (argCount() > 0) { |
217 | 48 | arguments = joinFrom(args(), ", ").toString(); |
218 | |
} else { |
219 | 96 | arguments = ""; |
220 | |
} |
221 | |
|
222 | 144 | return format( |
223 | 144 | "%s%s%s%s%s %s(%s)", |
224 | 144 | visibility, |
225 | 144 | staticModifier, |
226 | 144 | finalModifier, |
227 | 144 | typeParameters, |
228 | 144 | type(), |
229 | 144 | method.getName(), |
230 | 144 | arguments); |
231 | |
} |
232 | |
|
233 | |
@Override public boolean equals(final Object obj) { |
234 | 0 | if (obj != null && this.getClass().equals(obj.getClass())) { |
235 | 0 | final FluentMethodImpl that = (FluentMethodImpl) obj; |
236 | 0 | return new EqualsBuilder() |
237 | 0 | .append(this.method, that.method) |
238 | 0 | .append(this.typeLiteral, that.typeLiteral) |
239 | 0 | .isEquals(); |
240 | |
} |
241 | 0 | return false; |
242 | |
} |
243 | |
|
244 | |
@Override public int hashCode() { |
245 | 0 | return new HashCodeBuilder().append(method).append(typeLiteral).toHashCode(); |
246 | |
} |
247 | |
} |