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”).