| package org.apache.tapestry5.genericsresolverguava.internal; |
| //Copyright 2007, 2008, 2009, 2010, 2011 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. |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| |
| import org.apache.tapestry5.beaneditor.RelativePosition; |
| import org.apache.tapestry5.beaneditor.Sortable; |
| import org.apache.tapestry5.beanmodel.BeanModel; |
| import org.apache.tapestry5.beanmodel.PropertyConduit; |
| import org.apache.tapestry5.beanmodel.PropertyModel; |
| import org.apache.tapestry5.beanmodel.internal.services.PropertyAccessImpl; |
| import org.apache.tapestry5.beanmodel.services.BeanModelSource; |
| import org.apache.tapestry5.commons.Messages; |
| import org.apache.tapestry5.commons.util.UnknownValueException; |
| import org.apache.tapestry5.ioc.test.IOCTestCase; |
| import org.easymock.EasyMock; |
| import org.testng.annotations.BeforeClass; |
| import org.testng.annotations.Test; |
| |
| /** |
| * Copied from tapestry-core's tests due to the lack of a better option. |
| * Tests for the bean editor model source itself, as well as the model classes. |
| */ |
| public abstract class AbstractBeanModelSourceImplTest extends IOCTestCase |
| { |
| private BeanModelSource source; |
| |
| protected abstract BeanModelSource create(); |
| |
| @BeforeClass |
| public void setup() |
| { |
| source = create(); |
| } |
| |
| /** |
| * Tests defaults for property names, labels and conduits. |
| */ |
| @Test |
| public void default_model_for_bean() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertSame(model.getBeanType(), SimpleBean.class); |
| |
| // Based on order of the getter methods (no longer alphabetical) |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age")); |
| |
| assertEquals(model.toString(), |
| "BeanModel[org.apache.tapestry5.genericsresolverguava.internal.SimpleBean properties:firstName, lastName, age]"); |
| |
| PropertyModel age = model.get("age"); |
| |
| assertEquals(age.getLabel(), "Age"); |
| assertSame(age.getPropertyType(), int.class); |
| assertEquals(age.getDataType(), "number"); |
| |
| PropertyModel firstName = model.get("firstName"); |
| |
| assertEquals(firstName.getLabel(), "First Name"); |
| assertEquals(firstName.getPropertyType(), String.class); |
| assertEquals(firstName.getDataType(), "text"); |
| |
| assertEquals(model.get("lastName").getLabel(), "Last Name"); |
| |
| PropertyConduit conduit = model.get("lastName").getConduit(); |
| |
| SimpleBean instance = new SimpleBean(); |
| |
| instance.setLastName("Lewis Ship"); |
| |
| assertEquals(conduit.get(instance), "Lewis Ship"); |
| |
| conduit.set(instance, "TapestryDude"); |
| |
| assertEquals(instance.getLastName(), "TapestryDude"); |
| |
| // Now, one with some type coercion. |
| |
| age.getConduit().set(instance, "40"); |
| |
| assertEquals(instance.getAge(), 40); |
| |
| verify(); |
| } |
| |
| @Test |
| public void include_properties() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertSame(model.getBeanType(), SimpleBean.class); |
| |
| model.include("lastname", "firstname"); |
| |
| // Based on order of the getter methods (no longer alphabetical) |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "firstName")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void add_before() |
| { |
| Messages messages = mockMessages(); |
| PropertyConduit conduit = mockPropertyConduit(); |
| |
| Class propertyType = String.class; |
| |
| stub_contains(messages, false); |
| |
| expect(conduit.getPropertyType()).andReturn(propertyType).atLeastOnce(); |
| expect(conduit.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age")); |
| |
| // Note the use of case insensitivity here. |
| |
| PropertyModel property = model.add(RelativePosition.BEFORE, "lastname", "middleInitial", conduit); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "middleInitial", "lastName", "age")); |
| |
| assertEquals(property.getPropertyName(), "middleInitial"); |
| assertSame(property.getConduit(), conduit); |
| assertSame(property.getPropertyType(), propertyType); |
| |
| verify(); |
| } |
| |
| /** |
| * TAPESTRY-2202 |
| */ |
| @Test |
| public void new_instance() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel<SimpleBean> model = source.create(SimpleBean.class, true, messages); |
| |
| SimpleBean s1 = model.newInstance(); |
| |
| assertNotNull(s1); |
| |
| SimpleBean s2 = model.newInstance(); |
| |
| assertNotNull(s2); |
| assertNotSame(s1, s2); |
| |
| verify(); |
| } |
| |
| @Test |
| public void add_before_using_default_conduit() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| model.exclude("firstname"); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "age")); |
| |
| // Note the use of case insensitivity here. |
| |
| PropertyModel property = model.add(RelativePosition.BEFORE, "lastname", "firstName"); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age")); |
| |
| assertEquals(property.getPropertyName(), "firstName"); |
| assertSame(property.getPropertyType(), String.class); |
| |
| verify(); |
| } |
| |
| @Test |
| public void add_after() |
| { |
| Messages messages = mockMessages(); |
| PropertyConduit conduit = mockPropertyConduit(); |
| |
| Class propertyType = String.class; |
| |
| stub_contains(messages, false); |
| |
| expect(conduit.getPropertyType()).andReturn(propertyType).atLeastOnce(); |
| |
| expect(conduit.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age")); |
| |
| PropertyModel property = model.add(RelativePosition.AFTER, "firstname", "middleInitial", conduit); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "middleInitial", "lastName", "age")); |
| |
| assertEquals(property.getPropertyName(), "middleInitial"); |
| assertSame(property.getConduit(), conduit); |
| assertSame(property.getPropertyType(), propertyType); |
| |
| verify(); |
| } |
| |
| @Test |
| public void filtering_out_read_only_properties() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(ReadOnlyBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("value")); |
| |
| model = source.create(ReadOnlyBean.class, false, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("value", "readOnly")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void non_text_property() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(EnumBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("token")); |
| |
| assertEquals(model.get("token").getDataType(), "enum"); |
| |
| verify(); |
| } |
| |
| @Test |
| public void add_duplicate_property_name_is_failure() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| try |
| { |
| model.add("age"); |
| unreachable(); |
| } catch (RuntimeException ex) |
| { |
| assertEquals( |
| ex.getMessage(), |
| "Bean editor model for org.apache.tapestry5.genericsresolverguava.internal.SimpleBean already contains a property model for property \'age\'."); |
| } |
| |
| verify(); |
| } |
| |
| @Test |
| public void unknown_property_name() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| try |
| { |
| model.get("frobozz"); |
| unreachable(); |
| } catch (UnknownValueException ex) |
| { |
| assertEquals( |
| ex.getMessage(), |
| "Bean editor model for org.apache.tapestry5.genericsresolverguava.internal.SimpleBean does not contain a property named \'frobozz\'."); |
| |
| assertListsEquals(ex.getAvailableValues().getValues(), "age", "firstName", "lastName"); |
| } |
| |
| verify(); |
| } |
| |
| @Test |
| public void unknown_property_id() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| model.addEmpty("shrub.foo()"); |
| |
| try |
| { |
| model.getById("frobozz"); |
| unreachable(); |
| } catch (UnknownValueException ex) |
| { |
| assertEquals( |
| ex.getMessage(), |
| "Bean editor model for org.apache.tapestry5.genericsresolverguava.internal.SimpleBean does not contain a property with id \'frobozz\'."); |
| |
| assertListsEquals(ex.getAvailableValues().getValues(), "age", "firstName", "lastName", "shrubfoo"); |
| } |
| |
| verify(); |
| } |
| |
| @Test |
| public void get_added_property_by_name() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| PropertyModel pm = model.addEmpty("shrub.foo()"); |
| |
| assertSame(model.get("Shrub.Foo()"), pm); |
| |
| verify(); |
| } |
| |
| @Test |
| public void get_added_property_by_id() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| PropertyModel pm = model.addEmpty("shrub.foo()"); |
| |
| assertSame(model.getById("ShrubFoo"), pm); |
| |
| verify(); |
| |
| } |
| |
| @Test |
| public void order_via_annotation() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(StoogeBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("larry", "moe", "shemp", "curly")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void edit_property_label() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages).get("age").label("Decrepitude").model(); |
| |
| assertEquals(model.get("age").getLabel(), "Decrepitude"); |
| |
| verify(); |
| } |
| |
| @Test |
| public void label_from_component_messages() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| train_contains(messages, "age-label", true); |
| train_get(messages, "age-label", "Decrepitude"); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertEquals(model.get("age").getLabel(), "Decrepitude"); |
| |
| verify(); |
| } |
| |
| @Test |
| public void array_type_bean() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(StringArrayBean.class, true, messages); |
| |
| // There's not editor for string arrays yet, so it won't show up normally. |
| |
| PropertyModel propertyModel = model.add("array"); |
| |
| assertSame(propertyModel.getPropertyType(), String[].class); |
| |
| String[] value = |
| {"foo", "bar"}; |
| |
| StringArrayBean bean = new StringArrayBean(); |
| |
| PropertyConduit conduit = propertyModel.getConduit(); |
| |
| conduit.set(bean, value); |
| |
| assertSame(bean.getArray(), value); |
| |
| assertSame(conduit.get(bean), value); |
| |
| verify(); |
| } |
| |
| @Test |
| public void composite_bean() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| train_contains(messages, "simpleage-label", true); |
| train_get(messages, "simpleage-label", "Years of Age"); |
| |
| replay(); |
| |
| BeanModel model = source.create(CompositeBean.class, true, messages); |
| |
| // No editor for CompositeBean, so this will be empty. |
| |
| assertEquals(model.getPropertyNames(), Collections.emptyList()); |
| |
| // There's not editor for string arrays yet, so it won't show up normally. |
| |
| PropertyModel firstName = model.add("simple.firstName"); |
| |
| assertEquals(firstName.getLabel(), "First Name"); |
| |
| PropertyModel age = model.add("simple.age"); |
| assertEquals(age.getLabel(), "Years of Age"); |
| |
| CompositeBean bean = new CompositeBean(); |
| |
| firstName.getConduit().set(bean, "Fred"); |
| age.getConduit().set(bean, "97"); |
| |
| assertEquals(bean.getSimple().getFirstName(), "Fred"); |
| assertEquals(bean.getSimple().getAge(), 97); |
| |
| bean.getSimple().setAge(24); |
| |
| assertEquals(age.getConduit().get(bean), new Integer(24)); |
| |
| verify(); |
| } |
| |
| @Test |
| public void default_properties_exclude_write_only() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(WriteOnlyBean.class, false, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("readOnly", "readWrite")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void add_synthetic_property() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| PropertyModel property = model.addEmpty("placeholder"); |
| |
| assertFalse(property.isSortable()); |
| assertSame(property.getPropertyType(), Object.class); |
| assertEquals(property.getLabel(), "Placeholder"); |
| |
| verify(); |
| } |
| |
| @Test |
| public void add_missing_property_is_failure() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| try |
| { |
| model.add("doesNotExist"); |
| unreachable(); |
| } catch (Exception ex) |
| { |
| assertMessageContains(ex, "does not contain", "doesNotExist"); |
| } |
| |
| verify(); |
| } |
| |
| @Test |
| public void exclude_property() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertSame(model.exclude("age"), model); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void exclude_unknown_property_is_noop() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertSame(model.exclude("frobozz"), model); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void nonvisual_properties_are_excluded() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(NonVisualBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("name")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void reorder() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(SimpleBean.class, true, messages); |
| |
| assertSame(model.getBeanType(), SimpleBean.class); |
| |
| // Based on order of the getter methods (no longer alphabetical) |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age")); |
| |
| // Testing a couple of things here: |
| // 1) case insensitive |
| // 2) unreferenced property names added to the end. |
| |
| model.reorder("lastname", "AGE"); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "age", "firstName")); |
| |
| verify(); |
| } |
| |
| @Test |
| public void reoder_from_annotation() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel model = source.create(PropertyOrderBean.class, true, messages); |
| |
| assertEquals(model.getPropertyNames(), Arrays.asList("third", "first", "second")); |
| |
| verify(); |
| } |
| |
| // https://issues.apache.org/jira/browse/TAP5-1798 |
| @Test |
| public void static_fields_are_ignored() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel<BeanWithStaticField> model = source.createDisplayModel(BeanWithStaticField.class, messages); |
| |
| assertListsEquals(model.getPropertyNames(), "name"); |
| |
| verify(); |
| } |
| |
| // https://issues.apache.org/jira/browse/TAP5-2305 |
| @Test |
| public void sortable_annotation() |
| { |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel<SortableBean> model = source.createDisplayModel(SortableBean.class, messages); |
| model.add("nonSortableByDefault"); |
| model.add("sortable"); |
| |
| // checking whether non-@Sortable annotated properties still behave in the old ways |
| assertTrue(model.get("sortableByDefault").isSortable()); |
| assertFalse(model.get("nonSortableByDefault").isSortable()); |
| |
| // checking @Sortable itself |
| assertFalse(model.get("nonSortable").isSortable()); |
| assertTrue(model.get("sortable").isSortable()); |
| |
| verify(); |
| } |
| |
| // https://issues.apache.org/jira/browse/TAP5-2560 |
| @Test |
| public void missing_property_due_to_wrong_type_parameter_resolution() |
| { |
| |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel<FileContentUnit> beanModel = source.createDisplayModel(FileContentUnit.class, messages); |
| |
| // throws "org.apache.tapestry5.internal.services.PropertyExpressionException: Exception generating conduit for expression 'content.mimeType': Class org.apache.tapestry5.internal.services.AbstractBeanModelSourceImplTest$ContentData does not contain a property (or public field) named 'mimeType'." without fix |
| beanModel.add("content.mimeType"); |
| |
| verify(); |
| |
| } |
| |
| // https://issues.apache.org/jira/browse/TAP5-2560 |
| @Test |
| public void handling_nested_generics() |
| { |
| |
| Messages messages = mockMessages(); |
| |
| stub_contains(messages, false); |
| |
| replay(); |
| |
| BeanModel<BeanHolder> beanModel = source.createDisplayModel(BeanHolder.class, messages); |
| |
| // this line would throw an exception |
| beanModel.add("bean.value"); |
| |
| verify(); |
| |
| } |
| |
| // https://issues.apache.org/jira/browse/TAP5-2032 |
| @Test |
| public void tap5_2032() |
| { |
| // explodes without fix |
| new PropertyAccessImpl().getAdapter(ById.class); |
| } |
| |
| public interface IdentifiableEnum<E extends Enum<E>, ID extends Number> |
| { |
| ID getId(); |
| } |
| |
| public enum ById implements IdentifiableEnum<ById, Byte> |
| { |
| ; |
| public Byte getId() |
| { |
| return null; |
| } |
| } |
| |
| final private static class SortableBean |
| { |
| private int sortableByDefault; |
| private int nonSortable; |
| private SimpleBean sortable; |
| private SimpleBean nonSortableByDefault; |
| |
| public int getSortableByDefault() |
| { |
| return sortableByDefault; |
| } |
| |
| @Sortable(false) |
| public int getNonSortable() |
| { |
| return nonSortable; |
| } |
| |
| @Sortable(true) |
| public SimpleBean getSortable() |
| { |
| return sortable; |
| } |
| |
| public SimpleBean getNonSortableByDefault() |
| { |
| return nonSortableByDefault; |
| } |
| |
| } |
| |
| public interface NonTranslatableContentUnit<T extends ContentData> { |
| T getContent(); |
| } |
| |
| public interface BinaryContentUnit<T extends BinaryContent> extends NonTranslatableContentUnit<T> { |
| } |
| |
| public interface FileContentUnit extends BinaryContentUnit<FileContent> { |
| } |
| |
| public interface ContentData { |
| |
| boolean isEmpty(); |
| } |
| |
| public interface BinaryContent extends ContentData { |
| |
| String getMimeType(); |
| } |
| |
| public interface FileContent extends BinaryContent { |
| } |
| |
| public class ConcreteFileContent implements FileContentUnit { |
| |
| @Override |
| public FileContent getContent() { |
| return null; |
| } |
| |
| } |
| |
| public class BeanHolder { |
| private final Bean bean; |
| |
| public BeanHolder(Bean bean) { |
| super(); |
| this.bean = bean; |
| } |
| |
| |
| public Bean getBean() { |
| return bean; |
| } |
| |
| } |
| |
| public class Bean<T> implements Comparable<Bean<T>> { |
| |
| private final T value_; |
| |
| public Bean(T value) { |
| value_ = value; |
| } |
| |
| @Override |
| public int compareTo(Bean<T> o) { |
| return 0; |
| } |
| |
| public T getValue() { |
| return value_; |
| } |
| } |
| |
| protected final PropertyConduit mockPropertyConduit() |
| { |
| return newMock(PropertyConduit.class); |
| } |
| |
| } |