Domain objects to support specialized use cases (solution space objects) are not persisted; instead their state is serialized into the Wicket page components.
The ClaimWizard uses an internal
page field (of type Page
enum) to determine which page the user is on; from this we determine
which properties should be visible, and whether the
previous(), next() and
finish() actions are available.
package org.nakedobjects.examples.claims.dom.claim;
import java.util.Calendar;
import java.util.List;
import org.nakedobjects.applib.AbstractDomainObject;
import org.nakedobjects.applib.annotation.Disabled;
import org.nakedobjects.applib.annotation.Hidden;
import org.nakedobjects.applib.annotation.Ignore;
import org.nakedobjects.applib.annotation.MemberOrder;
import org.nakedobjects.applib.annotation.NotPersistable;
import org.nakedobjects.applib.annotation.TypicalLength;
import org.nakedobjects.applib.clock.Clock;
import org.nakedobjects.examples.claims.dom.employee.EmployeeRepository;
import org.starobjects.wicket.applib.WizardPageDescription;
@NotPersistable
public class ClaimWizard extends AbstractDomainObject {
public enum Page {
INTRO("This wizard will take you through the process of creating a claim"),
CLAIMANT("Enter the claimant that is making this claim"),
APPROVER("By default, the claimant's own approver will approve this claim. " +
"Update here if another approver will approve this claim."),
DESCRIPTION("Update the description if required."),
SUMMARY("Confirm all details, or go back and amend if needed");
private String description;
private Page(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public boolean hasPrevious() {
return ordinal() > 0;
}
public Page previous() {
if (hasPrevious()) {
return values()[ordinal() - 1];
} else {
return this;
}
}
public boolean hasNext() {
return ordinal() < values().length - 1;
}
public Page next() {
if (hasNext()) {
return values()[ordinal() + 1];
} else {
return this;
}
}
@Ignore
public boolean is(Page... pages) {
for (Page page : pages) {
if (page == this) {
return true;
}
}
return false;
}
}
// {{ Lifecycle
public void created() {
setPage(Page.INTRO);
setDescription("Expenses for week #" + weekNum());
}
private int weekNum() {
return getTimeAsCalendar().get(Calendar.WEEK_OF_YEAR);
}
protected Calendar getTimeAsCalendar() {
return Clock.getTimeAsCalendar();
}
// }}
// {{ Page
private Page page;
@Hidden
public Page getPage() { ... }
public void setPage(final Page page) { ... }
// }}
// {{ Page Description
@WizardPageDescription
@TypicalLength(60)
@MemberOrder(sequence = "1")
public String getPageDescription() {
return getPage().getDescription();
}
// }}
// {{ Claimant
private Claimant claimant;
@MemberOrder(sequence = "2")
public Claimant getClaimant() { ... }
public void setClaimant(final Claimant claimant) { ... }
public void modifyClaimant(final Claimant claimant) { ... }
public void clearClaimant() { ... }
}
protected void onModifyClaimant(final Claimant oldClaimant,
final Claimant newClaimant) {
setApprover(newClaimant.getApprover());
}
protected void onClearClaimant(final Claimant oldClaimant) {
}
@SuppressWarnings("unchecked")
public List<Claimant> choicesClaimant() {
return employeeRepository.allEmployees();
}
public String disableClaimant() {
return coalesce(claimCreated(), confirmIfOnSummaryPage());
}
public boolean hideClaimant() {
return !getPage().is(Page.CLAIMANT, Page.SUMMARY);
}
// }}
// {{ Approver
private Approver approver;
@MemberOrder(sequence = "3")
public Approver getApprover() { ... }
public void setApprover(final Approver approver) { ... }
public String disableApprover() {
return coalesce(claimCreated(), confirmIfOnSummaryPage());
}
public boolean hideApprover() {
return !getPage().is(Page.APPROVER, Page.SUMMARY);
}
// }}
// {{ Description
private String description;
@MemberOrder(sequence = "4")
public String getDescription() { ... }
public void setDescription(final String description) { ... }
public String disableDescription() {
return coalesce(claimCreated(), confirmIfOnSummaryPage());
}
public boolean hideDescription() {
return !getPage().is(Page.DESCRIPTION, Page.SUMMARY);
}
private String claimCreated() {
return claim != null ? "Claim created" : null;
}
// }}
// {{ Claim
private Claim claim;
@Disabled
@MemberOrder(sequence = "5")
public Claim getClaim() { ... }
public void setClaim(final Claim claim) { ... }
public boolean hideClaim() { ... }
// }}
// {{ previous
@MemberOrder(sequence = "1")
public void previous() {
setPage(getPage().previous());
}
public String disablePrevious() {
return coalesce(noPreviousPage(), confirmIfOnSummaryPage());
}
private String noPreviousPage() {
return !getPage().hasPrevious() ? "no previous page" : null;
}
// }}
// {{ next
@MemberOrder(sequence = "2")
public void next() {
setPage(getPage().next());
}
public String disableNext() {
return coalesce(noNextPage(), confirmIfOnSummaryPage());
}
private String noNextPage() {
return !getPage().hasNext() ? "no next page" : null;
}
// }}
// {{ finish
@MemberOrder(sequence = "3")
public Claim finish() {
Claim claim = newTransientInstance(Claim.class);
claim.setClaimant(getClaimant());
claim.setApprover(getApprover());
claim.setDescription(getDescription());
setClaim(claim);
persist(claim);
return claim;
}
public String disableFinish() {
if (getPage().hasNext()) {
return "wizard has further pages to complete";
}
return getContainer().validate(this);
}
// }}
// {{ helpers
private String confirmIfOnSummaryPage() {
return getPage().is(Page.SUMMARY) ? "confirm" : null;
}
private static String coalesce(String... strings) {
for (String string : strings) {
if (string != null)
return string;
}
return null;
}
// }}
// {{ injected: EmployeeRepository
private EmployeeRepository employeeRepository;
public void setEmployeeRepository(
final EmployeeRepository employeeRepository) { ... }
// }}
}The ClaimExpenseSummary is used as a
report object:
package org.nakedobjects.examples.claims.dom.claim;
import org.nakedobjects.applib.annotation.Disabled;
import org.nakedobjects.applib.annotation.Ignore;
import org.nakedobjects.applib.annotation.MemberOrder;
import org.nakedobjects.applib.annotation.NotPersistable;
import org.nakedobjects.applib.value.Money;
import org.starobjects.wicket.applib.PieChartable;
@NotPersistable
public class ClaimantExpenseSummary implements PieChartable,
Comparable<ClaimantExpenseSummary> {
// {{ Identification
public String title() {
return getClaimant() != null ? getClaimant().title() : "(untitled)";
}
// }}
// {{ Claimant
private Claimant claimant;
@MemberOrder(sequence = "1")
public Claimant getClaimant() { ... }
public void setClaimant(final Claimant claimant) { ... }
// }}
// {{ Amount
private Money amount;
@Disabled
@MemberOrder(sequence = "1")
public Money getAmount() { ... }
public void setAmount(final Money amount) { ... }
// }}
// {{ programmatic
@Ignore
public void addAmount(Money amount) {
if (this.amount == null) {
this.amount = amount;
} else {
this.amount = this.amount.add(amount);
}
}
// }}
// {{ PieChartable impl
@Ignore
@Override
public double getPieChartValue() {
return getAmount().doubleValue();
}
@Ignore
@Override
public String getPieChartLabel() {
return title();
}
// }}
@Override
public int compareTo(ClaimantExpenseSummary o) {
if (getPieChartValue() < o.getPieChartValue())
return -1;
if (getPieChartValue() > o.getPieChartValue())
return +1;
return 0;
}
}Note that it implements the (rather horribly named)
PieChartable, which is picked up by the
googlecharts custom component (see Section 5.3, “Google Charts”).