You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2013/02/19 09:00:23 UTC

[9/10] git commit: wrapped models

wrapped models


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/d93701fe
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/d93701fe
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/d93701fe

Branch: refs/heads/reference-guide
Commit: d93701fe8d58da0d314137c47f5a0b5af2a90bdb
Parents: 8d310e9
Author: Michael Mosmann <mi...@mosmann.de>
Authored: Mon Feb 18 22:41:47 2013 +0100
Committer: Michael Mosmann <mi...@mosmann.de>
Committed: Mon Feb 18 22:41:47 2013 +0100

----------------------------------------------------------------------
 wicket-reference-guide/models/pom.xml              |    4 +
 .../wicket/reference/models/compound/Person.java   |    6 +-
 .../wicket/reference/models/wrapped/Address.java   |   21 +++++
 .../reference/models/wrapped/AddressModel.java     |   55 +++++++++++
 .../reference/models/wrapped/CustomLabel.java      |   33 +++++++
 .../wicket/reference/models/wrapped/Person.java    |   42 +++++++++
 .../reference/models/wrapped/PersonModel.java      |   70 +++++++++++++++
 .../reference/models/wrapped/PersonProvider.java   |   36 ++++++++
 .../models/wrapped/PersonTableColumn.java          |   43 +++++++++
 .../wrapped/PersonTableColumnWithCustomLabel.java  |   59 ++++++++++++
 .../models/wrapped/WrappedModelFormPage.html       |   15 +++
 .../models/wrapped/WrappedModelFormPage.java       |   53 +++++++++++
 .../src/documentation/source/models.rst            |   59 ++++++++++++
 13 files changed, 493 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/pom.xml b/wicket-reference-guide/models/pom.xml
index cecc5e9..9e775e9 100644
--- a/wicket-reference-guide/models/pom.xml
+++ b/wicket-reference-guide/models/pom.xml
@@ -32,5 +32,9 @@
 			<groupId>org.apache.wicket</groupId>
 			<artifactId>wicket-core</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.apache.wicket</groupId>
+			<artifactId>wicket-extensions</artifactId>
+		</dependency>
 	</dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/compound/Person.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/compound/Person.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/compound/Person.java
index 3f27e85..4bbd950 100644
--- a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/compound/Person.java
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/compound/Person.java
@@ -6,7 +6,7 @@ import java.io.Serializable;
 public class Person implements Serializable
 {
 	String name;
-	Integer age;
+	int age;
 	Address address;
 
 	public String getName()
@@ -19,12 +19,12 @@ public class Person implements Serializable
 		this.name = name;
 	}
 
-	public Integer getAge()
+	public int getAge()
 	{
 		return age;
 	}
 
-	public void setAge(Integer age)
+	public void setAge(int age)
 	{
 		this.age = age;
 	}

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Address.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Address.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Address.java
new file mode 100644
index 0000000..9fb7d9d
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Address.java
@@ -0,0 +1,21 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import java.io.Serializable;
+
+//#classOnly
+public class Address implements Serializable
+{
+	String city;
+
+	public String getCity()
+	{
+		return city;
+	}
+
+	public void setCity(String city)
+	{
+		this.city = city;
+	}
+}
+//#classOnly
+

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/AddressModel.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/AddressModel.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/AddressModel.java
new file mode 100644
index 0000000..42bb760
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/AddressModel.java
@@ -0,0 +1,55 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import org.apache.wicket.model.IModel;
+
+//#classOnly
+public class AddressModel<T> implements IModel<T>
+{
+	private final IModel<Address> addressContainingModel;
+
+	private final AddressModelType type;
+
+	public enum AddressModelType {
+		CITY_MODEL;
+	};
+
+	public AddressModel(IModel<Address> addressContainingModel, AddressModelType type)
+	{
+		this.addressContainingModel = addressContainingModel;
+		this.type = type;
+	}
+
+	@Override
+	public T getObject()
+	{
+		Address address = addressContainingModel.getObject();
+		switch (type)
+		{
+			case CITY_MODEL :
+				return (T)address.getCity();
+			default :
+				throw new UnsupportedOperationException("invalid AddressModelType = " + type.name());
+		}
+	}
+
+	@Override
+	public void setObject(T object)
+	{
+		Address address = addressContainingModel.getObject();
+		switch (type)
+		{
+			case CITY_MODEL :
+				address.setCity((String)object);
+				break;
+			default :
+				throw new UnsupportedOperationException("invalid AddressModelType = " + type.name());
+		}
+	}
+
+	@Override
+	public void detach()
+	{
+		addressContainingModel.detach();
+	}
+}
+//#classOnly

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/CustomLabel.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/CustomLabel.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/CustomLabel.java
new file mode 100644
index 0000000..8d61ada
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/CustomLabel.java
@@ -0,0 +1,33 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.util.convert.IConverter;
+
+//#classOnly
+public class CustomLabel extends Label
+{
+	private final IConverter<?> converter;
+
+	/**
+	 * @param id
+	 * @param label
+	 */
+	public <T> CustomLabel(String id, IModel<T> labelModel, IConverter<T> converter)
+	{
+		super(id, labelModel);
+		this.converter = converter;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.apache.wicket.Component#getConverter(java.lang.Class)
+	 */
+	@Override
+	public <C> IConverter<C> getConverter(Class<C> type)
+	{
+		return (IConverter<C>)this.converter;
+	}
+}
+//#classOnly

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Person.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Person.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Person.java
new file mode 100644
index 0000000..b18e3ee
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/Person.java
@@ -0,0 +1,42 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import java.io.Serializable;
+
+//#classOnly
+public class Person implements Serializable
+{
+	String name;
+	int age;
+	Address address;
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public void setName(String name)
+	{
+		this.name = name;
+	}
+
+	public int getAge()
+	{
+		return age;
+	}
+
+	public void setAge(int age)
+	{
+		this.age = age;
+	}
+
+	public Address getAddress()
+	{
+		return address;
+	}
+
+	public void setAddress(Address address)
+	{
+		this.address = address;
+	}
+}
+//#classOnly

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonModel.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonModel.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonModel.java
new file mode 100644
index 0000000..4348e1a
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonModel.java
@@ -0,0 +1,70 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import org.apache.wicket.model.IModel;
+
+public class PersonModel<T> implements IModel<T>
+{
+	private final IModel<Person> personContainingModel;
+	private final PersonModelType type;
+
+	public enum PersonModelType {
+		NAME_MODEL, AGE_MODEL, ADDRESS_MODEL;
+	}
+
+	public PersonModel(IModel<Person> personContainingModel, PersonModelType type)
+	{
+		this.personContainingModel = personContainingModel;
+		this.type = type;
+	}
+
+	@Override
+	public T getObject()
+	{
+		Person person = personContainingModel.getObject();
+
+		switch (type)
+		{
+			case NAME_MODEL :
+				return (T)person.getName();
+
+			case AGE_MODEL :
+				return (T)Integer.valueOf(person.getAge());
+
+			case ADDRESS_MODEL :
+				return (T)person.getAddress();
+
+			default :
+				throw new UnsupportedOperationException("invalid PersonModelType = " + type.name());
+		}
+	}
+
+	@Override
+	public void setObject(T object)
+	{
+		Person person = personContainingModel.getObject();
+
+		switch (type)
+		{
+			case NAME_MODEL :
+				person.setName((String)object);
+				break;
+
+			case AGE_MODEL :
+				person.setAge((Integer)object);
+				break;
+
+			case ADDRESS_MODEL :
+				person.setAddress((Address)object);
+				break;
+
+			default :
+				throw new UnsupportedOperationException("invalid PersonModelType = " + type.name());
+		}
+	}
+
+	@Override
+	public void detach()
+	{
+		personContainingModel.detach();
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonProvider.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonProvider.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonProvider.java
new file mode 100644
index 0000000..873eeb5
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonProvider.java
@@ -0,0 +1,36 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.wicket.markup.repeater.data.IDataProvider;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class PersonProvider implements IDataProvider<Person>
+{
+	@Override
+	public void detach()
+	{
+
+	}
+
+	@Override
+	public Iterator<? extends Person> iterator(long first, long count)
+	{
+		return new ArrayList<Person>().iterator();
+	}
+
+	@Override
+	public long size()
+	{
+		return 0;
+	}
+
+	@Override
+	public IModel<Person> model(Person person)
+	{
+		return Model.of(person);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumn.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumn.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumn.java
new file mode 100644
index 0000000..bc8e3a5
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumn.java
@@ -0,0 +1,43 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.reference.models.wrapped.AddressModel.AddressModelType;
+import org.apache.wicket.reference.models.wrapped.PersonModel.PersonModelType;
+
+//#classOnly
+public class PersonTableColumn extends AbstractColumn<Person, Void>
+{
+	private final PersonModelType type;
+
+	public PersonTableColumn(String columnName, PersonModelType type)
+	{
+		super(Model.of(columnName));
+		this.type = type;
+	}
+
+	@Override
+	public void populateItem(Item<ICellPopulator<Person>> cellItem, String componentId,
+		IModel<Person> rowModel)
+	{
+
+		switch (type)
+		{
+			// this needs to be handled seperately due to it being an Address
+			// instance from the Person object.
+			case ADDRESS_MODEL :
+				cellItem.add(new Label(componentId, new AddressModel<String>(
+					new PersonModel<Address>(rowModel, PersonModelType.ADDRESS_MODEL),
+					AddressModelType.CITY_MODEL)));
+				break;
+
+			default :
+				cellItem.add(new Label(componentId, new PersonModel(rowModel, type)));
+		}
+	}
+}
+//#classOnly

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumnWithCustomLabel.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumnWithCustomLabel.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumnWithCustomLabel.java
new file mode 100644
index 0000000..4ea1b3e
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumnWithCustomLabel.java
@@ -0,0 +1,59 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import java.util.Locale;
+
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.reference.models.wrapped.PersonModel.PersonModelType;
+import org.apache.wicket.util.convert.IConverter;
+
+//#classOnly
+public class PersonTableColumnWithCustomLabel extends AbstractColumn<Person, Void>
+{
+	private final PersonModelType type;
+
+	public PersonTableColumnWithCustomLabel(String columnName, PersonModelType type)
+	{
+		super(Model.of(columnName));
+		this.type = type;
+	}
+
+	//#refactor
+	@Override
+	public void populateItem(Item<ICellPopulator<Person>> cellItem, String componentId,
+		IModel<Person> rowModel)
+	{
+
+		switch (type)
+		{
+			// this needs to be handled seperately due to it being an Address
+			// instance from the Person object.
+			case ADDRESS_MODEL :
+				cellItem.add(new CustomLabel(componentId, new PersonModel<Address>(rowModel,
+					PersonModelType.ADDRESS_MODEL), new IConverter<Address>()
+				{
+					@Override
+					public Address convertToObject(String value, Locale locale)
+					{
+						throw new UnsupportedOperationException("converter is readonly.");
+					}
+
+					@Override
+					public String convertToString(Address address, Locale locale)
+					{
+						return address.getCity();
+					}
+				}));
+				break;
+
+			default :
+				cellItem.add(new Label(componentId, new PersonModel(rowModel, type)));
+		}
+	}
+	//#refactor
+}
+// #classOnly

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.html
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.html b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.html
new file mode 100644
index 0000000..2db0eeb
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.html
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:wicket="http://wicket.apache.org">
+<head>
+<title>Compound Model Form</title>
+</head>
+<body>
+	<form wicket:id="form">
+		<label>Name</label> <input wicket:id="name" />
+		<label>Age</label><input wicket:id="age" />
+		<label>City</label><input wicket:id="address.city" />
+		<input type="submit" value="Submit">
+	</form>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.java
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.java b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.java
new file mode 100644
index 0000000..c152c9a
--- /dev/null
+++ b/wicket-reference-guide/models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.java
@@ -0,0 +1,53 @@
+package org.apache.wicket.reference.models.wrapped;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.RequiredTextField;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.reference.models.wrapped.AddressModel.AddressModelType;
+import org.apache.wicket.reference.models.wrapped.PersonModel.PersonModelType;
+
+public class WrappedModelFormPage extends WebPage
+{
+	public WrappedModelFormPage()
+	{
+		//#form
+		Person person = new Person();
+		Model<Person> beanModel = Model.of(person);
+		
+		//#form
+		person.setAge(12);
+		person.setName("Klaus");
+		Address address = new Address();
+		address.setCity("Lübeck");
+		person.setAddress(address);
+		//#form
+		Form<Void> form = new Form<Void>("form");
+		
+		form.add(new RequiredTextField<String>("name", new PersonModel<String>(beanModel,
+			PersonModelType.NAME_MODEL)));
+		form.add(new RequiredTextField<Integer>("age", new PersonModel<Integer>(beanModel,
+			PersonModelType.AGE_MODEL), Integer.class));
+		form.add(new RequiredTextField<String>("address.city", new AddressModel<String>(
+			new PersonModel<Address>(beanModel, PersonModelType.ADDRESS_MODEL),
+			AddressModelType.CITY_MODEL)));
+
+		add(form);
+		//#form
+
+		//#datatable
+		List<IColumn<Person,Void>> columns=new ArrayList<IColumn<Person,Void>>();
+		
+		columns.add(new PersonTableColumn("Name", PersonModelType.NAME_MODEL));
+		columns.add(new PersonTableColumn("Age", PersonModelType.AGE_MODEL));
+		columns.add(new PersonTableColumn("City", PersonModelType.ADDRESS_MODEL));
+		
+		DataTable<Person, Void> table = new DataTable<Person, Void>("datatable", columns, new PersonProvider(), 10);
+		//#datatable
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/d93701fe/wicket-reference-guide/src/documentation/source/models.rst
----------------------------------------------------------------------
diff --git a/wicket-reference-guide/src/documentation/source/models.rst b/wicket-reference-guide/src/documentation/source/models.rst
index 0840927..acfdaf6 100644
--- a/wicket-reference-guide/src/documentation/source/models.rst
+++ b/wicket-reference-guide/src/documentation/source/models.rst
@@ -183,6 +183,65 @@ Also, note that if you are using a component that you do not want to reference t
 
 
 
+Wrapped Object Models
+---------------------
+
+.. todo:: IMHO this is not the best way to explain and implement this concept
+
+It is possible to create Models that explicitly define in normal java code what is to be returned as the model object for each property within the object being wrapped. So instead of specifying via a string the name of the property to fetch the value you from the specification is done in Java.
+
+While creating Model's in this pattern takes longer (more classes) than using Reflection based PropertyModels it prevents the problems that can occur when critical functionality is defined in String based context that most IDE's do not refactor properly.
+
+It also helps with readability when the models are added to components to be able to easily see the types involved.
+
+These are the Address and Person classes used in the previous examples:
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/Address.java#classOnly
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/Person.java#classOnly
+
+The first step is to create a Wrapped Object Model for the Address and Person classes:
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/AddressModel.java#classOnly
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonModel.java#classOnly
+
+Notice how each wrapped model contains an inner model that contains the actual pojo instance. This allows for the wrapped model to be a plain Model or a LoadableDetachableModel, or even another wrapped model where its .getObject() results in a suitably typed input value (see the "address.city" field in the example below).
+
+At this point to create a form using our wrapped object models looks like:
+
+.. todo:: IMHO this needs refactoring (improve generic type handling)
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.java#form
+
+A wrapped object model also makes working with DataTables's easier as one IColumn implementation can be written for each object class which makes the declaration of the table much simpler.
+
+e.g.
+
+.. todo:: IMHO refactoring needed
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumn.java#classOnly
+	
+So the table could be declared like:
+
+.. todo:: IMHO refactoring needed
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/WrappedModelFormPage.java#datatable
+  
+Another option with the complex object is to create a custom ``IConverter`` that will take in this case the Address instance from the PersonModel and render the string value as the city name.
+
+e.g.
+
+.. todo:: IMHO refactoring needed
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/CustomLabel.java#classOnly
+
+
+With the populate from above as:
+
+.. includecode:: ../../../models/src/main/java/org/apache/wicket/reference/models/wrapped/PersonTableColumnWithCustomLabel.java#refactor
+
+