001// Copyright 2006, 2007, 2008, 2010, 2011, 2012 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
015package org.apache.tapestry5.ioc.internal.services;
016
017import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
018
019import java.beans.PropertyDescriptor;
020import java.lang.annotation.Annotation;
021import java.lang.reflect.Field;
022import java.lang.reflect.Method;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.tapestry5.ioc.internal.util.GenericsUtils;
027import org.apache.tapestry5.ioc.internal.util.InternalUtils;
028import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
029import org.apache.tapestry5.ioc.services.PropertyAdapter;
030
031public class ClassPropertyAdapterImpl implements ClassPropertyAdapter
032{
033    private final Map<String, PropertyAdapter> adapters = newCaseInsensitiveMap();
034
035    private final Class beanType;
036
037    public ClassPropertyAdapterImpl(Class beanType, List<PropertyDescriptor> descriptors)
038    {
039        this.beanType = beanType;
040
041        for (PropertyDescriptor pd : descriptors)
042        {
043            // Indexed properties will have a null propertyType (and a non-null
044            // indexedPropertyType). We ignore indexed properties.
045
046            if (pd.getPropertyType() == null)
047                continue;
048
049            Method readMethod = pd.getReadMethod();
050
051            Class propertyType = readMethod == null ? pd.getPropertyType() : GenericsUtils.extractGenericReturnType(
052                    beanType, readMethod);
053
054            PropertyAdapter pa = new PropertyAdapterImpl(this, pd.getName(), propertyType, readMethod, pd
055                    .getWriteMethod());
056
057            adapters.put(pa.getName(), pa);
058        }
059
060        // Now, add any public fields (even if static) that do not conflict
061
062        for (Field f : beanType.getFields())
063        {
064            String name = f.getName();
065
066            if (!adapters.containsKey(name))
067            {
068                Class propertyType = GenericsUtils.extractGenericFieldType(beanType, f);
069                PropertyAdapter pa = new PropertyAdapterImpl(this, name, propertyType, f);
070
071                adapters.put(name, pa);
072            }
073        }
074    }
075
076    public Class getBeanType()
077    {
078        return beanType;
079    }
080
081    @Override
082    public String toString()
083    {
084        String names = InternalUtils.joinSorted(adapters.keySet());
085
086        return String.format("<ClassPropertyAdaptor %s: %s>", beanType.getName(), names);
087    }
088
089    public List<String> getPropertyNames()
090    {
091        return InternalUtils.sortedKeys(adapters);
092    }
093
094    public PropertyAdapter getPropertyAdapter(String name)
095    {
096        return adapters.get(name);
097    }
098
099    public Object get(Object instance, String propertyName)
100    {
101        return adaptorFor(propertyName).get(instance);
102    }
103
104    public void set(Object instance, String propertyName, Object value)
105    {
106        adaptorFor(propertyName).set(instance, value);
107    }
108
109    public Annotation getAnnotation(Object instance, String propertyName, Class<? extends Annotation> annotationClass) {
110    return adaptorFor(propertyName).getAnnotation(annotationClass);
111    }
112
113    private PropertyAdapter adaptorFor(String name)
114    {
115        PropertyAdapter pa = adapters.get(name);
116
117        if (pa == null)
118            throw new IllegalArgumentException(ServiceMessages.noSuchProperty(beanType, name));
119
120        return pa;
121    }
122
123}