| // Copyright 2007, 2008, 2010 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry5.internal.services; |
| |
| import java.lang.reflect.Method; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.apache.tapestry5.beaneditor.BeanModel; |
| import org.apache.tapestry5.beaneditor.NonVisual; |
| import org.apache.tapestry5.beaneditor.ReorderProperties; |
| import org.apache.tapestry5.internal.beaneditor.BeanModelImpl; |
| import org.apache.tapestry5.internal.beaneditor.BeanModelUtils; |
| import org.apache.tapestry5.ioc.Location; |
| import org.apache.tapestry5.ioc.Messages; |
| import org.apache.tapestry5.ioc.ObjectLocator; |
| import org.apache.tapestry5.ioc.annotations.Primary; |
| import org.apache.tapestry5.ioc.internal.util.CollectionFactory; |
| import org.apache.tapestry5.ioc.services.ClassFactory; |
| import org.apache.tapestry5.ioc.services.ClassPropertyAdapter; |
| import org.apache.tapestry5.ioc.services.PropertyAccess; |
| import org.apache.tapestry5.ioc.services.PropertyAdapter; |
| import org.apache.tapestry5.ioc.services.TypeCoercer; |
| import org.apache.tapestry5.services.BeanModelSource; |
| import org.apache.tapestry5.services.ComponentLayer; |
| import org.apache.tapestry5.services.DataTypeAnalyzer; |
| import org.apache.tapestry5.services.PropertyConduitSource; |
| |
| public class BeanModelSourceImpl implements BeanModelSource |
| { |
| private final TypeCoercer typeCoercer; |
| |
| private final PropertyAccess propertyAccess; |
| |
| private final PropertyConduitSource propertyConduitSource; |
| |
| private final ClassFactory classFactory; |
| |
| private final DataTypeAnalyzer dataTypeAnalyzer; |
| |
| private final ObjectLocator locator; |
| |
| private static class PropertyOrder implements Comparable<PropertyOrder> |
| { |
| final String propertyName; |
| |
| final int classDepth; |
| |
| final int sortKey; |
| |
| public PropertyOrder(final String propertyName, int classDepth, int sortKey) |
| { |
| this.propertyName = propertyName; |
| this.classDepth = classDepth; |
| this.sortKey = sortKey; |
| } |
| |
| public int compareTo(PropertyOrder o) |
| { |
| int result = classDepth - o.classDepth; |
| |
| if (result == 0) |
| result = sortKey - o.sortKey; |
| |
| if (result == 0) |
| result = propertyName.compareTo(o.propertyName); |
| |
| return result; |
| } |
| } |
| |
| /** |
| * @param classAdapter |
| * defines the bean that contains the properties |
| * @param propertyNames |
| * the initial set of property names, which will be rebuilt in the correct order |
| */ |
| private void orderProperties(ClassPropertyAdapter classAdapter, List<String> propertyNames) |
| { |
| List<PropertyOrder> properties = CollectionFactory.newList(); |
| |
| for (String name : propertyNames) |
| { |
| PropertyAdapter pa = classAdapter.getPropertyAdapter(name); |
| |
| Method readMethod = pa.getReadMethod(); |
| |
| Location location = readMethod == null ? null : classFactory.getMethodLocation(readMethod); |
| |
| int line = location == null ? -1 : location.getLine(); |
| |
| properties.add(new PropertyOrder(name, computeDepth(pa), line)); |
| } |
| |
| Collections.sort(properties); |
| |
| propertyNames.clear(); |
| |
| for (PropertyOrder po : properties) |
| { |
| propertyNames.add(po.propertyName); |
| } |
| } |
| |
| private static int computeDepth(PropertyAdapter pa) |
| { |
| int depth = 0; |
| Class c = pa.getDeclaringClass(); |
| |
| // When the method originates in an interface, the parent may be null, not Object. |
| |
| while (c != null && c != Object.class) |
| { |
| depth++; |
| c = c.getSuperclass(); |
| } |
| |
| return depth; |
| } |
| |
| public BeanModelSourceImpl(TypeCoercer typeCoercer, PropertyAccess propertyAccess, |
| PropertyConduitSource propertyConduitSource, @ComponentLayer |
| ClassFactory classFactory, @Primary |
| DataTypeAnalyzer dataTypeAnalyzer, ObjectLocator locator) |
| { |
| this.typeCoercer = typeCoercer; |
| this.propertyAccess = propertyAccess; |
| this.propertyConduitSource = propertyConduitSource; |
| this.classFactory = classFactory; |
| this.dataTypeAnalyzer = dataTypeAnalyzer; |
| this.locator = locator; |
| } |
| |
| public <T> BeanModel<T> createDisplayModel(Class<T> beanClass, Messages messages) |
| { |
| return create(beanClass, false, messages); |
| } |
| |
| public <T> BeanModel<T> createEditModel(Class<T> beanClass, Messages messages) |
| { |
| return create(beanClass, true, messages); |
| } |
| |
| public <T> BeanModel<T> create(Class<T> beanClass, boolean filterReadOnlyProperties, Messages messages) |
| { |
| assert beanClass != null; |
| assert messages != null; |
| ClassPropertyAdapter adapter = propertyAccess.getAdapter(beanClass); |
| |
| BeanModel<T> model = new BeanModelImpl<T>(beanClass, propertyConduitSource, typeCoercer, messages, locator); |
| |
| for (final String propertyName : adapter.getPropertyNames()) |
| { |
| PropertyAdapter pa = adapter.getPropertyAdapter(propertyName); |
| |
| if (!pa.isRead()) |
| continue; |
| |
| if (pa.getAnnotation(NonVisual.class) != null) |
| continue; |
| |
| if (filterReadOnlyProperties && !pa.isUpdate()) |
| continue; |
| |
| final String dataType = dataTypeAnalyzer.identifyDataType(pa); |
| |
| // If an unregistered type, then ignore the property. |
| |
| if (dataType == null) |
| continue; |
| |
| model.add(propertyName).dataType(dataType); |
| } |
| |
| // First, order the properties based on the location of the getter method |
| // within the class. |
| |
| List<String> propertyNames = model.getPropertyNames(); |
| |
| orderProperties(adapter, propertyNames); |
| |
| model.reorder(propertyNames.toArray(new String[propertyNames.size()])); |
| |
| // Next, check for an annotation with specific ordering information. |
| |
| ReorderProperties reorderAnnotation = beanClass.getAnnotation(ReorderProperties.class); |
| |
| if (reorderAnnotation != null) |
| { |
| BeanModelUtils.reorder(model, reorderAnnotation.value()); |
| } |
| |
| return model; |
| } |
| } |