org.wicketopia.persistence.component.scaffold.Scaffold.java Source code

Java tutorial

Introduction

Here is the source code for org.wicketopia.persistence.component.scaffold.Scaffold.java

Source

/*
 * Copyright (c) 2011 Carman Consulting, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wicketopia.persistence.component.scaffold;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.feedback.ContainerFeedbackMessageFilter;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.EmptyPanel;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.wicketopia.Wicketopia;
import org.wicketopia.context.Context;
import org.wicketopia.factory.PropertyComponentFactory;
import org.wicketopia.layout.view.CssBeanViewLayoutPanel;
import org.wicketopia.metadata.WicketopiaBeanFacet;
import org.wicketopia.model.column.FragmentColumn;
import org.wicketopia.model.label.DisplayNameModel;
import org.wicketopia.model.label.PluralizedModel;
import org.wicketopia.persistence.PersistencePlugin;
import org.wicketopia.persistence.PersistenceProvider;
import org.wicketopia.persistence.component.link.ajax.AjaxCreateLink;
import org.wicketopia.persistence.component.link.ajax.AjaxUpdateLink;
import org.wicketopia.persistence.model.LoadableDetachableEntityModel;
import org.wicketopia.persistence.model.repeater.PersistenceDataProvider;

import java.util.List;

/**
 * @author James Carman
 */
public class Scaffold<T> extends Panel implements IHeaderContributor {
    //----------------------------------------------------------------------------------------------------------------------
    // Fields
    //----------------------------------------------------------------------------------------------------------------------

    public static final int DEFAULT_ROWS_PER_PAGE = 25;
    private static final PackageResourceReference CSS_REFERENCE = new PackageResourceReference(Scaffold.class,
            "scaffold.css");
    private static final String CONTENT_ID = "content";
    private final Class<T> beanType;
    private final PersistenceProvider persistenceProvider;
    private ScaffoldMode mode = ScaffoldMode.List;
    private final FeedbackPanel feedback = new FeedbackPanel("feedback");
    private IModel<T> model;
    private final DisplayNameModel displayName;

    //----------------------------------------------------------------------------------------------------------------------
    // Constructors
    //----------------------------------------------------------------------------------------------------------------------

    public Scaffold(String id, Class<T> beanType) {
        this(id, beanType, PersistencePlugin.get().getPersistenceProvider());
    }

    public Scaffold(String id, Class<T> beanType, PersistenceProvider persistenceProvider) {
        super(id);
        WicketopiaBeanFacet beanFacet = WicketopiaBeanFacet.get(Wicketopia.get().getBeanMetaData(beanType));
        displayName = new DisplayNameModel(beanFacet);
        add(new AttributeModifier("class", new Model<String>("scaffold")));
        feedback.setOutputMarkupPlaceholderTag(true);
        feedback.setFilter(new ContainerFeedbackMessageFilter(this));
        add(feedback);
        this.beanType = beanType;
        this.persistenceProvider = persistenceProvider;
        refreshContent(null);
        setOutputMarkupPlaceholderTag(true);
    }

    private void refreshContent(AjaxRequestTarget target) {
        final Component content = createContent();
        content.setOutputMarkupPlaceholderTag(true);
        addOrReplace(content);
        if (target != null) {
            target.add(this);
        }
    }

    private Component createContent() {
        switch (mode) {
        case List:
            return new ListFragment();
        case View:
            return new ViewFragment();
        case Update:
            return new EditFragment();
        case Create:
            return new CreateFragment();
        default:
            return new EmptyPanel(CONTENT_ID);
        }
    }

    //----------------------------------------------------------------------------------------------------------------------
    // IHeaderContributor Implementation
    //----------------------------------------------------------------------------------------------------------------------

    @Override
    public void renderHead(IHeaderResponse response) {
        response.render(CssHeaderItem.forReference(CSS_REFERENCE));
    }

    //----------------------------------------------------------------------------------------------------------------------
    // Other Methods
    //----------------------------------------------------------------------------------------------------------------------

    private Context createContext(String mode) {
        return new Context(mode);
    }

    @Override
    protected void onDetach() {
        super.onDetach();
        displayName.detach();
    }

    //----------------------------------------------------------------------------------------------------------------------
    // Inner Classes
    //----------------------------------------------------------------------------------------------------------------------

    private final class ActionsColumn extends FragmentColumn<T> {
        private ActionsColumn() {
            super(new Model<String>("Actions"));
        }

        @Override
        protected Fragment createFragment(String componentId, IModel<T> rowModel) {
            Fragment f = new Fragment(componentId, "actions", Scaffold.this);
            f.add(new ViewLink("viewLink", rowModel));
            f.add(new ScaffoldEditLink("updateLink", rowModel));
            f.add(new ScaffoldDeleteLink("deleteLink", rowModel));
            return f;
        }
    }

    private final class ConfirmBehavior extends Behavior {
        private final String event;
        private final IModel<String> message;

        private ConfirmBehavior(String event, IModel<String> message) {
            this.event = event;
            this.message = message;
        }

        @Override
        public void detach(Component component) {
            super.detach(component);
            message.detach();
        }

        public void onComponentTag(Component component, ComponentTag tag) {
            StringBuilder handler = new StringBuilder();
            handler.append("if (!confirm('");
            handler.append(message.getObject().replaceAll("'", "\\\\'"));
            handler.append("')) {return false;} ");

            String script = tag.getAttributes().getString(event);
            if (script != null) {
                handler.append(script);
            }

            tag.put(event, handler.toString());
        }
    }

    private final class CreateFragment extends Fragment {
        private CreateFragment() {
            super(CONTENT_ID, "create", Scaffold.this);
            model = new CreateModel();
            final ScaffoldListLink listLink = new ScaffoldListLink("listButton");
            add(listLink.add(new Label("nameList", displayName).setRenderBodyOnly(true)));
            add(new Label("nameCaption", displayName).setRenderBodyOnly(true));
            final Form<T> form = new Form<T>("form", model);
            final PropertyComponentFactory<T> editorFactory = Wicketopia.get().createEditorFactory(beanType);
            final Context context = createContext(Context.CREATE);
            form.add(new CssBeanViewLayoutPanel<T>("layout", beanType, model, context, editorFactory));
            add(new SaveLink(form));
            add(form);
        }
    }

    private final class CreateModel extends LoadableDetachableModel<T> {
        @Override
        protected T load() {
            try {
                return beanType.newInstance();
            } catch (Exception e) {
                throw new WicketRuntimeException(
                        "Unable to instantiate " + beanType.getName() + " object (" + e.getMessage() + ").", e);
            }
        }
    }

    private final class EditFragment extends Fragment {
        private EditFragment() {
            super(CONTENT_ID, "edit", Scaffold.this);
            add(new Label("nameCaption", displayName).setRenderBodyOnly(true));
            add(new ScaffoldDeleteLink("deleteButton", model));
            add(new ScaffoldListLink("listButton").add(new Label("nameList", displayName).setRenderBodyOnly(true)));
            add(new ScaffoldCreateLink("createButton")
                    .add(new Label("nameCreate", displayName).setRenderBodyOnly(true)));
            final Form<T> form = new Form<T>("form", model);
            final PropertyComponentFactory<T> editorFactory = Wicketopia.get().createEditorFactory(beanType);
            final Context context = createContext(Context.UPDATE);
            form.add(new CssBeanViewLayoutPanel<T>("layout", beanType, model, context, editorFactory));
            add(form);
            add(new ScaffoldUpdateLink<T>(form));
        }
    }

    private final class ListFragment extends Fragment {
        private ListFragment() {
            super(CONTENT_ID, "list", Scaffold.this);
            final ScaffoldCreateLink create = new ScaffoldCreateLink("newEntity");
            add(create);
            add(new Label("pluralName", new PluralizedModel(displayName)).setRenderBodyOnly(true));
            create.add(new Label("displayName", displayName).setRenderBodyOnly(true));
            final PropertyComponentFactory<T> viewerFactory = Wicketopia.get().createViewerFactory(beanType);
            final Context context = createContext(Context.LIST);
            final List<IColumn<T, String>> columns = Wicketopia.get().createColumns(beanType, viewerFactory,
                    context);
            columns.add(new ActionsColumn());
            add(new AjaxFallbackDefaultDataTable<T, String>("table", columns,
                    new PersistenceDataProvider<T>(beanType, persistenceProvider), DEFAULT_ROWS_PER_PAGE));
        }
    }

    private class SaveLink extends AjaxCreateLink<T> {
        public SaveLink(Form<T> form) {
            super("saveButton", form, persistenceProvider);
        }

        @Override
        protected void afterCreate(T object, AjaxRequestTarget target) {
            Scaffold.this.mode = ScaffoldMode.View;
            Scaffold.this.info(displayName.getObject() + " Created");
            model = new LoadableDetachableEntityModel<T>(beanType, object, persistenceProvider);
            refreshContent(target);
        }

        @Override
        protected void onError(AjaxRequestTarget target, Form<?> form) {
            target.add(feedback);
        }
    }

    private final class ScaffoldCreateLink extends AjaxLink<Void> {
        private ScaffoldCreateLink(String id) {
            super(id);
        }

        @Override
        public void onClick(AjaxRequestTarget target) {
            mode = ScaffoldMode.Create;
            refreshContent(target);
        }
    }

    private final class ScaffoldDeleteLink extends AjaxLink<T> {
        private ScaffoldDeleteLink(String id, IModel<T> tiModel) {
            super(id, tiModel);
            add(new ConfirmBehavior("onclick", new Model<String>("Are you sure?")));
        }

        @Override
        public void onClick(AjaxRequestTarget target) {
            persistenceProvider.delete(getModelObject());
            Scaffold.this.info(displayName.getObject() + " Deleted");
            mode = ScaffoldMode.List;
            refreshContent(target);
        }
    }

    private final class ScaffoldEditLink extends AjaxLink<T> {
        private ScaffoldEditLink(String id, IModel<T> model) {
            super(id, model);
        }

        @Override
        public void onClick(AjaxRequestTarget target) {
            model = getModel();
            mode = ScaffoldMode.Update;
            refreshContent(target);
        }
    }

    private final class ScaffoldListLink extends AjaxLink<Void> {
        private ScaffoldListLink(String id) {
            super(id);
        }

        @Override
        public void onClick(AjaxRequestTarget target) {
            mode = ScaffoldMode.List;
            refreshContent(target);
        }
    }

    private class ScaffoldUpdateLink<T> extends AjaxUpdateLink<T> {
        public ScaffoldUpdateLink(Form<T> form) {
            super("saveButton", form, persistenceProvider);
        }

        @Override
        protected void afterUpdate(T object, AjaxRequestTarget target) {
            mode = ScaffoldMode.View;
            Scaffold.this.info(displayName.getObject() + " Updated");
            refreshContent(target);
        }

        @Override
        protected void onError(AjaxRequestTarget target, Form<?> form) {
            target.add(feedback);
        }
    }

    private final class ViewFragment extends Fragment {
        private ViewFragment() {
            super(CONTENT_ID, "view", Scaffold.this);
            add(new Label("nameCaption", displayName).setRenderBodyOnly(true));
            add(new ScaffoldEditLink("editButton", model));
            add(new ScaffoldDeleteLink("deleteButton", model));
            add(new ScaffoldListLink("listButton").add(new Label("nameList", displayName).setRenderBodyOnly(true)));
            add(new ScaffoldCreateLink("createButton")
                    .add(new Label("nameCreate", displayName).setRenderBodyOnly(true)));
            final PropertyComponentFactory<T> factory = Wicketopia.get().createViewerFactory(beanType);
            final Context context = createContext(Context.VIEW);
            add(new CssBeanViewLayoutPanel<T>("layout", beanType, model, context, factory));
        }
    }

    private final class ViewLink extends AjaxLink<T> {
        private ViewLink(String id, IModel<T> tiModel) {
            super(id, tiModel);
        }

        @Override
        public void onClick(AjaxRequestTarget target) {
            model = getModel();
            mode = ScaffoldMode.View;
            refreshContent(target);
        }
    }
}