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 static org.apache.juneau.internal.CollectionUtils.*; 016 017import java.lang.annotation.*; 018import java.lang.reflect.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.annotation.*; 022 023/** 024 * Lightweight utility class for introspecting information about a field. 025 */ 026@BeanIgnore 027public final class FieldInfo implements Comparable<FieldInfo> { 028 029 private final Field f; 030 private ClassInfo declaringClass, type; 031 032 //----------------------------------------------------------------------------------------------------------------- 033 // Instantiation. 034 //----------------------------------------------------------------------------------------------------------------- 035 036 /** 037 * Constructor. 038 * 039 * @param declaringClass The class that declares this method. 040 * @param f The field being wrapped. 041 */ 042 protected FieldInfo(ClassInfo declaringClass, Field f) { 043 this.declaringClass = declaringClass; 044 this.f = f; 045 } 046 047 /** 048 * Convenience method for instantiating a {@link FieldInfo}; 049 * 050 * @param declaringClass The class that declares this method. 051 * @param f The field being wrapped. 052 * @return A new {@link FieldInfo} object, or <jk>null</jk> if the field was null. 053 */ 054 public static FieldInfo of(ClassInfo declaringClass, Field f) { 055 if (f == null) 056 return null; 057 return new FieldInfo(declaringClass, f); 058 } 059 060 /** 061 * Convenience method for instantiating a {@link FieldInfo}; 062 * 063 * @param f The field being wrapped. 064 * @return A new {@link FieldInfo} object, or <jk>null</jk> if the field was null. 065 */ 066 public static FieldInfo of(Field f) { 067 if (f == null) 068 return null; 069 return new FieldInfo(null, f); 070 } 071 072 /** 073 * Returns the wrapped field. 074 * 075 * @return The wrapped field. 076 */ 077 public Field inner() { 078 return f; 079 } 080 081 /** 082 * Returns metadata about the declaring class. 083 * 084 * @return Metadata about the declaring class. 085 */ 086 public ClassInfo getDeclaringClass() { 087 if (declaringClass == null) 088 declaringClass = ClassInfo.of(f.getDeclaringClass()); 089 return declaringClass; 090 } 091 092 //----------------------------------------------------------------------------------------------------------------- 093 // Annotations 094 //----------------------------------------------------------------------------------------------------------------- 095 096 /** 097 * Returns the specified annotation on this field. 098 * 099 * @param a The annotation to look for. 100 * @return The annotation, or <jk>null</jk> if not found. 101 */ 102 public <T extends Annotation> T getAnnotation(Class<T> a) { 103 return getAnnotation(a, MetaProvider.DEFAULT); 104 } 105 106 /** 107 * Returns the specified annotation on this field. 108 * 109 * @param a The annotation to look for. 110 * @param mp The meta provider for looking up annotations on reflection objects (classes, methods, fields, constructors). 111 * @return The annotation, or <jk>null</jk> if not found. 112 */ 113 public <T extends Annotation> T getAnnotation(Class<T> a, MetaProvider mp) { 114 return last(mp.getAnnotations(a, f)); 115 } 116 117 /** 118 * Returns <jk>true</jk> if the specified annotation is present. 119 * 120 * @param a The annotation to check for. 121 * @return <jk>true</jk> if the specified annotation is present. 122 */ 123 public boolean hasAnnotation(Class<? extends Annotation> a) { 124 return f.isAnnotationPresent(a); 125 } 126 127 /** 128 * Returns <jk>true</jk> if the specified annotation is present. 129 * 130 * @param a The annotation to check for. 131 * @param mp The meta provider for looking up annotations on reflection objects (classes, methods, fields, constructors). 132 * @return <jk>true</jk> if the specified annotation is present. 133 */ 134 public boolean hasAnnotation(Class<? extends Annotation> a, MetaProvider mp) { 135 return ! mp.getAnnotations(a, f).isEmpty(); 136 } 137 138 //----------------------------------------------------------------------------------------------------------------- 139 // Characteristics 140 //----------------------------------------------------------------------------------------------------------------- 141 142 /** 143 * Returns <jk>true</jk> if all specified flags are applicable to this field. 144 * 145 * @param flags The flags to test for. 146 * @return <jk>true</jk> if all specified flags are applicable to this field. 147 */ 148 public boolean isAll(ReflectFlags...flags) { 149 for (ReflectFlags f : flags) { 150 switch (f) { 151 case DEPRECATED: 152 if (isNotDeprecated()) 153 return false; 154 break; 155 case NOT_DEPRECATED: 156 if (isDeprecated()) 157 return false; 158 break; 159 case PUBLIC: 160 if (isNotPublic()) 161 return false; 162 break; 163 case NOT_PUBLIC: 164 if (isPublic()) 165 return false; 166 break; 167 case STATIC: 168 if (isNotStatic()) 169 return false; 170 break; 171 case NOT_STATIC: 172 if (isStatic()) 173 return false; 174 break; 175 case TRANSIENT: 176 if (isNotTransient()) 177 return false; 178 break; 179 case NOT_TRANSIENT: 180 if (isTransient()) 181 return false; 182 break; 183 default: 184 throw new RuntimeException("Invalid flag for field: " + f); 185 } 186 } 187 return true; 188 } 189 190 /** 191 * Returns <jk>true</jk> if all specified flags are applicable to this field. 192 * 193 * @param flags The flags to test for. 194 * @return <jk>true</jk> if all specified flags are applicable to this field. 195 */ 196 public boolean isAny(ReflectFlags...flags) { 197 for (ReflectFlags f : flags) { 198 switch (f) { 199 case DEPRECATED: 200 if (isDeprecated()) 201 return true; 202 break; 203 case NOT_DEPRECATED: 204 if (isNotDeprecated()) 205 return true; 206 break; 207 case PUBLIC: 208 if (isPublic()) 209 return true; 210 break; 211 case NOT_PUBLIC: 212 if (isNotPublic()) 213 return true; 214 break; 215 case STATIC: 216 if (isStatic()) 217 return true; 218 break; 219 case NOT_STATIC: 220 if (isNotStatic()) 221 return true; 222 break; 223 case TRANSIENT: 224 if (isTransient()) 225 return true; 226 break; 227 case NOT_TRANSIENT: 228 if (isNotTransient()) 229 return true; 230 break; 231 default: 232 throw new RuntimeException("Invalid flag for field: " + f); 233 } 234 } 235 return false; 236 } 237 238 /** 239 * Returns <jk>true</jk> if this field has the {@link Deprecated @Deprecated} annotation on it. 240 * 241 * @return <jk>true</jk> if this field has the {@link Deprecated @Deprecated} annotation on it. 242 */ 243 public boolean isDeprecated() { 244 return f.isAnnotationPresent(Deprecated.class); 245 } 246 247 /** 248 * Returns <jk>true</jk> if this field doesn't have the {@link Deprecated @Deprecated} annotation on it. 249 * 250 * @return <jk>true</jk> if this field doesn't have the {@link Deprecated @Deprecated} annotation on it. 251 */ 252 public boolean isNotDeprecated() { 253 return ! f.isAnnotationPresent(Deprecated.class); 254 } 255 256 /** 257 * Returns <jk>true</jk> if this field is public. 258 * 259 * @return <jk>true</jk> if this field is public. 260 */ 261 public boolean isPublic() { 262 return Modifier.isPublic(f.getModifiers()); 263 } 264 265 /** 266 * Returns <jk>true</jk> if this field is not public. 267 * 268 * @return <jk>true</jk> if this field is not public. 269 */ 270 public boolean isNotPublic() { 271 return ! Modifier.isPublic(f.getModifiers()); 272 } 273 274 /** 275 * Returns <jk>true</jk> if this field is static. 276 * 277 * @return <jk>true</jk> if this field is static. 278 */ 279 public boolean isStatic() { 280 return Modifier.isStatic(f.getModifiers()); 281 } 282 283 /** 284 * Returns <jk>true</jk> if this field is not static. 285 * 286 * @return <jk>true</jk> if this field is not static. 287 */ 288 public boolean isNotStatic() { 289 return ! Modifier.isStatic(f.getModifiers()); 290 } 291 292 /** 293 * Returns <jk>true</jk> if this field is transient. 294 * 295 * @return <jk>true</jk> if this field is transient. 296 */ 297 public boolean isTransient() { 298 return Modifier.isTransient(f.getModifiers()); 299 } 300 301 /** 302 * Returns <jk>true</jk> if this field is not transient. 303 * 304 * @return <jk>true</jk> if this field is not transient. 305 */ 306 public boolean isNotTransient() { 307 return ! Modifier.isTransient(f.getModifiers()); 308 } 309 310 /** 311 * Returns <jk>true</jk> if the field has the specified name. 312 * 313 * @param name The name to compare against. 314 * @return <jk>true</jk> if the field has the specified name. 315 */ 316 public boolean hasName(String name) { 317 return f.getName().equals(name); 318 } 319 320 //----------------------------------------------------------------------------------------------------------------- 321 // Visibility 322 //----------------------------------------------------------------------------------------------------------------- 323 324 /** 325 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. 326 * 327 * @return This object (for method chaining). 328 */ 329 public FieldInfo accessible() { 330 setAccessible(); 331 return this; 332 } 333 334 /** 335 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. 336 * 337 * @return <jk>true</jk> if call was successful. 338 */ 339 public boolean setAccessible() { 340 try { 341 if (! (f.isAccessible())) 342 f.setAccessible(true); 343 return true; 344 } catch (SecurityException e) { 345 return false; 346 } 347 } 348 349 /** 350 * Identifies if the specified visibility matches this field. 351 * 352 * @param v The visibility to validate against. 353 * @return <jk>true</jk> if this visibility matches the modifier attribute of this field. 354 */ 355 public boolean isVisible(Visibility v) { 356 return v.isVisible(f); 357 } 358 359 //----------------------------------------------------------------------------------------------------------------- 360 // Other methods. 361 //----------------------------------------------------------------------------------------------------------------- 362 363 /** 364 * Returns the type of this field. 365 * 366 * @return The type of this field. 367 */ 368 public ClassInfo getType() { 369 if (type == null) 370 type = ClassInfo.of(f.getType()); 371 return type; 372 } 373 374 @Override 375 public String toString() { 376 return f.getDeclaringClass().getName() + "." + f.getName(); 377 } 378 379 @Override 380 public int compareTo(FieldInfo o) { 381 return getName().compareTo(o.getName()); 382 } 383 384 /** 385 * Returns the name of this field. 386 * 387 * @return The name of this field. 388 */ 389 public String getName() { 390 return f.getName(); 391 } 392 393 /** 394 * Invokes this field on the specified object. 395 * 396 * @param o The object containing the field. 397 * @return The field value. 398 * @throws IllegalAccessException Field was not accessible. 399 * @throws IllegalArgumentException Field does not belong to object. 400 */ 401 public Object invoke(Object o) throws IllegalArgumentException, IllegalAccessException { 402 return f.get(o); 403 } 404}