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}