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 com.lexicalscope.fluentreflection.Visibility.visibilityFromModifiers;
20  import static java.lang.String.format;
21  
22  import java.lang.reflect.Field;
23  import java.lang.reflect.Modifier;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.commons.lang3.builder.EqualsBuilder;
28  import org.apache.commons.lang3.builder.HashCodeBuilder;
29  
30  import com.google.inject.TypeLiteral;
31  
32  final class FluentFieldImpl extends AbstractFluentAnnotated implements FluentField {
33      private final ReflectedTypeFactory reflectedTypeFactory;
34      private final FluentClass<?> reflectedClass;
35      private final TypeLiteral<?> typeLiteral;
36      private final Field field;
37  
38      public FluentFieldImpl(
39              final ReflectedTypeFactory reflectedTypeFactory,
40              final FluentClass<?> reflectedClass,
41              final TypeLiteral<?> typeLiteral,
42              final Field field) {
43          super(reflectedTypeFactory, field);
44          this.reflectedTypeFactory = reflectedTypeFactory;
45          this.reflectedClass = reflectedClass;
46          this.typeLiteral = typeLiteral;
47          this.field = field;
48  
49          try { field.setAccessible(true); } catch(final SecurityException e)
50          { /* ignore, if we can set the field as accessible we get much improved performance */ }
51      }
52  
53      @Override public String name() {
54          return field.getName();
55      }
56  
57      @Override public FluentClass<?> declarer() {
58          return reflectedTypeFactory.reflect(typeLiteral);
59      }
60  
61      @Override public boolean isStatic() {
62          return Modifier.isStatic(field.getModifiers());
63      }
64  
65      @Override public boolean isFinal() {
66          return Modifier.isFinal(field.getModifiers());
67      }
68  
69      @Override public String property() {
70          return field.getName();
71      }
72  
73      @Override public Field member() {
74          return field;
75      }
76  
77      @Override public Visibility visibility() {
78          return visibilityFromModifiers(field.getModifiers());
79      }
80  
81      @Override public String toString() {
82          final String visibility;
83          if (visibility().toString().isEmpty())
84          {
85              visibility = visibility().toString();
86          }
87          else
88          {
89              visibility = visibility().toString() + " ";
90          }
91  
92          final String staticModifier;
93          if (isStatic())
94          {
95              staticModifier = "static ";
96          }
97          else
98          {
99              staticModifier = "";
100         }
101 
102         final String finalModifier;
103         if (isFinal())
104         {
105             finalModifier = "final ";
106         }
107         else
108         {
109             finalModifier = "";
110         }
111 
112         return format(
113                 "%s%s%s%s %s",
114                 visibility,
115                 staticModifier,
116                 finalModifier,
117                 type(),
118                 field.getName());
119     }
120 
121     @Override public boolean equals(final Object obj) {
122         if (obj != null && this.getClass().equals(obj.getClass())) {
123             final FluentFieldImpl that = (FluentFieldImpl) obj;
124             return new EqualsBuilder()
125                     .append(this.field, that.field)
126                     .append(this.typeLiteral, that.typeLiteral)
127                     .isEquals();
128         }
129         return false;
130     }
131 
132     @Override public int hashCode() {
133         return new HashCodeBuilder().append(field).append(typeLiteral).toHashCode();
134     }
135 
136     @Override public int argCount() {
137         return 1;
138     }
139 
140     @Override public List<FluentClass<?>> args() {
141         return new ArrayList<FluentClass<?>>();
142     }
143 
144     @Override public FluentClass<?> type() {
145         final TypeLiteral<?> returnType = typeLiteral.getFieldType(field);
146         if (returnType == null) {
147             return null;
148         }
149         return reflectedTypeFactory.reflect(returnType);
150     }
151 
152     @Override public Object callRaw(final Object... args) {
153         if(args == null || args.length == 0 || args[0] == null) {
154             throw new ReflectionRuntimeException("reading a field requires an instance argument");
155         } else if (args.length > 2) {
156             throw new ReflectionRuntimeException("reading a field requires one argument, writing a field requires two arguments. Got " + args.length + " arguments");
157         }
158 
159         try {
160             final Object fieldValue = field.get(args[0]);
161             if(args.length == 2)
162             {
163                 field.set(args[0], args[1]);
164             }
165             return fieldValue;
166         } catch (final IllegalArgumentException e) {
167             throw new IllegalArgumentRuntimeException(e, field, args[0]);
168         } catch (final IllegalAccessException e) {
169             throw new IllegalAccessRuntimeException(e, field);
170         }
171     }
172 
173     @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public FluentObject<?> call(final Object... args) {
174         final Object object = callRaw(args);
175         if(object == null) {
176             return null;
177         }
178         return reflectedTypeFactory.reflect((Class) object.getClass(), object);
179     }
180 
181     @Override public <T> FluentCall<T> as(final Class<T> returnType) {
182         return new AbstractCall<T>(reflectedTypeFactory) {
183             @Override public T callRaw(final Object... args) {
184                 return returnType.cast(FluentFieldImpl.this.callRaw(args));
185             }
186         };
187     }
188 }