Package org.apache.isis.viewer.wicket.ui.components.widgets.entitylink

Source Code of org.apache.isis.viewer.wicket.ui.components.widgets.entitylink.EntityLink

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you 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.apache.isis.viewer.wicket.ui.components.widgets.entitylink;

import java.util.List;

import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.facets.object.icon.IconFacet;
import org.apache.isis.core.metamodel.spec.ActionType;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.runtimes.dflt.runtime.system.DeploymentType;
import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
import org.apache.isis.viewer.wicket.model.models.ActionModel;
import org.apache.isis.viewer.wicket.model.models.EntityModel;
import org.apache.isis.viewer.wicket.model.models.ScalarModel;
import org.apache.isis.viewer.wicket.model.util.Generics;
import org.apache.isis.viewer.wicket.model.util.Mementos;
import org.apache.isis.viewer.wicket.ui.ComponentType;
import org.apache.isis.viewer.wicket.ui.components.actions.ActionInvokeHandler;
import org.apache.isis.viewer.wicket.ui.components.actions.ActionPanel;
import org.apache.isis.viewer.wicket.ui.components.entity.combined.EntityCombinedPanel;
import org.apache.isis.viewer.wicket.ui.components.widgets.cssmenu.CssMenuBuilder;
import org.apache.isis.viewer.wicket.ui.components.widgets.cssmenu.CssMenuPanel;
import org.apache.isis.viewer.wicket.ui.components.widgets.dropdownchoices.DropDownChoicesForObjectAdapterMementos;
import org.apache.isis.viewer.wicket.ui.components.widgets.formcomponent.CancelHintRequired;
import org.apache.isis.viewer.wicket.ui.components.widgets.formcomponent.FormComponentPanelAbstract;
import org.apache.isis.viewer.wicket.ui.pages.PageType;
import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.extensions.yui.calendar.DateField;
import org.apache.wicket.markup.html.PackageResource;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.FormComponentPanel;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

import com.google.common.collect.Lists;

/**
* {@link FormComponentPanel} representing a reference to an entity: a link and a findUsing button.
*/
public class EntityLink extends FormComponentPanelAbstract<ObjectAdapter> implements CancelHintRequired,
    ActionInvokeHandler {

    private static final long serialVersionUID = 1L;

    private static final String ID_ENTITY_LINK_WRAPPER = "entityLinkWrapper";
    private static final String ID_ENTITY_LINK = "entityLink";
    private static final String ID_ENTITY_OID = "entityOid";
    private static final String ID_ENTITY_TITLE = "entityTitle";
    private static final String ID_ENTITY_TITLE_NULL = "entityTitleNull";
    private static final String ID_ENTITY_IMAGE = "entityImage";
    private static final String ID_CHOICES = "choices";
    private static final String ID_FIND_USING = "findUsing";
    private static final String ID_ENTITY_DETAILS_LINK = "entityDetailsLink";
    private static final String ID_ENTITY_DETAILS_LINK_LABEL = "entityDetailsLinkLabel";
    private static final String ID_ENTITY_DETAILS = "entityDetails";

    private final FindUsingLinkFactory linkFactory;

    private TextField<ObjectAdapterMemento> entityOidField;
    private WebMarkupContainer findUsing;
    private PanelAbstract<?> actionFindUsingComponent;

    private Image image;
    private Label label;
    private ObjectAdapterMemento pending;

    public EntityLink(final String id, final EntityModel entityModel) {
        super(id, entityModel);
        setType(ObjectAdapter.class);
        linkFactory = new FindUsingLinkFactory(this);
        buildGui();
    }

    public EntityModel getEntityModel() {
        return (EntityModel) getModel();
    }

    /**
     * Builds the parts of the GUI that are not dynamic.
     */
    private void buildGui() {
        addOrReplaceOidField();
        rebuildFindUsingMenu();
        syncWithInput();
    }

    private void addOrReplaceOidField() {
        entityOidField = new TextField<ObjectAdapterMemento>(ID_ENTITY_OID, new Model<ObjectAdapterMemento>() {

            private static final long serialVersionUID = 1L;

            @Override
            public ObjectAdapterMemento getObject() {
                if (pending != null) {
                    return pending;
                }
                final ObjectAdapter adapter = EntityLink.this.getModelObject();
                return ObjectAdapterMemento.createOrNull(adapter);
            }

            @Override
            public void setObject(final ObjectAdapterMemento adapterMemento) {
                pending = adapterMemento;
            }

        }) {
            private static final long serialVersionUID = 1L;

            @Override
            protected void onModelChanged() {
                super.onModelChanged();
                syncWithInput();
            }

        };
        entityOidField.setType(ObjectAdapterMemento.class);
        addOrReplace(entityOidField);
        entityOidField.setVisible(false);
    }

    void rebuildFindUsingMenu() {
        final EntityModel entityModel = getEntityModel();
        final List<ObjectAction> actions = findServiceActionsFor(entityModel.getTypeOfSpecification());
        findUsing = new WebMarkupContainer(ID_FIND_USING);
        switch (actions.size()) {
            case 0:
                permanentlyHide(findUsing, ComponentType.ACTION);
                break;
            default:
                // TODO: i18n

                final CssMenuBuilder cssMenuBuilder =
                    new CssMenuBuilder(null, getServiceAdapters(), actions, linkFactory);
                final CssMenuPanel cssMenuPanel =
                    cssMenuBuilder.buildPanel(ComponentType.ACTION.getWicketId(), "find using...");

                findUsing.addOrReplace(cssMenuPanel);
                actionFindUsingComponent = cssMenuPanel;
                break;
        }
        addOrReplace(findUsing);
    }

    /**
     * Must be called after {@link #setEnabled(boolean)} to ensure that the <tt>findUsing</tt> button is shown/not shown
     * as required.
     *
     * <p>
     * REVIEW: there ought to be a better way to do this. I'd hoped to override {@link #setEnabled(boolean)}, but it is
     * <tt>final</tt>, and there doesn't seem to be anyway to install a listener. One option might be to move it to
     * {@link #onBeforeRender()} ?
     */
    public void syncFindUsingVisibility() {
        findUsing.setVisible(isEnabled() && !getEntityModel().isViewMode());
    }

    /**
     * Since we override {@link #convertInput()}, it is (apparently) enough to just return a value that is suitable for
     * error reporting.
     *
     * @see DateField#getInput() for reference
     */
    @Override
    public String getInput() {
        return entityOidField.getInput();
    }

    /**
     * Ensures that the link is always enabled and traversable, even if (in the context of an entity property form) the
     * entity model is in view mode.
     *
     * <p>
     * A slight hack, but works...
     */
    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    protected void convertInput() {
        final ObjectAdapter pendingAdapter = getPendingAdapter();
        setConvertedInput(pendingAdapter);
    }

    private ObjectAdapter getPendingAdapter() {
        final ObjectAdapterMemento memento = entityOidField.getModelObject();
        return memento != null ? memento.getObjectAdapter() : null;
    }

    @Override
    protected void onBeforeRender() {
        syncWithInput();
        super.onBeforeRender();
    }

    private void syncWithInput() {
        final EntityModel entityModel = getEntityModel();

        final ObjectAdapter adapter = Generics.coalesce(getPendingAdapter(), entityModel.getObject());

        syncImageWithInput(adapter);

        final IModel<List<? extends ObjectAdapterMemento>> choicesMementos = getChoicesModel();
        if (choicesMementos != null) {

            // choices drop-down
            final IModel<ObjectAdapterMemento> modelObject = entityOidField.getModel();
            addOrReplace(new DropDownChoicesForObjectAdapterMementos(ID_CHOICES, modelObject, choicesMementos));

            // no need for link, since can see in drop-down
            permanentlyHide(ID_ENTITY_LINK_WRAPPER);

            // no need for the 'null' title, since if there is no object yet
            // can represent this fact in the drop-down
            permanentlyHide(ID_ENTITY_TITLE_NULL);
        } else {

            // choices drop-down
            permanentlyHide(ID_CHOICES);

            // show link if have value
            syncLinkWithInput(adapter);

            // represent no object by a simple label displaying '(null)'
            syncEntityTitleNullWithInput(adapter);
        }

        // link
        syncEntityDetailsButtonWithInput(adapter);

        syncEntityDetailsWithInput(adapter);
        syncFindUsingVisibility();
    }

    private void syncImageWithInput(final ObjectAdapter adapter) {
        if (adapter != null) {
            addOrReplaceImage(adapter);
        } else {
            permanentlyHide(ID_ENTITY_IMAGE);
        }
    }

    private void syncEntityDetailsButtonWithInput(final ObjectAdapter adapter) {
        if (adapter != null && getEntityModel().isEditMode()) {
            final Link<String> link = new Link<String>(ID_ENTITY_DETAILS_LINK) {
                private static final long serialVersionUID = 1L;

                @Override
                public void onClick() {
                    getEntityModel().toggleDetails();
                }
            };
            addOrReplace(link);
            link.add(new Label(ID_ENTITY_DETAILS_LINK_LABEL, buildEntityDetailsModel()));
        } else {
            permanentlyHide(ID_ENTITY_DETAILS_LINK);
        }
    }

    private Model<String> buildEntityDetailsModel() {
        final String label = getEntityModel().isEntityDetailsVisible() ? "hide" : "show";
        return Model.of(label);
    }

    private void syncLinkWithInput(final ObjectAdapter adapter) {
        if (adapter != null) {
            addOrReplaceLink(adapter);
        } else {
            permanentlyHide(ID_ENTITY_LINK_WRAPPER);
        }
    }

    private void syncEntityTitleNullWithInput(final ObjectAdapter adapter) {
        if (adapter != null) {
            permanentlyHide(ID_ENTITY_TITLE_NULL);
        } else {
            addOrReplace(new Label(ID_ENTITY_TITLE_NULL, "(null)"));
        }
    }

    private void syncEntityDetailsWithInput(final ObjectAdapter adapter) {
        if (adapter != null && getEntityModel().isEntityDetailsVisible()) {
            final EntityModel entityModel = new EntityModel(adapter);
            addOrReplace(new EntityCombinedPanel(ID_ENTITY_DETAILS, entityModel));
        } else {
            permanentlyHide(ID_ENTITY_DETAILS);
        }
    }

    private IModel<List<? extends ObjectAdapterMemento>> getChoicesModel() {
        final EntityModel entityModel = getEntityModel();
        if (entityModel instanceof ScalarModel) {
            final ScalarModel scalarModel = (ScalarModel) entityModel;
            final List<ObjectAdapter> choices = scalarModel.getChoices();
            if (choices.size() == 0) {
                return null;
            }
            // take a copy otherwise is only lazily evaluated
            final List<ObjectAdapterMemento> choicesMementos =
                Lists.newArrayList(Lists.transform(choices, Mementos.fromAdapter()));
            return Model.ofList(choicesMementos);
        }
        return null;
    }

    private void addOrReplaceImage(final ObjectAdapter adapter) {
        final PackageResource imageResource = determineImageResource(adapter);

        if (imageResource != null) {
            image = new Image(ID_ENTITY_IMAGE, imageResource);
            addOrReplace(image);
        } else {
            permanentlyHide(ID_ENTITY_IMAGE);
        }
    }

    private PackageResource determineImageResource(final ObjectAdapter adapter) {
        ObjectSpecification typeOfSpec;
        PackageResource imageResource = null;
        if (adapter != null) {
            typeOfSpec = adapter.getSpecification();
            final IconFacet iconFacet = typeOfSpec.getFacet(IconFacet.class);
            if (iconFacet != null) {
                final String iconName = iconFacet.iconName(adapter);
                imageResource = getImageCache().findImage(iconName);
            }
        }
        if (imageResource == null) {
            typeOfSpec = getEntityModel().getTypeOfSpecification();
            imageResource = getImageCache().findImage(typeOfSpec);
        }
        return imageResource;
    }

    private void addOrReplaceLink(final ObjectAdapter adapter) {
        final PageParameters pageParameters = EntityModel.createPageParameters(adapter, getOidStringifier());
        final Class<? extends Page> pageClass = getPageClassRegistry().getPageClass(PageType.ENTITY);
        final BookmarkablePageLink<EntityPage> link =
            new BookmarkablePageLink<EntityPage>(ID_ENTITY_LINK, pageClass, pageParameters);
        label = new Label(ID_ENTITY_TITLE, adapter.titleString());
        link.add(label);
        final WebMarkupContainer entityLinkWrapper = new WebMarkupContainer(ID_ENTITY_LINK_WRAPPER);
        entityLinkWrapper.addOrReplace(link);

        entityLinkWrapper.setEnabled(true);

        addOrReplace(entityLinkWrapper);
    }

    private static List<ObjectAction> findServiceActionsFor(final ObjectSpecification scalarTypeSpec) {
        final List<ObjectAction> actionList = Lists.newArrayList();
        addServiceActionsFor(scalarTypeSpec, ActionType.USER, actionList);
        if (IsisContext.getDeploymentType() == DeploymentType.EXPLORATION) {
            addServiceActionsFor(scalarTypeSpec, ActionType.EXPLORATION, actionList);
        }
        return actionList;
    }

    private static void addServiceActionsFor(final ObjectSpecification noSpec, final ActionType actionType,
        final List<ObjectAction> actionList) {
        final List<ObjectAction> serviceActionsFor = noSpec.getServiceActionsReturning(actionType);
        actionList.addAll(serviceActionsFor);
    }

    @Override
    public void onClick(final ActionModel actionModel) {
        final ActionPanel actionPanel =
            new ActionPanel(actionFindUsingComponent.getComponentType().toString(), actionModel);
        actionFindUsingComponent.replaceWith(actionPanel);
    }

    public void onSelected(final ObjectAdapter selectedAdapter) {
        final ObjectAdapterMemento selectedAdapterMemento = ObjectAdapterMemento.createOrNull(selectedAdapter);
        onSelected(selectedAdapterMemento);
    }

    private void onSelected(final ObjectAdapterMemento selectedAdapterMemento) {
        entityOidField.setDefaultModelObject(selectedAdapterMemento);
        rebuildFindUsingMenu();
        renderSamePage();
    }

    public void onNoResults() {
        rebuildFindUsingMenu();
        renderSamePage();
    }

    @Override
    public void onCancel() {
        entityOidField.clearInput();
        this.pending = null;
    }

    private void renderSamePage() {
        setResponsePage(getPage());
    }
}
TOP

Related Classes of org.apache.isis.viewer.wicket.ui.components.widgets.entitylink.EntityLink

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.