    001    // Copyright 2006, 2008, 2010 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // 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
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.ioc.internal.services;
    016
    017    import org.apache.tapestry5.ioc.AnnotationProvider;
    018    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    019    import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
    020    import org.apache.tapestry5.ioc.services.PropertyAdapter;
    021
    022    import java.lang.annotation.Annotation;
    023    import java.lang.reflect.*;
    024    import java.util.List;
    025
    026    public class PropertyAdapterImpl implements PropertyAdapter
    027    {
    028        private final ClassPropertyAdapter classAdapter;
    029
    030        private final String name;
    031
    032        private final Method readMethod;
    033
    034        private final Method writeMethod;
    035
    036        private final Class type;
    037
    038        private final boolean castRequired;
    039
    040        // Synchronized by this; lazily initialized
    041        private AnnotationProvider annotationProvider;
    042
    043        private final Field field;
    044
    045        private final Class declaringClass;
    046
    047        PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Method readMethod,
    048                            Method writeMethod)
    049        {
    050            this.classAdapter = classAdapter;
    051            this.name = name;
    052            this.type = type;
    053
    054            this.readMethod = readMethod;
    055            this.writeMethod = writeMethod;
    056
    057            declaringClass = readMethod != null ? readMethod.getDeclaringClass() : writeMethod.getDeclaringClass();
    058
    059            castRequired = readMethod != null && readMethod.getReturnType() != type;
    060
    061            field = null;
    062        }
    063
    064        PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Field field)
    065        {
    066            this.classAdapter = classAdapter;
    067            this.name = name;
    068            this.type = type;
    069
    070            this.field = field;
    071
    072            declaringClass = field.getDeclaringClass();
    073
    074            castRequired = field.getType() != type;
    075
    076            readMethod = null;
    077            writeMethod = null;
    078        }
    079
    080        public String getName()
    081        {
    082            return name;
    083        }
    084
    085        public Method getReadMethod()
    086        {
    087            return readMethod;
    088        }
    089
    090        public Class getType()
    091        {
    092            return type;
    093        }
    094
    095        public Method getWriteMethod()
    096        {
    097            return writeMethod;
    098        }
    099
    100        public boolean isRead()
    101        {
    102            return field != null || readMethod != null;
    103        }
    104
    105        public boolean isUpdate()
    106        {
    107            return writeMethod != null || (field != null && !isFinal(field));
    108        }
    109
    110        private boolean isFinal(Member member)
    111        {
    112            return Modifier.isFinal(member.getModifiers());
    113        }
    114
    115        public Object get(Object instance)
    116        {
    117            if (field == null && readMethod == null)
    118                throw new UnsupportedOperationException(ServiceMessages.readNotSupported(instance, name));
    119
    120            Throwable fail;
    121
    122            try
    123            {
    124                if (field == null)
    125                    return readMethod.invoke(instance);
    126                else
    127                    return field.get(instance);
    128            } catch (InvocationTargetException ex)
    129            {
    130                fail = ex.getTargetException();
    131            } catch (Exception ex)
    132            {
    133                fail = ex;
    134            }
    135
    136            throw new RuntimeException(ServiceMessages.readFailure(name, instance, fail), fail);
    137        }
    138
    139        public void set(Object instance, Object value)
    140        {
    141            if (field == null && writeMethod == null)
    142                throw new UnsupportedOperationException(ServiceMessages.writeNotSupported(instance, name));
    143
    144            Throwable fail;
    145
    146            try
    147            {
    148                if (field == null)
    149                    writeMethod.invoke(instance, value);
    150                else
    151                    field.set(instance, value);
    152
    153                return;
    154            } catch (InvocationTargetException ex)
    155            {
    156                fail = ex.getTargetException();
    157            } catch (Exception ex)
    158            {
    159                fail = ex;
    160            }
    161
    162            throw new RuntimeException(ServiceMessages.writeFailure(name, instance, fail), fail);
    163        }
    164
    165        public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    166        {
    167            return getAnnnotationProvider().getAnnotation(annotationClass);
    168        }
    169
    170        /**
    171         * Creates (as needed) the annotation provider for this property.
    172         */
    173        private synchronized AnnotationProvider getAnnnotationProvider()
    174        {
    175            if (annotationProvider == null)
    176            {
    177                List<AnnotationProvider> providers = CollectionFactory.newList();
    178
    179                if (readMethod != null)
    180                    providers.add(new AccessableObjectAnnotationProvider(readMethod));
    181
    182                if (writeMethod != null)
    183                    providers.add(new AccessableObjectAnnotationProvider(writeMethod));
    184
    185                // There's an assumption here, that the fields match the property name (we ignore case
    186                // which leads to a manageable ambiguity) and that the field and the getter/setter
    187                // are in the same class (i.e., that we don't have a getter exposing a protected field inherted
    188                // from a base class, or some other oddity).
    189
    190                Class cursor = getBeanType();
    191
    192                out:
    193                while (cursor != null)
    194                {
    195                    for (Field f : cursor.getDeclaredFields())
    196                    {
    197                        if (f.getName().equalsIgnoreCase(name))
    198                        {
    199                            providers.add(new AccessableObjectAnnotationProvider(f));
    200
    201                            break out;
    202                        }
    203                    }
    204
    205                    cursor = cursor.getSuperclass();
    206                }
    207
    208                annotationProvider = AnnotationProviderChain.create(providers);
    209            }
    210
    211            return annotationProvider;
    212        }
    213
    214        public boolean isCastRequired()
    215        {
    216            return castRequired;
    217        }
    218
    219        public ClassPropertyAdapter getClassAdapter()
    220        {
    221            return classAdapter;
    222        }
    223
    224        public Class getBeanType()
    225        {
    226            return classAdapter.getBeanType();
    227        }
    228
    229        public boolean isField()
    230        {
    231            return field != null;
    232        }
    233
    234        public Field getField()
    235        {
    236            return field;
    237        }
    238
    239        public Class getDeclaringClass()
    240        {
    241            return declaringClass;
    242        }
    243    }