Table of Contents
Tapestry includes a number of components designed to simplify interactions with the client, especially when handling forms.
In this chapter, we'll build a survey-taking application that collects information from the user, stores it in an in-memory database, and produces tabular results summarizing what has been entered.
We'll see how to validate input from the client, how to create radio groups and pop-up selections and how to organize information for display.
The application has three main screens; the first is a home page:
The second page is for entering survey data:
The last page is used to present results collected from many surveys:
In addition, we are re-using the Border component from the previous chapter.
The application does not use an actual database; the survey information is stored in memory (the amount of work to set up a JDBC database is beyond the scope of this tutorial).
The source code for this chapter is in the tutorial.survey package.
At the root of this application is an object that represents a survey taken by a user. We want to collect the name (which is optional), the sex and the race, the age and lastly, which pets the survey taker prefers.
package tutorial.survey; import java.util.*; import com.primix.tapestry.*; import java.io.*; public class Survey implements Serializable, Cloneable { private Object primaryKey; private String name; private int age = 0; private Sex sex = Sex.MALE; private Race race = Race.CAUCASIAN; private boolean likesDogs = true; private boolean likesCats; private boolean likesFerrits; private boolean likesTurnips; public Object getPrimaryKey() { return primaryKey; } public void setPrimaryKey(Object value) { primaryKey = value; } public String getName() { return name; } public void setName(String value) { name = value; } public int getAge() { return age; } public void setAge(int value) { age = value; } public void setSex(Sex value) { sex = value; } public Sex getSex() { return sex; } public void setRace(Race value) { race = value; } public Race getRace() { return race; } public boolean getLikesCats() { return likesCats; } public void setLikesCats(boolean value) { likesCats = value; } public boolean getLikesDogs() { return likesDogs; } public void setLikesDogs(boolean value) { likesDogs = value; } public boolean getLikesFerrits() { return likesFerrits; } public void setLikesFerrits(boolean value) { likesFerrits = value; } public boolean getLikesTurnips() { return likesTurnips; } public void setLikesTurnips(boolean value) { likesTurnips = value; } /** * Validates that the survey is acceptible; throws an {@link IllegalArgumentException} * if not valid. * */ public void validate() throws IllegalArgumentException { if (race == null) throw new IllegalArgumentException("Race must be specified."); if (sex == null) throw new IllegalArgumentException("Sex must be specified."); if (age < 1) throw new IllegalArgumentException("Age must be at least one."); } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { return null; } } } |
The race and sex properties are defined in terms of the Race and Sex classes, which are derived from Enum. Enum classes act like C enum types; a specific number of pre-defined values are declared by the class (as static final constants of the class).
package tutorial.survey; import com.primix.foundation.Enum; /** * An enumeration of different races. * */ public class Race extends Enum { public static final Race CAUCASIAN = new Race("CAUCASIAN"); public static final Race AFRICAN = new Race("AFRICAN"); public static final Race ASIAN = new Race("ASIAN"); public static final Race INUIT = new Race("INUIT"); public static final Race MARTIAN = new Race("MARTIAN"); private Race(String enumerationId) { super(enumerationId); } private Object readResolve() { return getSingleton(); } } |
This is better than using String or int constants because of type safety; the Java compiler will notice if you pass Race.INUIT as a parameter that expects an instance of Sex ... if they were both encoded as numbers, the compiler wouldn't know that there was a programming error.