001/* 002 * Copyright 2013 Chris Pheby 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.jadira.reflection.access.model; 017 018import java.lang.reflect.Field; 019import java.lang.reflect.Modifier; 020import java.util.concurrent.ConcurrentHashMap; 021 022import org.jadira.reflection.access.api.FieldAccess; 023import org.jadira.reflection.cloning.annotation.Transient; 024 025/** 026 * Provides a model resulting from introspection of a field of a class suitable for use with Java Reflection 027 */ 028public class FieldModel<C> { 029 030 private static final ConcurrentHashMap<String, FieldModel<?>> fieldModels = new ConcurrentHashMap<String, FieldModel<?>>(100); 031 032 private static final Object MONITOR = new Object(); 033 034 private final FieldAccess<C> fieldAccess; 035 036 private final Field field; 037 private final FieldType fieldType; 038 private final Class<?> fieldClass; 039 040 private final boolean transientField; 041 private final boolean transientAnnotatedField; 042 043 private final boolean isSynthetic; 044 045 private final boolean isPrivate; 046 047 private FieldModel(Field field, FieldAccess<C> fieldAccess) { 048 049 this.fieldAccess = fieldAccess; 050 051 this.isPrivate = Modifier.isPrivate(field.getModifiers()); 052 053 this.field = field; 054 if (field.getType().isPrimitive()) { 055 this.fieldType = FieldType.PRIMITIVE; 056 } else if (field.getType().isArray()) { 057 this.fieldType = FieldType.ARRAY; 058 } else { 059 this.fieldType = FieldType.OBJECT; 060 } 061 this.fieldClass = field.getType(); 062 063 this.transientField = Modifier.isTransient(field.getModifiers()); 064 this.transientAnnotatedField = field.getAnnotation(Transient.class) != null; 065 066 this.isSynthetic = field.isSynthetic(); 067 } 068 069 /** 070 * Returns a field model for the given Field and FieldAccess instance. If a FieldModel 071 * already exists, it will be reused. 072 * @param f The Field 073 * @param fieldAccess The Field Access that can be used to introspect the field 074 * @param <C> The type of class being accessed 075 * @return The Field Model 076 */ 077 @SuppressWarnings("unchecked") 078 public static final <C> FieldModel<C> get(Field f, FieldAccess<C> fieldAccess) { 079 080 String fieldModelKey = (fieldAccess.getClass().getSimpleName() + ":" + f.getClass().getName() + "#" + f.getName()); 081 FieldModel<C> fieldModel = (FieldModel<C>)fieldModels.get(fieldModelKey); 082 if (fieldModel != null) { 083 return fieldModel; 084 } 085 086 synchronized(MONITOR) { 087 fieldModel = (FieldModel<C>)fieldModels.get(fieldModelKey); 088 if (fieldModel != null) { 089 return fieldModel; 090 } else { 091 fieldModel = new FieldModel<C>(f, fieldAccess); 092 fieldModels.putIfAbsent(fieldModelKey, fieldModel); 093 094 return fieldModel; 095 } 096 } 097 } 098 099 /** 100 * Access the FieldAccess associated with the FieldModel 101 * @return The associated FieldAccess. 102 */ 103 public FieldAccess<C> getFieldAccess() { 104 return fieldAccess; 105 } 106 107 /** 108 * Access the Field associated with the FieldModel 109 * @return The associated Field 110 */ 111 public Field getField() { 112 return field; 113 } 114 115 /** 116 * Indicates the type of the Field - Primitive, Array or Object 117 * @return The FieldType 118 */ 119 public FieldType getFieldType() { 120 return fieldType; 121 } 122 123 /** 124 * Gets the Declared Class for the Field 125 * @return The Class for the Field 126 */ 127 public Class<?> getFieldClass() { 128 return fieldClass; 129 } 130 131 /** 132 * Indicates whether the field is transient 133 * @return True if transient 134 */ 135 public boolean isTransientField() { 136 return transientField; 137 } 138 139 /** 140 * Indicates whether the field carries a Transient annotation 141 * @return True if transient annotated 142 */ 143 public boolean isTransientAnnotatedField() { 144 return transientAnnotatedField; 145 } 146 147 /** 148 * Indicates whether the field is synthetic according to the Java Language Specific 149 * @return True if synthetic 150 */ 151 public boolean isSynthetic() { 152 return isSynthetic; 153 } 154 155 /** 156 * Indicates whether the field is private access 157 * @return True if private 158 */ 159 public boolean isPrivate() { 160 return isPrivate; 161 } 162}