org.nuclos.client.ui.collect.searcheditor.SearchConditionTreeNode.java Source code

Java tutorial

Introduction

Here is the source code for org.nuclos.client.ui.collect.searcheditor.SearchConditionTreeNode.java

Source

//Copyright (C) 2010  Novabit Informationssysteme GmbH
//
//This file is part of Nuclos.
//
//Nuclos is free software: you can redistribute it and/or modify
//it under the terms of the GNU Affero General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Nuclos is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU Affero General Public License for more details.
//
//You should have received a copy of the GNU Affero General Public License
//along with Nuclos.  If not, see <http://www.gnu.org/licenses/>.
package org.nuclos.client.ui.collect.searcheditor;

import org.nuclos.client.ui.UIUtils;
import org.nuclos.client.ui.tree.CompositeTreeNodeAction;
import org.nuclos.client.ui.tree.TreeNodeAction;
import org.nuclos.common.collect.collectable.CollectableEntity;
import org.nuclos.common.collect.collectable.CollectableEntityField;
import org.nuclos.common.collect.collectable.CollectableFieldsProviderFactory;
import org.nuclos.common.collect.collectable.searchcondition.AtomicCollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableIdCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableIdListCondition;
import org.nuclos.common.collect.collectable.searchcondition.PivotJoinCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableSelfSubCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableSubCondition;
import org.nuclos.common.collect.collectable.searchcondition.CompositeCollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.LogicalOperator;
import org.nuclos.common.collect.collectable.searchcondition.PlainSubCondition;
import org.nuclos.common.collect.collectable.searchcondition.RefJoinCondition;
import org.nuclos.common.collect.collectable.searchcondition.ReferencingCollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.ToHumanReadablePresentationVisitor;
import org.nuclos.common.collect.collectable.searchcondition.TrueCondition;
import org.nuclos.common.collect.collectable.searchcondition.visit.CompositeVisitor;
import org.nuclos.common.collect.collectable.searchcondition.visit.Visitor;
import org.nuclos.common2.SpringLocaleDelegate;

import java.awt.Component;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.swing.Action;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import org.apache.commons.lang.NullArgumentException;

/**
 * <code>TreeNode</code> that contains a <code>CollectableSearchCondition</code>.
 * <br>
 * <br>Created by Novabit Informationssysteme GmbH
 * <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a>
 *
 * @author   <a href="mailto:Christoph.Radig@novabit.de">Christoph.Radig</a>
 * @version 01.00.00
 */

public abstract class SearchConditionTreeNode extends DefaultMutableTreeNode {

    protected static final String ACTIONCOMMAND_ADD_ATOMICNODE = "ADD ATOMIC NODE";

    /**
     * @param clctcond
     * @precondition clctcond != null
     * @return
     * @postcondition result != null
     */
    public static SearchConditionTreeNode newInstance(CollectableSearchCondition clctcond) {
        if (clctcond == null) {
            throw new NullArgumentException("clctcond");
        }
        final SearchConditionTreeNode result = clctcond.accept(new NewSearchConditionTreeNodeVisitor());

        assert result != null;
        return result;
    }

    public abstract CollectableSearchCondition getSearchCondition();

    @Override
    public Object getUserObject() {
        return this.getSearchCondition();
    }

    @Override
    public String toString() {
        return this.getLabelForCollapsedState();
    }

    public String getLabel(boolean bExpanded) {
        return bExpanded ? this.getLabelForExpandedState() : this.getLabelForCollapsedState();
    }

    protected String getLabelForCollapsedState() {
        return this.getSearchCondition().accept(new ToHumanReadablePresentationVisitor());
    }

    protected abstract String getLabelForExpandedState();

    @Override
    public SearchConditionTreeNode getChildAt(int iIndex) {
        return (SearchConditionTreeNode) super.getChildAt(iIndex);
    }

    /**
     * @param tree the tree where the action is about to take place.
     * @param clcteRoot the <code>CollectableEntity</code> for the whole search condition.
     * @param clctfproviderfactory
     * @return the list of possible <code>TreeNodeAction</code>s for this node.
     * These may be shown in the node's context menu.
     * Separators are shown in the menus for <code>null</code> entries.
     * @postcondition result != null
     */
    public final List<TreeNodeAction> getTreeNodeActions(JTree tree, CollectableEntity clcteRoot,
            CollectableFieldsProviderFactory clctfproviderfactory,
            Collection<CollectableEntityField> additionalFields) {
        final List<TreeNodeAction> result = this._getTreeNodeActions(tree, this.getEntity(clcteRoot),
                clctfproviderfactory, additionalFields);
        assert result != null;
        return result;
    }

    /**
     * @param clcteRoot
     * @return the entity for this treenode
     */
    private CollectableEntity getEntity(CollectableEntity clcteRoot) {
        final CollectableEntity clcteEnclosing = getEnclosingEntity(this);
        return (clcteEnclosing == null) ? clcteRoot : clcteEnclosing;
    }

    /**
     * @param treenode
     * @return the enclosing entity, if any.
     */
    private static CollectableEntity getEnclosingEntity(SearchConditionTreeNode treenode) {
        final CollectableEntity result;
        if (treenode == null) {
            result = null;
        } else if (treenode instanceof SubConditionTreeNode) {
            /** @todo can we encapsulate these getXxxEntity() methods in an interface? */
            result = ((SubConditionTreeNode) treenode).getSubEntity();
        } else if (treenode instanceof ReferencingSearchConditionTreeNode) {
            result = ((ReferencingSearchConditionTreeNode) treenode).getReferencedEntity();
        } else {
            result = getEnclosingEntity((SearchConditionTreeNode) treenode.getParent());
        }
        return result;
    }

    /**
     * @param tree the tree where the action is about to take place.
     * @param clcte   the <code>CollectableEntity</code> for this treenode.
     * @param clctfproviderfactory
     * @return the list of possible <code>TreeNodeAction</code>s for this node.
     * @postcondition result != null
     * These may be shown in the node's context menu.
     * Separators are shown in the menus for <code>null</code> entries.
     */
    protected List<TreeNodeAction> _getTreeNodeActions(JTree tree, CollectableEntity clcte,
            CollectableFieldsProviderFactory clctfproviderfactory,
            Collection<CollectableEntityField> additionalFields) {
        return Collections.emptyList();
    }

    /**
     * There is no default action by default. Subclasses may specify the default action here.
     * @return the action command of the default <code>TreeNodeAction</code> for this node, if any.
     */
    public String getDefaultTreeNodeActionCommand() {
        return null;
    }

    /**
     * finds the default action for this node by searching the tree actions
     * (as specified in <code>getExplorerActions()</code>) for a tree action with the default tree action
     * command (as specified in <code>getDefaultExplorerActionCommand()</code>). Note that this method
     * doesn't have to be overridden in subclasses.
     * @param tree the tree where the action is about to take place.
     * @param clcteRoot the <code>CollectableEntity</code> for the whole search condition.
     * @return the default action for this node, if any.
     */
    public TreeNodeAction getDefaultTreeNodeAction(JTree tree, CollectableEntity clcteRoot,
            CollectableFieldsProviderFactory clctfproviderfactory,
            Collection<CollectableEntityField> additionalFields) {
        TreeNodeAction result = null;

        final String sDefaultTreeActionCommmand = this.getDefaultTreeNodeActionCommand();
        if (sDefaultTreeActionCommmand != null) {
            for (TreeNodeAction action : this.getTreeNodeActions(tree, clcteRoot, clctfproviderfactory,
                    additionalFields)) {
                if (action != null) {
                    if (sDefaultTreeActionCommmand.equals(action.getValue(Action.ACTION_COMMAND_KEY))) {
                        result = action;
                        break;
                    }
                }
            }
        }
        return result;
    }

    protected void refresh(JTree tree) {
        this.refresh((DefaultTreeModel) tree.getModel());
    }

    /**
     * refreshes the current node (and its children) and notifies the given treemodel
     * @param dtm the DefaultTreeModel to notify. Must contain this node.
     */
    protected void refresh(DefaultTreeModel dtm) {
        dtm.nodeStructureChanged(this);
    }

    public void removeFromParentAndRefresh(JTree tree) {
        final SearchConditionTreeNode nodeParent = (SearchConditionTreeNode) SearchConditionTreeNode.this
                .getParent();
        if (parent != null) {
            nodeParent.remove(SearchConditionTreeNode.this);
            nodeParent.refresh(tree);
        }
    }

    /**
     * @param tree
     * @param clcte
     * @return composite actions that contains actions to create the different nodes.
     */
    protected CompositeTreeNodeAction getAddTreeNodeActions(JTree tree, CollectableEntity clcte,
            CollectableFieldsProviderFactory clctfproviderfactory,
            Collection<CollectableEntityField> additionalFields) {
        return new CompositeTreeNodeAction(
                SpringLocaleDelegate.getInstance().getMessage("SearchConditionTreeNode.1", "Hinzuf\u00fcgen"),
                Arrays.asList(new TreeNodeAction[] {
                        new AddAtomicNodeAction(tree, clcte, clctfproviderfactory, additionalFields),
                        TreeNodeAction.newSeparatorAction(), new AddCompositeNodeAction(LogicalOperator.AND, tree),
                        new AddCompositeNodeAction(LogicalOperator.OR, tree),
                        new AddCompositeNodeAction(LogicalOperator.NOT, tree) }));
    }

    /**
     * source actions for drag&drop.
     * @see java.awt.dnd.DnDConstants
     * @return (default: DnDConstants.ACTION_NONE)
     */
    public int getDataTransferSourceActions() {
        return DnDConstants.ACTION_NONE;
    }

    /**
     * @return a <code>Transferable</code> for data transfer
     */
    public final Transferable createTransferable() {
        return this.getSearchCondition();
    }

    /**
     * imports data from a drop or paste operation. The default implementation throws an
     * UnsupportedFlavorException to indicate that drag&drop is not supported by default.
     * @see javax.swing.TransferHandler#importData
     * @param transferable
     * @param tree
     * @return Was the data imported? <code>false</code> may be returned if the drop action requires
     * additional user input, and the user cancels the operation.
     * @throws java.awt.datatransfer.UnsupportedFlavorException
     */
    public boolean importTransferData(Component parent, Transferable transferable, JTree tree)
            throws IOException, UnsupportedFlavorException {
        throw new UnsupportedFlavorException(null);
    }

    protected class RemoveNodeAction extends TreeNodeAction {

        RemoveNodeAction(JTree tree) {
            super(ACTIONCOMMAND_REMOVE,
                    SpringLocaleDelegate.getInstance().getMessage("SearchConditionTreeNode.2", "Entfernen"), tree);
        }

        @Override
        public void actionPerformed(ActionEvent ev) {
            SearchConditionTreeNode.this.removeFromParentAndRefresh(this.getJTree());
        }
    }

    protected class AddCompositeNodeAction extends TreeNodeAction {
        private final LogicalOperator logicalOperator;

        AddCompositeNodeAction(LogicalOperator logicalOperator, JTree tree) {
            super("ADD LOGICAL "
                    + SpringLocaleDelegate.getInstance().getMessage(logicalOperator.getResourceIdForLabel(), null)
                    + " NODE",
                    SpringLocaleDelegate.getInstance().getMessage(logicalOperator.getResourceIdForDescription(),
                            null),
                    tree);
            this.logicalOperator = logicalOperator;
        }

        public LogicalOperator getLogicalOperator() {
            return this.logicalOperator;
        }

        @Override
        public void actionPerformed(ActionEvent ev) {
            add(new CompositeSearchConditionTreeNode(
                    new CompositeCollectableSearchCondition(this.logicalOperator)));
            refresh(this.getJTree());
        }
    }

    protected class AddAtomicNodeAction extends TreeNodeAction {

        private final CollectableEntity clcte;
        private final CollectableFieldsProviderFactory clctfproviderfactory;
        private final Collection<CollectableEntityField> additionalFields;

        AddAtomicNodeAction(JTree tree, CollectableEntity clcte,
                CollectableFieldsProviderFactory clctfproviderfactory,
                Collection<CollectableEntityField> additionalFields) {
            super(ACTIONCOMMAND_ADD_ATOMICNODE, SpringLocaleDelegate.getInstance()
                    .getMessage("SearchConditionTreeNode.3", "Einfache Bedingung"), tree);
            this.clcte = clcte;
            this.clctfproviderfactory = clctfproviderfactory;
            this.additionalFields = additionalFields;
        }

        @Override
        public void actionPerformed(ActionEvent ev) {
            /** @todo parent == tree? */
            final Component parent = this.getJTree();
            UIUtils.runCommand(parent, new Runnable() {
                @Override
                public void run() {
                    new AtomicNodeController(parent, getJTree(), clcte, clctfproviderfactory, additionalFields)
                            .runAdd(SearchConditionTreeNode.this);
                }
            });
        }
    }

    /**
     * Visitor that creates a new <code>SearchConditionTreeNode</code> out of a <code>CollectableSearchCondition</code>.
     */
    private static class NewSearchConditionTreeNodeVisitor
            implements Visitor<SearchConditionTreeNode, RuntimeException>,
            CompositeVisitor<SearchConditionTreeNode, RuntimeException> {
        @Override
        public AtomicSearchConditionTreeNode visitAtomicCondition(AtomicCollectableSearchCondition atomiccond) {
            return new AtomicSearchConditionTreeNode(atomiccond);
        }

        @Override
        public CompositeSearchConditionTreeNode visitCompositeCondition(
                CompositeCollectableSearchCondition compositecond) {
            return new CompositeSearchConditionTreeNode(compositecond);
        }

        @Override
        public IdConditionTreeNode visitIdCondition(CollectableIdCondition idcond) {
            return new IdConditionTreeNode(idcond);
        }

        @Override
        public ReferencingSearchConditionTreeNode visitReferencingCondition(
                ReferencingCollectableSearchCondition refcond) {
            return new ReferencingSearchConditionTreeNode(refcond);
        }

        @Override
        public SubConditionTreeNode visitSubCondition(CollectableSubCondition subcond) {
            return new SubConditionTreeNode(subcond);
        }

        @Override
        public SubConditionTreeNode visitPivotJoinCondition(PivotJoinCondition subcond) {
            throw new UnsupportedOperationException();
        }

        @Override
        public SubConditionTreeNode visitRefJoinCondition(RefJoinCondition subcond) {
            throw new UnsupportedOperationException();
        }

        @Override
        public SearchConditionTreeNode visitSelfSubCondition(CollectableSelfSubCondition subcond) {
            return subcond.getSubCondition().accept(new NewSearchConditionTreeNodeVisitor());
        }

        @Override
        public SearchConditionTreeNode visitPlainSubCondition(PlainSubCondition subcond) {
            return new PlainSubSearchConditionTreeNode(subcond);
        }

        /**
         * @param truecond
         * @throws IllegalArgumentException
         */
        @Override
        public SearchConditionTreeNode visitTrueCondition(TrueCondition truecond) {
            throw new IllegalArgumentException("truecond");
        }

        @Override
        public SearchConditionTreeNode visitIdListCondition(CollectableIdListCondition collectableIdListCondition)
                throws RuntimeException {
            throw new IllegalArgumentException("collectableIdListCondition");
        }
    }

} // class SearchConditionTreeNode