A.3. Custom Views for Specialized Use Cases

It's very common that the objects underpinning specialized use cases have custom ComponentFactorys so that they are rendered in a particular way. This is the case for ClaimWizard (see Section 2.8, “Specialized Use Cases”); for ClaimExpenseSummary the specialized view is provided by the googlecharts custom component (see Section 5.3, “Google Charts”).

It's good practice to create a new Maven submodule for any custom components; you'll find this component in the claims-ui module.

A.3.1. ClaimWizardComponentFactory

package org.nakedobjects.examples.claims.ui.claimwizard;

import org.apache.wicket.Component;
import org.apache.wicket.model.IModel;
import org.nakedobjects.examples.claims.dom.claim.ClaimWizard;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.starobjects.wicket.viewer.components.ComponentType;
import org.starobjects.wicket.viewer.components.entity.EntityComponentFactoryAbstract;
import org.starobjects.wicket.viewer.components.entity.EntityModel;

public class ClaimWizardComponentFactory extends EntityComponentFactoryAbstract {

    private static final long serialVersionUID = 1L;
    private static final String NAME = "wizard";

    public ClaimWizardComponentFactory() {
        super(ComponentType.ENTITY, NAME);
    }

    @Override
    protected ApplicationAdvice appliesTo(IModel<?> model) {
        return appliesExclusivelyIf(super.appliesTo(model).applies() && isModelForWizard((EntityModel)model));
    }

    private boolean isModelForWizard(EntityModel model) {
        final NakedObjectSpecification typeOfSpec = model.getTypeOfSpecification();
        final NakedObjectSpecification claimWizardSpec = getSpecificationLoader().loadSpecification(ClaimWizard.class);
        return typeOfSpec.isOfType(claimWizardSpec);
    }

    @Override
    public Component createComponent(String id, IModel<?> model) {
        final EntityModel entityModel = (EntityModel)model;
        return new ClaimWizardPanel(id, entityModel);
    }
}

A.3.2. ClaimWizardPanel

package org.nakedobjects.examples.claims.ui.claimwizard;

import java.util.List;

import org.apache.wicket.Session;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.validation.AbstractFormValidator;
import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.Model;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.commons.filters.Filter;
import org.nakedobjects.metamodel.facets.object.validate.ValidateObjectFacet;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociationFilters;
import org.nakedobjects.metamodel.spec.feature.OneToOneAssociation;
import org.starobjects.wicket.viewer.common.ProcessObjectPanelAbstract;
import org.starobjects.wicket.viewer.common.scalars.ScalarModel;
import org.starobjects.wicket.viewer.components.ComponentType;
import org.starobjects.wicket.viewer.components.entity.EntityModel;
import org.starobjects.wicket.viewer.mementos.PropertyMemento;
import org.starobjects.wicket.viewer.util.Mementos;

import com.google.common.collect.Lists;

public class ClaimWizardPanel extends ProcessObjectPanelAbstract {

    private static final long serialVersionUID = 1L;

    private static final String ID_CLAIM_WIZARD_PROPERTIES_FORM = "claimWizardPropertiesForm";
    private static final String ID_PROPERTIES = "properties";

    public ClaimWizardPanel(String id, EntityModel model) {
        super(id, model);
        buildGui();
    }

    private ClaimWizardForm claimWizardForm;

    private void buildGui() {
        EntityModel entityModel = getModel();
        entityModel.toEditMode();

        claimWizardForm = new ClaimWizardForm(ID_CLAIM_WIZARD_PROPERTIES_FORM,
                getModel());
        addOrReplace(claimWizardForm);
    }

    class ClaimWizardForm extends Form<NakedObject> {

        private static final long serialVersionUID = 1L;

        private static final String ID_FEEDBACK = "feedback";
        private static final String ID_PREVIOUS = "previous";
        private static final String ID_NEXT = "next";
        private static final String ID_FINISH = "finish";

        private static final String PREVIOUS_ACTION_ID = "previous()";
        private static final String NEXT_ACTION_ID = "next()";
        private static final String FINISH_ACTION_ID = "finish()";

        private FeedbackPanel feedback;

        public ClaimWizardForm(String id, EntityModel entityModel) {
            super(id, entityModel);

            buildFormGui();
        }

        private void buildFormGui() {
            addProperties();
            addButtons();
            addFeedbackGui();

            addValidator();
        }

        private void addProperties() {
            EntityModel entityModel = (EntityModel) getModel();
            NakedObject adapter = entityModel.getObject();
            NakedObjectSpecification noSpec = adapter.getSpecification();

            List<OneToOneAssociation> properties = visibleProperties(adapter,
                    noSpec);

            RepeatingView rv = new RepeatingView(ID_PROPERTIES);
            add(rv);
            List<PropertyMemento> mementos = buildPropertyMementos(properties);
            for (PropertyMemento pm : mementos) {
                WebMarkupContainer container = new WebMarkupContainer(rv
                        .newChildId());
                rv.add(container);

                ScalarModel scalarModel = entityModel.getPropertyModel(pm);
                getComponentFactoryRegistry().addOrReplaceComponent(container,
                        ComponentType.SCALAR, scalarModel);
            }
        }

        @SuppressWarnings("unchecked")
        private List<OneToOneAssociation> visibleProperties(
                NakedObject adapter, NakedObjectSpecification noSpec) {
            return (List<OneToOneAssociation>) noSpec
                    .getAssociationList(visiblePropertyFilter(adapter));
        }

        private Filter<NakedObjectAssociation> visiblePropertyFilter(
                NakedObject adapter) {
            return NakedObjectAssociationFilters.PROPERTIES
                    .and(NakedObjectAssociationFilters.dynamicallyVisible(
                            getAuthenticationSession(), adapter));
        }

        private List<PropertyMemento> buildPropertyMementos(
                List<OneToOneAssociation> properties) {
            List<PropertyMemento> transform = Lists.transform(properties,
                    Mementos.fromProperty());
            // we copy into a new array list otherwise we get lazy evaluation =
            // reference to a non-serializable object
            return Lists.newArrayList(transform);
        }

        private void addButtons() {
            add(createButton(ID_PREVIOUS, "Previous", PREVIOUS_ACTION_ID));
            add(createButton(ID_NEXT, "Next", NEXT_ACTION_ID));
            add(createButton(ID_FINISH, "Finish", FINISH_ACTION_ID));
        }

        private Button createButton(final String id, final String label,
                final String actionId) {
            return new Button(id, Model.of(label)) {
                private static final long serialVersionUID = 1L;

                @Override
                public void onSubmit() {
                    boolean isValid = ClaimWizardPanel.this.isValid(getForm());
                    if (!isValid) {
                        return;
                    }

                    executeNoArgAction(actionId);
                }
            };
        }

        private void addFeedbackGui() {
            final FeedbackPanel feedback = addOrReplaceFeedback();

            NakedObject adapter = getModel().getObject();
            if (adapter == null) {
                feedback.error("cannot locate object:"
                        + getEntityModel().getNakedObjectMemento().toString());
            }
        }

        private FeedbackPanel addOrReplaceFeedback() {
            feedback = new ComponentFeedbackPanel(ID_FEEDBACK, this);
            feedback.setOutputMarkupPlaceholderTag(true);
            addOrReplace(feedback);
            return feedback;
        }

        private void addValidator() {
            add(new AbstractFormValidator() {

                private static final long serialVersionUID = 1L;

                @Override
                public FormComponent<?>[] getDependentFormComponents() {
                    return new FormComponent<?>[0];
                }

                @Override
                public void validate(Form<?> form) {
                    EntityModel entityModel = (EntityModel) getModel();
                    NakedObject adapter = entityModel.getObject();
                    ValidateObjectFacet facet = adapter.getSpecification()
                            .getFacet(ValidateObjectFacet.class);
                    if (facet == null) {
                        return;
                    }
                    String invalidReasonIfAny = facet.invalidReason(adapter);
                    if (invalidReasonIfAny != null) {
                        Session.get().getFeedbackMessages().add(
                                new FeedbackMessage(form, invalidReasonIfAny,
                                        FeedbackMessage.ERROR));
                    }
                }
            });
        }
    }
}

A.3.3. ClaimWizardPanel.html

<html>
    <body>
        <wicket:panel>
            <div class="claimWizard">
                <form wicket:id="claimWizardPropertiesForm" class="inputForm">
                    <fieldset class="inputFormTable properties">
                        <div wicket:id="properties">
                          <div wicket:id="scalarNameAndValue">[scalar]</div>
                        </div>
                        <div class="feedbackPanel">
                            <span wicket:id="feedback"/>
                        </div>
                        <input class="submit" type="submit" wicket:id="previous"/>
                        <input class="submit" type="submit" wicket:id="next"/>
                        <input class="submit" type="submit" wicket:id="finish"/>
                    </fieldset>
                </form>
            </div>
        </wicket:panel>
    </body>
</html>