001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.reflect;
014
015import java.lang.annotation.*;
016import java.lang.reflect.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.annotation.*;
020
021/**
022 * Lightweight utility class for introspecting information about a field.
023 */
024@BeanIgnore
025public final class FieldInfo implements Comparable<FieldInfo> {
026
027   private final Field f;
028   private ClassInfo declaringClass, type;
029
030   //-----------------------------------------------------------------------------------------------------------------
031   // Instantiation.
032   //-----------------------------------------------------------------------------------------------------------------
033
034   /**
035    * Constructor.
036    *
037    * @param declaringClass The class that declares this method.
038    * @param f The field being wrapped.
039    */
040   protected FieldInfo(ClassInfo declaringClass, Field f) {
041      this.declaringClass = declaringClass;
042      this.f = f;
043   }
044
045   /**
046    * Convenience method for instantiating a {@link FieldInfo};
047    *
048    * @param declaringClass The class that declares this method.
049    * @param f The field being wrapped.
050    * @return A new {@link FieldInfo} object, or <jk>null</jk> if the field was null.
051    */
052   public static FieldInfo of(ClassInfo declaringClass, Field f) {
053      if (f == null)
054         return null;
055      return new FieldInfo(declaringClass, f);
056   }
057
058   /**
059    * Convenience method for instantiating a {@link FieldInfo};
060    *
061    * @param f The field being wrapped.
062    * @return A new {@link FieldInfo} object, or <jk>null</jk> if the field was null.
063    */
064   public static FieldInfo of(Field f) {
065      if (f == null)
066         return null;
067      return new FieldInfo(null, f);
068   }
069
070   /**
071    * Returns the wrapped field.
072    *
073    * @return The wrapped field.
074    */
075   public Field inner() {
076      return f;
077   }
078
079   /**
080    * Returns metadata about the declaring class.
081    *
082    * @return Metadata about the declaring class.
083    */
084   public ClassInfo getDeclaringClass() {
085      if (declaringClass == null)
086         declaringClass = ClassInfo.of(f.getDeclaringClass());
087      return declaringClass;
088   }
089
090   //-----------------------------------------------------------------------------------------------------------------
091   // Annotations
092   //-----------------------------------------------------------------------------------------------------------------
093
094   /**
095    * Returns the specified annotation on this field.
096    *
097    * @param a The annotation to look for.
098    * @return The annotation, or <jk>null</jk> if not found.
099    */
100   public <T extends Annotation> T getAnnotation(Class<T> a) {
101      if (a == null)
102         return null;
103      return f.getAnnotation(a);
104   }
105
106   /**
107    * Returns <jk>true</jk> if the specified annotation is present.
108    *
109    * @param a The annotation to check for.
110    * @return <jk>true</jk> if the specified annotation is present.
111    */
112   public boolean hasAnnotation(Class<? extends Annotation> a) {
113      return f.isAnnotationPresent(a);
114   }
115
116   //-----------------------------------------------------------------------------------------------------------------
117   // Characteristics
118   //-----------------------------------------------------------------------------------------------------------------
119
120   /**
121    * Returns <jk>true</jk> if all specified flags are applicable to this field.
122    *
123    * @param flags The flags to test for.
124    * @return <jk>true</jk> if all specified flags are applicable to this field.
125    */
126   public boolean isAll(ReflectFlags...flags) {
127      for (ReflectFlags f : flags) {
128         switch (f) {
129            case DEPRECATED:
130               if (isNotDeprecated())
131                  return false;
132               break;
133            case NOT_DEPRECATED:
134               if (isDeprecated())
135                  return false;
136               break;
137            case PUBLIC:
138               if (isNotPublic())
139                  return false;
140               break;
141            case NOT_PUBLIC:
142               if (isPublic())
143                  return false;
144               break;
145            case STATIC:
146               if (isNotStatic())
147                  return false;
148               break;
149            case NOT_STATIC:
150               if (isStatic())
151                  return false;
152               break;
153            case TRANSIENT:
154               if (isNotTransient())
155                  return false;
156               break;
157            case NOT_TRANSIENT:
158               if (isTransient())
159                  return false;
160               break;
161            default:
162               throw new RuntimeException("Invalid flag for field: " + f);
163         }
164      }
165      return true;
166   }
167
168   /**
169    * Returns <jk>true</jk> if all specified flags are applicable to this field.
170    *
171    * @param flags The flags to test for.
172    * @return <jk>true</jk> if all specified flags are applicable to this field.
173    */
174   public boolean isAny(ReflectFlags...flags) {
175      for (ReflectFlags f : flags) {
176         switch (f) {
177            case DEPRECATED:
178               if (isDeprecated())
179                  return true;
180               break;
181            case NOT_DEPRECATED:
182               if (isNotDeprecated())
183                  return true;
184               break;
185            case PUBLIC:
186               if (isPublic())
187                  return true;
188               break;
189            case NOT_PUBLIC:
190               if (isNotPublic())
191                  return true;
192               break;
193            case STATIC:
194               if (isStatic())
195                  return true;
196               break;
197            case NOT_STATIC:
198               if (isNotStatic())
199                  return true;
200               break;
201            case TRANSIENT:
202               if (isTransient())
203                  return true;
204               break;
205            case NOT_TRANSIENT:
206               if (isNotTransient())
207                  return true;
208               break;
209            default:
210               throw new RuntimeException("Invalid flag for field: " + f);
211         }
212      }
213      return false;
214   }
215
216   /**
217    * Returns <jk>true</jk> if this field has the {@link Deprecated @Deprecated} annotation on it.
218    *
219    * @return <jk>true</jk> if this field has the {@link Deprecated @Deprecated} annotation on it.
220    */
221   public boolean isDeprecated() {
222      return f.isAnnotationPresent(Deprecated.class);
223   }
224
225   /**
226    * Returns <jk>true</jk> if this field doesn't have the {@link Deprecated @Deprecated} annotation on it.
227    *
228    * @return <jk>true</jk> if this field doesn't have the {@link Deprecated @Deprecated} annotation on it.
229    */
230   public boolean isNotDeprecated() {
231      return ! f.isAnnotationPresent(Deprecated.class);
232   }
233
234   /**
235    * Returns <jk>true</jk> if this field is public.
236    *
237    * @return <jk>true</jk> if this field is public.
238    */
239   public boolean isPublic() {
240      return Modifier.isPublic(f.getModifiers());
241   }
242
243   /**
244    * Returns <jk>true</jk> if this field is not public.
245    *
246    * @return <jk>true</jk> if this field is not public.
247    */
248   public boolean isNotPublic() {
249      return ! Modifier.isPublic(f.getModifiers());
250   }
251
252   /**
253    * Returns <jk>true</jk> if this field is static.
254    *
255    * @return <jk>true</jk> if this field is static.
256    */
257   public boolean isStatic() {
258      return Modifier.isStatic(f.getModifiers());
259   }
260
261   /**
262    * Returns <jk>true</jk> if this field is not static.
263    *
264    * @return <jk>true</jk> if this field is not static.
265    */
266   public boolean isNotStatic() {
267      return ! Modifier.isStatic(f.getModifiers());
268   }
269
270   /**
271    * Returns <jk>true</jk> if this field is transient.
272    *
273    * @return <jk>true</jk> if this field is transient.
274    */
275   public boolean isTransient() {
276      return Modifier.isTransient(f.getModifiers());
277   }
278
279   /**
280    * Returns <jk>true</jk> if this field is not transient.
281    *
282    * @return <jk>true</jk> if this field is not transient.
283    */
284   public boolean isNotTransient() {
285      return ! Modifier.isTransient(f.getModifiers());
286   }
287
288   /**
289    * Returns <jk>true</jk> if the field has the specified name.
290    *
291    * @param name The name to compare against.
292    * @return <jk>true</jk> if the field has the specified name.
293    */
294   public boolean hasName(String name) {
295      return f.getName().equals(name);
296   }
297
298   //-----------------------------------------------------------------------------------------------------------------
299   // Visibility
300   //-----------------------------------------------------------------------------------------------------------------
301
302   /**
303    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
304    *
305    * @return <jk>true</jk> if call was successful.
306    */
307   public boolean setAccessible() {
308      try {
309         if (! (f.isAccessible()))
310            f.setAccessible(true);
311         return true;
312      } catch (SecurityException e) {
313         return false;
314      }
315   }
316
317   /**
318    * Identifies if the specified visibility matches this field.
319    *
320    * @param v The visibility to validate against.
321    * @return <jk>true</jk> if this visibility matches the modifier attribute of this field.
322    */
323   public boolean isVisible(Visibility v) {
324      return v.isVisible(f);
325   }
326
327   //-----------------------------------------------------------------------------------------------------------------
328   // Other methods.
329   //-----------------------------------------------------------------------------------------------------------------
330
331   /**
332    * Returns the type of this field.
333    *
334    * @return The type of this field.
335    */
336   public ClassInfo getType() {
337      if (type == null)
338         type = ClassInfo.of(f.getType());
339      return type;
340   }
341
342   @Override
343   public String toString() {
344      return f.getDeclaringClass().getName() + "." + f.getName();
345   }
346
347   @Override
348   public int compareTo(FieldInfo o) {
349      return getName().compareTo(o.getName());
350   }
351
352   /**
353    * Returns the name of this field.
354    *
355    * @return The name of this field.
356    */
357   public String getName() {
358      return f.getName();
359   }
360}