ontopoly.components.FieldInstanceAssociationBinaryPanel.java Source code

Java tutorial

Introduction

Here is the source code for ontopoly.components.FieldInstanceAssociationBinaryPanel.java

Source

/*
 * #!
 * Ontopoly Editor
 * #-
 * Copyright (C) 2001 - 2013 The Ontopia Project
 * #-
 * 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 ontopoly.components;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import net.ontopia.utils.ObjectUtils;
import ontopoly.LockManager;
import ontopoly.OntopolySession;
import ontopoly.jquery.DraggableBehavior;
import ontopoly.jquery.DroppableBehavior;
import ontopoly.model.CreateAction;
import ontopoly.model.EditMode;
import ontopoly.model.FieldAssignment;
import ontopoly.model.FieldInstance;
import ontopoly.model.InterfaceControl;
import ontopoly.model.RoleField;
import ontopoly.model.Topic;
import ontopoly.model.TopicMap;
import ontopoly.models.FieldDefinitionModel;
import ontopoly.models.FieldInstanceModel;
import ontopoly.models.FieldValueModel;
import ontopoly.models.FieldValuesModel;
import ontopoly.models.FieldsViewModel;
import ontopoly.models.RoleFieldModel;
import ontopoly.models.TopicModel;
import ontopoly.pages.AbstractOntopolyPage;
import ontopoly.pages.ModalFindPage;
import ontopoly.utils.RoleFieldValueComparator;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.PageParameters;
import org.apache.wicket.Session;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;

public class FieldInstanceAssociationBinaryPanel extends AbstractFieldInstancePanel {

    protected final ConfirmDeletePanel confirmDeletePanel;
    protected final RoleFieldModel ofieldModel;
    protected final TopicModel<Topic> topicModel;
    protected final RoleFieldModel roleFieldModel;

    public FieldInstanceAssociationBinaryPanel(String id, final FieldInstanceModel fieldInstanceModel,
            final FieldsViewModel fieldsViewModel, final boolean readonlyField, final boolean traversable) {
        this(id, fieldInstanceModel, fieldsViewModel, readonlyField, false, traversable);
    }

    protected FieldInstanceAssociationBinaryPanel(String id, final FieldInstanceModel fieldInstanceModel,
            final FieldsViewModel fieldsViewModel, final boolean readonlyField, final boolean embedded,
            final boolean traversable) {
        super(id, fieldInstanceModel);

        FieldInstance fieldInstance = fieldInstanceModel.getFieldInstance();
        FieldAssignment fieldAssignment = fieldInstance.getFieldAssignment();
        RoleField roleField = (RoleField) fieldAssignment.getFieldDefinition();
        this.roleFieldModel = new RoleFieldModel(roleField);

        add(new FieldDefinitionLabel("fieldLabel", new FieldDefinitionModel(roleField)));

        // set up container
        this.fieldValuesContainer = new WebMarkupContainer("fieldValuesContainer");
        fieldValuesContainer.setOutputMarkupId(true);
        add(fieldValuesContainer);

        // add feedback panel
        this.feedbackPanel = new FeedbackPanel("feedback", new AbstractFieldInstancePanelFeedbackMessageFilter());
        feedbackPanel.setOutputMarkupId(true);
        fieldValuesContainer.add(feedbackPanel);

        this.confirmDeletePanel = new ConfirmDeletePanel("confirm", fieldValuesContainer) {
            @Override
            protected void onDeleteTopic(AjaxRequestTarget target) {
                super.onDeleteTopic(target);
                FieldInstanceAssociationBinaryPanel.this.onUpdate(target);
            }
        };
        confirmDeletePanel.setOutputMarkupId(true);
        fieldValuesContainer.add(confirmDeletePanel);

        RoleField ofield = (RoleField) roleField.getFieldsForOtherRoles().iterator().next();
        this.ofieldModel = new RoleFieldModel(ofield);
        this.topicModel = new TopicModel<Topic>(fieldInstance.getInstance());

        InterfaceControl interfaceControl = ofield.getInterfaceControl();

        WebMarkupContainer fieldValuesList = new WebMarkupContainer("fieldValuesList");
        fieldValuesContainer.add(fieldValuesList);

        final String fieldDefinitionId = roleField.getId();

        EditMode editMode = roleField.getEditMode();

        final boolean ownedvalues = editMode.isOwnedValues();
        final boolean allowAdd = !(ownedvalues || editMode.isNewValuesOnly() || editMode.isNoEdit());
        final boolean allowCreate = !(editMode.isExistingValuesOnly() || editMode.isNoEdit());
        final boolean allowRemove = !editMode.isNoEdit();
        final boolean sortable = roleField.isSortable();

        // add field values component(s)
        // TODO: consider moving ordering logic into object model
        if (sortable) {
            // HACK: retrieving values ourselves so that we can get them ordered
            this.fieldValuesModel = new FieldValuesModel(fieldInstanceModel) {
                @Override
                protected List<RoleField.ValueIF> getValues(FieldInstance fieldInstance) {
                    Topic instance = fieldInstance.getInstance();
                    RoleField roleField = (RoleField) fieldInstance.getFieldAssignment().getFieldDefinition();
                    return roleField.getOrderedValues(instance, ofieldModel.getRoleField());
                }
            };
        } else {
            Comparator<Object> comparator = new RoleFieldValueComparator(topicModel, ofieldModel);
            this.fieldValuesModel = new FieldValuesModel(fieldInstanceModel, comparator);
        }

        this.listView = new ListView<FieldValueModel>("fieldValues", fieldValuesModel) {
            @Override
            protected void onBeforeRender() {
                validateCardinality();
                super.onBeforeRender();
            }

            public void populateItem(final ListItem<FieldValueModel> item) {
                FieldValueModel fieldValueModel = item.getModelObject();

                // get topic
                Topic oplayer = null;
                if (fieldValueModel.isExistingValue()) {
                    RoleField.ValueIF valueIf = (RoleField.ValueIF) fieldValueModel.getObject();
                    RoleField ofield = ofieldModel.getRoleField();
                    oplayer = valueIf.getPlayer(ofield, fieldInstanceModel.getFieldInstance().getInstance());
                }
                final String topicMapId = (oplayer == null ? null : oplayer.getTopicMap().getId());
                final String topicId = (oplayer == null ? null : oplayer.getId());

                // acquire lock for embedded topic
                final boolean isLockedByOther;
                if (embedded && fieldValueModel.isExistingValue()) {
                    OntopolySession session = (OntopolySession) Session.get();
                    String lockerId = session.getLockerId(getRequest());
                    LockManager.Lock lock = session.lock(oplayer, lockerId);
                    isLockedByOther = !lock.ownedBy(lockerId);
                } else {
                    isLockedByOther = false;
                }
                final boolean readonly = readonlyField || isLockedByOther;

                boolean itemSortable = !readonly && sortable && fieldValueModel.isExistingValue();
                if (itemSortable) {
                    item.add(new DroppableBehavior(fieldDefinitionId) {
                        @Override
                        protected MarkupContainer getDropContainer() {
                            return listView;
                        }

                        @Override
                        protected void onDrop(Component component, AjaxRequestTarget target) {
                            FieldValueModel fvm_dg = (FieldValueModel) component.getDefaultModelObject();
                            FieldValueModel fvm_do = (FieldValueModel) getComponent().getDefaultModelObject();
                            RoleField.ValueIF rfv_dg = (RoleField.ValueIF) fvm_dg.getFieldValue();
                            RoleField.ValueIF rfv_do = (RoleField.ValueIF) fvm_do.getFieldValue();

                            Topic topic = topicModel.getTopic();
                            RoleField rfield = roleFieldModel.getRoleField();
                            RoleField ofield = ofieldModel.getRoleField();
                            rfield.moveAfter(topic, ofield, rfv_dg, rfv_do);
                            getModel().detach(); // FIXME: better if we could just tweak model directly without detaching
                            listView.removeAll();
                            target.addComponent(fieldValuesContainer);
                        }
                    });
                    item.add(new DraggableBehavior(fieldDefinitionId));
                }

                item.setOutputMarkupId(true);
                WebMarkupContainer fieldIconContainer = new WebMarkupContainer("fieldIconContainer");
                fieldIconContainer
                        .add(new OntopolyImage("fieldIcon", "dnd.gif", new ResourceModel("icon.dnd.reorder")));
                fieldIconContainer.setVisible(itemSortable);
                item.add(fieldIconContainer);

                final WebMarkupContainer fieldValueButtons = new WebMarkupContainer("fieldValueButtons");
                fieldValueButtons.setOutputMarkupId(true);
                item.add(fieldValueButtons);

                FieldInstanceRemoveButton removeButton = new FieldInstanceRemoveButton("remove", "remove-value.gif",
                        fieldValueModel) {
                    @Override
                    public boolean isVisible() {
                        boolean visible = !readonly && fieldValueModel.isExistingValue() && allowRemove; // && !isValueProtected;
                        if (visible) {
                            // filter by player
                            AbstractOntopolyPage page = (AbstractOntopolyPage) getPage();
                            RoleField.ValueIF value = (RoleField.ValueIF) fieldValueModel.getObject();
                            Topic[] players = value.getPlayers();
                            for (int i = 0; i < players.length; i++) {
                                if (!page.filterTopic(players[i]))
                                    return false;
                            }
                        }
                        return visible;
                    }

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        // FIXME: could reuse some of these variable from above
                        FieldInstance fieldInstance = fieldValueModel.getFieldInstanceModel().getFieldInstance();
                        Object value = fieldValueModel.getObject();

                        Topic currentTopic = fieldInstance.getInstance();

                        RoleField currentField = (RoleField) fieldInstance.getFieldAssignment()
                                .getFieldDefinition();
                        RoleField selectedField = ofieldModel.getRoleField();

                        RoleField.ValueIF valueIf = (RoleField.ValueIF) value;
                        Topic selectedTopic = valueIf.getPlayer(selectedField, fieldInstance.getInstance());

                        // check with page to see if add is allowed
                        boolean changesMade = false;
                        AbstractOntopolyPage page = (AbstractOntopolyPage) getPage();
                        if (page.isRemoveAllowed(currentTopic, currentField, selectedTopic, selectedField)) {
                            if (ownedvalues) {

                                // don't remove system topics
                                if (!selectedTopic.isSystemTopic()) {
                                    FieldInstanceAssociationBinaryPanel.this.confirmDeletePanel
                                            .setTopic(selectedTopic);
                                    changesMade = true;
                                }
                            } else {
                                fieldInstance.removeValue(value, page.getListener());
                                changesMade = true;
                            }
                        }
                        // notify association panel so that it can update itself
                        if (changesMade)
                            FieldInstanceAssociationBinaryPanel.this.onUpdate(target);
                    }
                };
                fieldValueButtons.add(removeButton);

                // embedded goto button
                OntopolyImageLink gotoButton = new OntopolyImageLink("goto", "goto.gif",
                        new ResourceModel("icon.goto.topic")) {
                    @Override
                    public boolean isVisible() {
                        FieldValueModel fieldValueModel = item.getModelObject();
                        return embedded && fieldValueModel.isExistingValue();
                    }

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        // navigate to topic
                        PageParameters pageParameters = new PageParameters();
                        pageParameters.put("topicMapId", topicMapId);
                        pageParameters.put("topicId", topicId);
                        setResponsePage(getPage().getClass(), pageParameters);
                        setRedirect(true);
                    }
                };
                fieldValueButtons.add(gotoButton);

                // embedded lock button
                OntopolyImageLink lockButton = new OntopolyImageLink("lock", "lock.gif",
                        new ResourceModel("icon.topic.locked")) {
                    @Override
                    public boolean isVisible() {
                        return embedded && isLockedByOther;
                    }

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                    }
                };
                fieldValueButtons.add(lockButton);

                // binary
                // ISSUE: should not really pass in readonly-parameter here as it is only relevant if page is readonly
                FieldInstanceAssociationBinaryField binaryField = new FieldInstanceAssociationBinaryField(
                        "fieldValue", ofieldModel, fieldValueModel, fieldsViewModel, readonly, embedded,
                        traversable, allowAdd) {
                    @Override
                    protected void performNewSelection(FieldValueModel fieldValueModel, RoleField selectedField,
                            Topic selectedTopic) {
                        RoleField.ValueIF value = FieldInstanceAssociationBinaryPanel.this
                                .performNewSelection(selectedField, selectedTopic);
                        fieldValueModel.setExistingValue(value);
                    }
                };
                if (binaryField.getUpdateableComponent() != null)
                    binaryField.getUpdateableComponent().add(new FieldUpdatingBehaviour(true));
                item.add(binaryField);

                addNewFieldValueCssClass(item, fieldValuesModel, fieldValueModel);
            }
        };
        listView.setReuseItems(true);
        fieldValuesList.add(listView);

        // figure out which buttons to show
        this.fieldInstanceButtons = new WebMarkupContainer("fieldInstanceButtons");
        fieldInstanceButtons.setOutputMarkupId(true);
        add(fieldInstanceButtons);

        if (readonlyField || !allowAdd) {
            // unused components
            fieldInstanceButtons.add(new Label("add", new Model<String>("unused")).setVisible(false));
            fieldInstanceButtons.add(new Label("find", new Model<String>("unused")).setVisible(false));
            fieldInstanceButtons.add(new Label("findModal", new Model<String>("unused")).setVisible(false));

            // add/find button
        } else if (interfaceControl.isDropDownList() || interfaceControl.isAutoComplete()) {
            // "add" button
            OntopolyImageLink addButton = new OntopolyImageLink("add", "add.gif") {
                @Override
                public void onClick(AjaxRequestTarget target) {
                    boolean showExtraField = !fieldValuesModel.getShowExtraField();
                    fieldValuesModel.setShowExtraField(showExtraField, true);
                    listView.removeAll();
                    updateDependentComponents(target);
                }

                @Override
                public boolean isVisible() {
                    if (readonlyField)
                        return false;
                    else
                        return fieldValuesModel.containsExisting();
                }

                @Override
                public String getImage() {
                    return fieldValuesModel.getShowExtraField() ? "remove.gif" : "add.gif";
                }

                @Override
                public IModel<String> getTitleModel() {
                    return new ResourceModel(
                            fieldValuesModel.getShowExtraField() ? "icon.remove.hide-field" : "icon.add.add-value");
                }
            };
            fieldInstanceButtons.add(addButton);

            // unused components
            fieldInstanceButtons.add(new Label("find", new Model<String>("unused")).setVisible(false));
            fieldInstanceButtons.add(new Label("findModal", new Model<String>("unused")).setVisible(false));

        } else if (interfaceControl.isSearchDialog() || interfaceControl.isBrowseDialog()) {

            // "search"/"browse" button
            final ModalWindow findModal = new ModalWindow("findModal");
            fieldInstanceButtons.add(findModal);

            int activeTab = (interfaceControl.isSearchDialog() ? ModalFindPage.ACTIVE_TAB_SEARCH
                    : ModalFindPage.ACTIVE_TAB_BROWSE);

            findModal
                    .setContent(new ModalFindPage<String>(findModal.getContentId(), fieldInstanceModel, activeTab) {
                        @Override
                        protected void onSelectionConfirmed(AjaxRequestTarget target, Collection<String> selected) {
                            FieldInstance fieldInstance = fieldInstanceModel.getFieldInstance();
                            RoleField currentField = (RoleField) fieldInstance.getFieldAssignment()
                                    .getFieldDefinition();
                            RoleField selectedField = (RoleField) currentField.getFieldsForOtherRoles().iterator()
                                    .next();

                            // check with page to see if add is allowed
                            if (ObjectUtils.different(currentField, selectedField) ||
                            // if assoc type is symmetric currentField == selectedField,
                            // but we're still OK to go ahead, so checking for that
                            // (this is issue 457)
                            currentField.getAssociationType().isSymmetric()) {

                                Topic currentTopic = fieldInstance.getInstance();
                                TopicMap topicMap = currentTopic.getTopicMap();

                                boolean changesMade = false;
                                Iterator<String> iter = selected.iterator();
                                while (iter.hasNext()) {
                                    String objectId = (String) iter.next();

                                    AbstractOntopolyPage page = (AbstractOntopolyPage) getPage();
                                    Topic selectedTopic = topicMap.getTopicById(objectId);

                                    if (page.isAddAllowed(currentTopic, currentField, selectedTopic,
                                            selectedField)) {
                                        performNewSelection(selectedField, selectedTopic);
                                        changesMade = true;
                                    }
                                }
                                // notify association panel so that it can update itself
                                if (changesMade)
                                    FieldInstanceAssociationBinaryPanel.this.onUpdate(target);
                            }
                        }

                        @Override
                        protected void onCloseCancel(AjaxRequestTarget target) {
                            findModal.close(target);
                        }

                        @Override
                        protected void onCloseOk(AjaxRequestTarget target) {
                            findModal.close(target);
                        }
                    });
            findModal.setTitle(new ResourceModel("ModalWindow.title.find.topic").getObject().toString());
            findModal.setCookieName("findModal");

            OntopolyImageLink findButton = new OntopolyImageLink("find", "search.gif",
                    new ResourceModel("find.topic")) {
                @Override
                public void onClick(AjaxRequestTarget target) {
                    findModal.show(target);
                }
            };
            fieldInstanceButtons.add(findButton);
            // unused components
            fieldInstanceButtons.add(new Label("add", new Model<String>("unused")).setVisible(false));
        } else {
            throw new RuntimeException("Unsupported interface control: " + interfaceControl);
        }

        // create button
        if (readonlyField || !allowCreate) {
            fieldInstanceButtons.add(new Label("create").setVisible(false));
        } else {

            CreateAction ca = roleField.getCreateAction();
            int createAction;
            if (embedded || ca.isNone())
                createAction = FieldInstanceCreatePlayerPanel.CREATE_ACTION_NONE;
            else if (ca.isNavigate())
                createAction = FieldInstanceCreatePlayerPanel.CREATE_ACTION_NAVIGATE;
            else
                createAction = FieldInstanceCreatePlayerPanel.CREATE_ACTION_POPUP;

            FieldInstanceCreatePlayerPanel createPanel = new FieldInstanceCreatePlayerPanel("create",
                    fieldInstanceModel, fieldsViewModel, new RoleFieldModel(ofield), this, createAction) {
                @Override
                protected void performNewSelection(RoleFieldModel ofieldModel, Topic selectedTopic) {
                    FieldInstanceAssociationBinaryPanel.this.performNewSelection(ofieldModel.getRoleField(),
                            selectedTopic);
                }

            };
            createPanel.setOutputMarkupId(true);
            fieldInstanceButtons.add(createPanel);
        }
    }

    protected RoleField.ValueIF performNewSelection(RoleField selectedField, Topic selectedTopic) {
        FieldInstance fieldInstance = fieldInstanceModel.getFieldInstance();
        RoleField currentField = (RoleField) fieldInstance.getFieldAssignment().getFieldDefinition();
        Topic currentTopic = fieldInstance.getInstance();
        RoleField.ValueIF value = RoleField.createValue(2);
        value.addPlayer(currentField, currentTopic);
        value.addPlayer(selectedField, selectedTopic);
        AbstractOntopolyPage page = (AbstractOntopolyPage) getPage();
        fieldInstance.addValue(value, page.getListener()); // currentField.addValue(fieldInstance, value, page.getListener());
        return value;
    }

    @Override
    public void onDetach() {
        ofieldModel.detach();
        topicModel.detach();
        roleFieldModel.detach();
        super.onDetach();
    }

}