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