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}