de.iteratec.iteraplan.presentation.memory.EntityTreeNode.java Source code

Java tutorial

Introduction

Here is the source code for de.iteratec.iteraplan.presentation.memory.EntityTreeNode.java

Source

/*
 * iteraplan is an IT Governance web application developed by iteratec, GmbH
 * Copyright (C) 2004 - 2014 iteratec, GmbH
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY ITERATEC, ITERATEC DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact iteratec GmbH headquarters at Inselkammerstr. 4
 * 82008 Munich - Unterhaching, Germany, or at email address info@iteratec.de.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "iteraplan" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by iteraplan".
 */
package de.iteratec.iteraplan.presentation.memory;

import java.io.Serializable;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import com.google.common.collect.Sets;

import de.iteratec.iteraplan.model.AbstractHierarchicalEntity;
import de.iteratec.iteraplan.model.interfaces.HierarchicalEntity;

/**
 * Wrapper (bean) class for {@link HierarchicalEntity} objects for the GUI.
 */
public class EntityTreeNode implements Iterable<EntityTreeNode>, Serializable {

    private static final long serialVersionUID = -2035512650999940148L;

    /** Iterator for subtrees of a node element (excluding the node itself). */
    static class EntityNodeIterator implements Iterator<EntityTreeNode> {

        private final Deque<Iterator<EntityTreeNode>> iteratorStack = Lists.newLinkedList();
        private EntityTreeNode nextNode;
        private boolean nextNodeDelivered = true;
        private Set<Predicate<EntityTreeNode>> predicates = Sets.newHashSet();

        public EntityNodeIterator(EntityTreeNode rootNode) {
            this.iteratorStack.offer(rootNode.getChildren().iterator());
        }

        public void addPredicate(Predicate<EntityTreeNode> etnPredicate) {
            predicates.add(etnPredicate);
        }

        /**{@inheritDoc}**/
        public boolean hasNext() {
            loadNextNode();
            return nextNode != null;
        }

        /**{@inheritDoc}**/
        public EntityTreeNode next() {
            loadNextNode();
            nextNodeDelivered = true;
            return nextNode;
        }

        private void loadNextNode() {
            if (nextNodeDelivered) {
                findNextDisplayNode();
                nextNodeDelivered = false;
            }
        }

        private void findNextDisplayNode() {
            Iterator<EntityTreeNode> iterator;

            while (!iteratorStack.isEmpty()) {
                iterator = iteratorStack.peek();
                if (iterator.hasNext()) {
                    EntityTreeNode nextNodeCandidate = iterator.next();
                    if (!nextNodeCandidate.isLeaf()) {
                        // push child iterator onto the stack
                        iteratorStack.push(nextNodeCandidate.getChildren().iterator());
                    }
                    if (fulfillsAllPredicates(nextNodeCandidate)) {
                        nextNode = nextNodeCandidate;
                        return;
                    }
                } else {
                    iteratorStack.pop();
                }
            }

            // no more elements
            nextNode = null;
        }

        private boolean fulfillsAllPredicates(EntityTreeNode etn) {
            boolean fulfilled = true;
            for (Predicate<EntityTreeNode> predicate : predicates) {
                fulfilled &= predicate.apply(etn);
            }
            return fulfilled;
        }

        /**{@inheritDoc}**/
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    /** Create placeholder stub ("... load x more") */
    static EntityTreeNode placeholderFor(EntityTreeNode parentNode, EntityTreeNode renderLikeNode) {
        EntityTreeNode placeholderNode = new EntityTreeNode(renderLikeNode.getEntity());
        placeholderNode.setTreeLevel(renderLikeNode.getTreeLevel());
        placeholderNode.setParent(parentNode);
        placeholderNode.setSiblingIndex(-1);
        placeholderNode.setPartialPlaceholder(true);
        placeholderNode.setCollapsed(true);
        placeholderNode.setDisplay(parentNode.isDisplay() && !parentNode.isCollapsed());
        placeholderNode.setValidResult(false);
        return placeholderNode;
    }

    /** Entity which is wrapped by this node element. */
    private AbstractHierarchicalEntity<?> entity;

    /** Id of the wrapped entity. */
    private final Integer id;

    /** This node's child nodes. */
    private List<EntityTreeNode> children;

    /** This node's parent node. */
    private EntityTreeNode parent;

    /** The tree level (root element = 0).  */
    private int treeLevel;

    /** Flag to indicate if this node is displayed collapsed (true) or expanded (false).  */
    private boolean collapsed = false;

    /** Flag to indicate how CSS "display" property should be set.  */
    private boolean display = true;

    /** Flag to indicate if this node's entity is among the valid search results.  */
    private boolean validResult = false;

    private int siblingIndex;

    private boolean partialPlaceholder = false;

    private List<EntityTreeNode> unloadedChildren;

    private boolean loadAll = false;

    EntityTreeNode(HierarchicalEntity<?> entity) {
        this(entity, 0);
    }

    EntityTreeNode(HierarchicalEntity<?> entity, int level) {
        //    Preconditions.checkState(entity instanceof AbstractHierarchicalEntity<?>);

        this.entity = (AbstractHierarchicalEntity<?>) entity;
        this.treeLevel = level;
        this.id = entity.getId();
    }

    public Integer getId() {
        return id;
    }

    public AbstractHierarchicalEntity<?> getEntity() {
        return entity;
    }

    void setEntity(AbstractHierarchicalEntity<?> entity) {
        this.entity = entity;
    }

    public List<EntityTreeNode> getChildren() {
        if (children == null) {
            children = Lists.newArrayList();
        }
        return children;
    }

    EntityTreeNode getChildBySiblingIndex(int lkpSiblingIndex) {
        Preconditions.checkElementIndex(lkpSiblingIndex, children.size());
        return children.get(lkpSiblingIndex);
    }

    void resetChildren() {
        children = Lists.newArrayList();
    }

    public EntityTreeNode getParent() {
        return parent;
    }

    void setParent(EntityTreeNode parentNode) {
        parent = parentNode;
    }

    void add(EntityTreeNode childNode) {
        getChildren().add(childNode);
    }

    void addUnloadedChild(EntityTreeNode childNode) {
        if (unloadedChildren == null) {
            unloadedChildren = Lists.newArrayList();
        }
        unloadedChildren.add(childNode);
    }

    void resetUnloadedChildren() {
        unloadedChildren = null;
    }

    boolean hasUnloadedChildren() {
        return unloadedChildren != null && unloadedChildren.size() > 0;
    }

    List<EntityTreeNode> getUnloadedChildren() {
        return unloadedChildren;
    }

    public int getNumberOfUnloadedChildren() {
        if (unloadedChildren != null) {
            return unloadedChildren.size();
        } else {
            return 0;
        }
    }

    public int getTreeLevel() {
        return treeLevel;
    }

    void setTreeLevel(int treeLevel) {
        this.treeLevel = treeLevel;
    }

    public boolean isCollapsed() {
        return collapsed;
    }

    void setCollapsed(boolean collapsed) {
        this.collapsed = collapsed;
    }

    public boolean isDisplay() {
        return display;
    }

    void setDisplay(boolean display) {
        this.display = display;
    }

    public boolean isValidResult() {
        return validResult;
    }

    void setValidResult(boolean validResult) {
        this.validResult = validResult;
    }

    public boolean isLeaf() {
        return children == null || children.size() == 0;
    }

    public int getSiblingIndex() {
        return siblingIndex;
    }

    void setSiblingIndex(int siblingIndex) {
        this.siblingIndex = siblingIndex;
    }

    public boolean isFirst() {
        return siblingIndex == 0;
    }

    public boolean isLast() {
        return siblingIndex == getParent().getChildren().size() - 1;
    }

    public boolean isPartialPlaceholder() {
        return partialPlaceholder;
    }

    void setPartialPlaceholder(boolean partialPlaceholder) {
        this.partialPlaceholder = partialPlaceholder;
    }

    public boolean isLoadAll() {
        return loadAll;
    }

    public void setLoadAll(boolean loadAll) {
        this.loadAll = loadAll;
    }

    /**{@inheritDoc}**/
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof EntityTreeNode)) {
            return false;
        }
        EntityTreeNode other = (EntityTreeNode) obj;
        return new EqualsBuilder().append(getId(), other.getId()).isEquals();
    }

    /**{@inheritDoc}**/
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(getId()).hashCode();
    }

    /**{@inheritDoc}**/
    @Override
    public String toString() {
        if (isPartialPlaceholder()) {
            return "STUB at position of: " + (new ToStringBuilder(this).append("id", entity.getId())
                    .append("hierarchical name", entity.getHierarchicalName()).toString());
        } else {
            return new ToStringBuilder(this).append("id", entity.getId())
                    .append("hierarchical name", entity.getHierarchicalName()).toString();
        }
    }

    /**{@inheritDoc}**/
    public Iterator<EntityTreeNode> iterator() {
        return Iterators.peekingIterator(new EntityNodeIterator(this));
    }

    public static void skipSubtree(EntityTreeNode currentNode, Iterator<EntityTreeNode> iterator) {
        Preconditions.checkState(iterator instanceof PeekingIterator<?>,
                "Skipping subtree requires an Iterator of type com.google.common.collect.PeekingIterator<EntityTreeNode> !");
        PeekingIterator<EntityTreeNode> peekingIterator = (PeekingIterator<EntityTreeNode>) iterator;
        int currentNodeLevel = currentNode.getTreeLevel();
        while (peekingIterator.hasNext() && peekingIterator.peek() != null
                && peekingIterator.peek().getTreeLevel() > currentNodeLevel) {
            peekingIterator.next();
        }
    }
}