    001// Copyright 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.internal.services;
    016
    017import org.apache.tapestry5.beaneditor.BeanModel;
    018import org.apache.tapestry5.beaneditor.NonVisual;
    019import org.apache.tapestry5.beaneditor.ReorderProperties;
    020import org.apache.tapestry5.internal.beaneditor.BeanModelImpl;
    021import org.apache.tapestry5.internal.beaneditor.BeanModelUtils;
    022import org.apache.tapestry5.ioc.Location;
    023import org.apache.tapestry5.ioc.Messages;
    024import org.apache.tapestry5.ioc.ObjectLocator;
    025import org.apache.tapestry5.ioc.annotations.Primary;
    026import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    027import org.apache.tapestry5.ioc.services.*;
    028import org.apache.tapestry5.services.BeanModelSource;
    029import org.apache.tapestry5.services.ComponentLayer;
    030import org.apache.tapestry5.services.DataTypeAnalyzer;
    031import org.apache.tapestry5.services.PropertyConduitSource;
    032
    033import java.lang.reflect.Method;
    034import java.lang.reflect.Modifier;
    035import java.util.Collections;
    036import java.util.List;
    037
    038public class BeanModelSourceImpl implements BeanModelSource
    039{
    040    private final TypeCoercer typeCoercer;
    041
    042    private final PropertyAccess propertyAccess;
    043
    044    private final PropertyConduitSource propertyConduitSource;
    045
    046    private final PlasticProxyFactory proxyFactory;
    047
    048    private final DataTypeAnalyzer dataTypeAnalyzer;
    049
    050    private final ObjectLocator locator;
    051
    052    private static class PropertyOrder implements Comparable<PropertyOrder>
    053    {
    054        final String propertyName;
    055
    056        final int classDepth;
    057
    058        final int sortKey;
    059
    060        public PropertyOrder(final String propertyName, int classDepth, int sortKey)
    061        {
    062            this.propertyName = propertyName;
    063            this.classDepth = classDepth;
    064            this.sortKey = sortKey;
    065        }
    066
    067        public int compareTo(PropertyOrder o)
    068        {
    069            int result = classDepth - o.classDepth;
    070
    071            if (result == 0)
    072                result = sortKey - o.sortKey;
    073
    074            if (result == 0)
    075                result = propertyName.compareTo(o.propertyName);
    076
    077            return result;
    078        }
    079    }
    080
    081    /**
    082     * @param classAdapter  defines the bean that contains the properties
    083     * @param propertyNames the initial set of property names, which will be rebuilt in the correct order
    084     */
    085    private void orderProperties(ClassPropertyAdapter classAdapter, List<String> propertyNames)
    086    {
    087        List<PropertyOrder> properties = CollectionFactory.newList();
    088
    089        for (String name : propertyNames)
    090        {
    091            PropertyAdapter pa = classAdapter.getPropertyAdapter(name);
    092
    093            Method readMethod = pa.getReadMethod();
    094
    095            Location location = readMethod == null ? null : proxyFactory.getMethodLocation(readMethod);
    096
    097            int line = location == null ? -1 : location.getLine();
    098
    099            properties.add(new PropertyOrder(name, computeDepth(pa), line));
    100        }
    101
    102        Collections.sort(properties);
    103
    104        propertyNames.clear();
    105
    106        for (PropertyOrder po : properties)
    107        {
    108            propertyNames.add(po.propertyName);
    109        }
    110    }
    111
    112    private static int computeDepth(PropertyAdapter pa)
    113    {
    114        int depth = 0;
    115        Class c = pa.getDeclaringClass();
    116
    117        // When the method originates in an interface, the parent may be null, not Object.
    118
    119        while (c != null && c != Object.class)
    120        {
    121            depth++;
    122            c = c.getSuperclass();
    123        }
    124
    125        return depth;
    126    }
    127
    128    public BeanModelSourceImpl(TypeCoercer typeCoercer, PropertyAccess propertyAccess,
    129                               PropertyConduitSource propertyConduitSource,
    130                               @ComponentLayer
    131                               PlasticProxyFactory proxyFactory,
    132                               @Primary
    133                               DataTypeAnalyzer dataTypeAnalyzer, ObjectLocator locator)
    134    {
    135        this.typeCoercer = typeCoercer;
    136        this.propertyAccess = propertyAccess;
    137        this.propertyConduitSource = propertyConduitSource;
    138        this.proxyFactory = proxyFactory;
    139        this.dataTypeAnalyzer = dataTypeAnalyzer;
    140        this.locator = locator;
    141    }
    142
    143    public <T> BeanModel<T> createDisplayModel(Class<T> beanClass, Messages messages)
    144    {
    145        return create(beanClass, false, messages);
    146    }
    147
    148    public <T> BeanModel<T> createEditModel(Class<T> beanClass, Messages messages)
    149    {
    150        return create(beanClass, true, messages);
    151    }
    152
    153    public <T> BeanModel<T> create(Class<T> beanClass, boolean filterReadOnlyProperties, Messages messages)
    154    {
    155        assert beanClass != null;
    156        assert messages != null;
    157        ClassPropertyAdapter adapter = propertyAccess.getAdapter(beanClass);
    158
    159        BeanModel<T> model = new BeanModelImpl<T>(beanClass, propertyConduitSource, typeCoercer, messages, locator);
    160
    161        for (final String propertyName : adapter.getPropertyNames())
    162        {
    163            PropertyAdapter pa = adapter.getPropertyAdapter(propertyName);
    164
    165            if (!pa.isRead())
    166            {
    167                continue;
    168            }
    169
    170            if (isStaticFieldProperty(pa))
    171            {
    172                continue;
    173            }
    174
    175            if (pa.getAnnotation(NonVisual.class) != null)
    176            {
    177                continue;
    178            }
    179
    180            if (filterReadOnlyProperties && !pa.isUpdate())
    181            {
    182                continue;
    183            }
    184
    185            final String dataType = dataTypeAnalyzer.identifyDataType(pa);
    186
    187            // If an unregistered type, then ignore the property.
    188
    189            if (dataType == null)
    190            {
    191                continue;
    192            }
    193
    194            model.add(propertyName).dataType(dataType);
    195        }
    196
    197        // First, order the properties based on the location of the getter method
    198        // within the class.
    199
    200        List<String> propertyNames = model.getPropertyNames();
    201
    202        orderProperties(adapter, propertyNames);
    203
    204        model.reorder(propertyNames.toArray(new String[propertyNames.size()]));
    205
    206        // Next, check for an annotation with specific ordering information.
    207
    208        ReorderProperties reorderAnnotation = beanClass.getAnnotation(ReorderProperties.class);
    209
    210        if (reorderAnnotation != null)
    211        {
    212            BeanModelUtils.reorder(model, reorderAnnotation.value());
    213        }
    214
    215        return model;
    216    }
    217
    218    private boolean isStaticFieldProperty(PropertyAdapter adapter)
    219    {
    220        return adapter.isField() && Modifier.isStatic(adapter.getField().getModifiers());
    221    }
    222}