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      return getAnnotation(a, MetaProvider.DEFAULT);
102   }
103
104   /**
105    * Returns the specified annotation on this field.
106    *
107    * @param a The annotation to look for.
108    * @param mp The meta provider for looking up annotations on reflection objects (classes, methods, fields, constructors).
109    * @return The annotation, or <jk>null</jk> if not found.
110    */
111   public <T extends Annotation> T getAnnotation(Class<T> a, MetaProvider mp) {
112      return mp.getAnnotation(a, f);
113   }
114
115   /**
116    * Returns <jk>true</jk> if the specified annotation is present.
117    *
118    * @param a The annotation to check for.
119    * @return <jk>true</jk> if the specified annotation is present.
120    */
121   public boolean hasAnnotation(Class<? extends Annotation> a) {
122      return f.isAnnotationPresent(a);
123   }
124
125   /**
126    * Returns <jk>true</jk> if the specified annotation is present.
127    *
128    * @param a The annotation to check for.
129    * @param mp The meta provider for looking up annotations on reflection objects (classes, methods, fields, constructors).
130    * @return <jk>true</jk> if the specified annotation is present.
131    */
132   public boolean hasAnnotation(Class<? extends Annotation> a, MetaProvider mp) {
133      return mp.getAnnotation(a, f) != null;
134   }
135
136   //-----------------------------------------------------------------------------------------------------------------
137   // Characteristics
138   //-----------------------------------------------------------------------------------------------------------------
139
140   /**
141    * Returns <jk>true</jk> if all specified flags are applicable to this field.
142    *
143    * @param flags The flags to test for.
144    * @return <jk>true</jk> if all specified flags are applicable to this field.
145    */
146   public boolean isAll(ReflectFlags...flags) {
147      for (ReflectFlags f : flags) {
148         switch (f) {
149            case DEPRECATED:
150               if (isNotDeprecated())
151                  return false;
152               break;
153            case NOT_DEPRECATED:
154               if (isDeprecated())
155                  return false;
156               break;
157            case PUBLIC:
158               if (isNotPublic())
159                  return false;
160               break;
161            case NOT_PUBLIC:
162               if (isPublic())
163                  return false;
164               break;
165            case STATIC:
166               if (isNotStatic())
167                  return false;
168               break;
169            case NOT_STATIC:
170               if (isStatic())
171                  return false;
172               break;
173            case TRANSIENT:
174               if (isNotTransient())
175                  return false;
176               break;
177            case NOT_TRANSIENT:
178               if (isTransient())
179                  return false;
180               break;
181            default:
182               throw new RuntimeException("Invalid flag for field: " + f);
183         }
184      }
185      return true;
186   }
187
188   /**
189    * Returns <jk>true</jk> if all specified flags are applicable to this field.
190    *
191    * @param flags The flags to test for.
192    * @return <jk>true</jk> if all specified flags are applicable to this field.
193    */
194   public boolean isAny(ReflectFlags...flags) {
195      for (ReflectFlags f : flags) {
196         switch (f) {
197            case DEPRECATED:
198               if (isDeprecated())
199                  return true;
200               break;
201            case NOT_DEPRECATED:
202               if (isNotDeprecated())
203                  return true;
204               break;
205            case PUBLIC:
206               if (isPublic())
207                  return true;
208               break;
209            case NOT_PUBLIC:
210               if (isNotPublic())
211                  return true;
212               break;
213            case STATIC:
214               if (isStatic())
215                  return true;
216               break;
217            case NOT_STATIC:
218               if (isNotStatic())
219                  return true;
220               break;
221            case TRANSIENT:
222               if (isTransient())
223                  return true;
224               break;
225            case NOT_TRANSIENT:
226               if (isNotTransient())
227                  return true;
228               break;
229            default:
230               throw new RuntimeException("Invalid flag for field: " + f);
231         }
232      }
233      return false;
234   }
235
236   /**
237    * Returns <jk>true</jk> if this field has the {@link Deprecated @Deprecated} annotation on it.
238    *
239    * @return <jk>true</jk> if this field has the {@link Deprecated @Deprecated} annotation on it.
240    */
241   public boolean isDeprecated() {
242      return f.isAnnotationPresent(Deprecated.class);
243   }
244
245   /**
246    * Returns <jk>true</jk> if this field doesn't have the {@link Deprecated @Deprecated} annotation on it.
247    *
248    * @return <jk>true</jk> if this field doesn't have the {@link Deprecated @Deprecated} annotation on it.
249    */
250   public boolean isNotDeprecated() {
251      return ! f.isAnnotationPresent(Deprecated.class);
252   }
253
254   /**
255    * Returns <jk>true</jk> if this field is public.
256    *
257    * @return <jk>true</jk> if this field is public.
258    */
259   public boolean isPublic() {
260      return Modifier.isPublic(f.getModifiers());
261   }
262
263   /**
264    * Returns <jk>true</jk> if this field is not public.
265    *
266    * @return <jk>true</jk> if this field is not public.
267    */
268   public boolean isNotPublic() {
269      return ! Modifier.isPublic(f.getModifiers());
270   }
271
272   /**
273    * Returns <jk>true</jk> if this field is static.
274    *
275    * @return <jk>true</jk> if this field is static.
276    */
277   public boolean isStatic() {
278      return Modifier.isStatic(f.getModifiers());
279   }
280
281   /**
282    * Returns <jk>true</jk> if this field is not static.
283    *
284    * @return <jk>true</jk> if this field is not static.
285    */
286   public boolean isNotStatic() {
287      return ! Modifier.isStatic(f.getModifiers());
288   }
289
290   /**
291    * Returns <jk>true</jk> if this field is transient.
292    *
293    * @return <jk>true</jk> if this field is transient.
294    */
295   public boolean isTransient() {
296      return Modifier.isTransient(f.getModifiers());
297   }
298
299   /**
300    * Returns <jk>true</jk> if this field is not transient.
301    *
302    * @return <jk>true</jk> if this field is not transient.
303    */
304   public boolean isNotTransient() {
305      return ! Modifier.isTransient(f.getModifiers());
306   }
307
308   /**
309    * Returns <jk>true</jk> if the field has the specified name.
310    *
311    * @param name The name to compare against.
312    * @return <jk>true</jk> if the field has the specified name.
313    */
314   public boolean hasName(String name) {
315      return f.getName().equals(name);
316   }
317
318   //-----------------------------------------------------------------------------------------------------------------
319   // Visibility
320   //-----------------------------------------------------------------------------------------------------------------
321
322   /**
323    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
324    *
325    * @return <jk>true</jk> if call was successful.
326    */
327   public boolean setAccessible() {
328      try {
329         if (! (f.isAccessible()))
330            f.setAccessible(true);
331         return true;
332      } catch (SecurityException e) {
333         return false;
334      }
335   }
336
337   /**
338    * Identifies if the specified visibility matches this field.
339    *
340    * @param v The visibility to validate against.
341    * @return <jk>true</jk> if this visibility matches the modifier attribute of this field.
342    */
343   public boolean isVisible(Visibility v) {
344      return v.isVisible(f);
345   }
346
347   //-----------------------------------------------------------------------------------------------------------------
348   // Other methods.
349   //-----------------------------------------------------------------------------------------------------------------
350
351   /**
352    * Returns the type of this field.
353    *
354    * @return The type of this field.
355    */
356   public ClassInfo getType() {
357      if (type == null)
358         type = ClassInfo.of(f.getType());
359      return type;
360   }
361
362   @Override
363   public String toString() {
364      return f.getDeclaringClass().getName() + "." + f.getName();
365   }
366
367   @Override
368   public int compareTo(FieldInfo o) {
369      return getName().compareTo(o.getName());
370   }
371
372   /**
373    * Returns the name of this field.
374    *
375    * @return The name of this field.
376    */
377   public String getName() {
378      return f.getName();
379   }
380}