com.flexive.shared.tree.FxTreeNode.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.shared.tree.FxTreeNode.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) framework.
 *
 *  Copyright (c) 1999-2014
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) project is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser General Public
 *  License version 2.1 or higher as published by the Free Software Foundation.
 *
 *  The GNU Lesser General Public License can be found at
 *  http://www.gnu.org/licenses/lgpl.html.
 *  A copy is found in the textfile LGPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  This library 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.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.shared.tree;

import com.flexive.shared.*;
import com.flexive.shared.content.FxContentVersionInfo;
import com.flexive.shared.content.FxPK;
import com.flexive.shared.security.ACLCategory;
import com.flexive.shared.security.LifeCycleInfo;
import com.flexive.shared.value.FxString;
import org.apache.commons.lang.ArrayUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * FxNode implementation for flexive
 *
 * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 */
public class FxTreeNode
        implements Serializable, SelectableObjectWithLabel, SelectableObjectWithName, Iterable<FxTreeNode> {
    private static final long serialVersionUID = -1666004845250114348L;

    private String path = null;
    private boolean temporary = true;
    private boolean markForDelete = false;
    protected FxString label = null;
    protected FxPK reference;
    protected LifeCycleInfo referenceLifeCycleInfo;
    protected long referenceTypeId = 0;
    protected FxLock lock = FxLock.noLockPK();
    protected FxTreeMode mode;
    protected int position;
    protected List<FxTreeNode> children;
    private List<Long> childIds = null;
    private boolean dirty;
    protected long id;
    private long[] acls;
    protected long parentNodeId;
    protected String name;
    private long modifiedAt;
    protected String data;
    private int depth;
    private int directChildCount;
    private boolean leaf;
    private boolean mayEdit, mayDelete, mayRelate, mayExport, mayCreate;
    protected boolean activate = false;

    //flag indicating that this tree node is only partial loaded
    private boolean partialLoaded = false;
    public static final int PARTIAL_LOADED_POS = -42;
    public static final String PATH_NOT_LOADED = "/not_loaded(partial!)";

    /**
     * Constant id of the root node
     */
    public final static long ROOT_NODE = 1;

    /**
     * Protected constructor to avoid construction
     */
    protected FxTreeNode() {
    }

    /**
     * Ctor
     *
     * @param mode                   FxTreeMode
     * @param lock                   the lock of the reference
     * @param id                     node id
     * @param parentNodeId           id of the parent node
     * @param reference              pk of the referenced content
     * @param referenceLifeCycleInfo if the node has a referenced content this is its LifeCycleInfo
     * @param referenceTypeId        type id of the referenced content
     * @param acls                   acls of the referenced content
     * @param name                   name (part of the path)
     * @param path                   complete path from the root node
     * @param label                  label
     * @param position               position
     * @param children               child nodes (only available if loaded with <code>#getTree</code>)
     * @param childIds               ids of the child nodes (only available if loaded with <code>#getTree</code>)
     * @param depth                  depth of this node relative to the root node
     * @param directChildCount       number of children attached to this node directly
     * @param leaf                   is this node a leaf?
     * @param dirty                  dirty flag
     * @param modifiedAt             timestamp of last modification
     * @param data                   optional data
     * @param mayEdit                edit permission for the calling user
     * @param mayCreate              create permission for the calling user
     * @param mayDelete              delete permission for the calling user
     * @param mayRelate              relate permission for the calling user
     * @param mayExport              export permission for the calling user
     */
    public FxTreeNode(FxTreeMode mode, FxLock lock, long id, long parentNodeId, FxPK reference,
            LifeCycleInfo referenceLifeCycleInfo, long referenceTypeId, List<Long> acls, String name, String path,
            FxString label, int position, List<FxTreeNode> children, List<Long> childIds, int depth,
            int directChildCount, boolean leaf, boolean dirty, long modifiedAt, String data, boolean mayEdit,
            boolean mayCreate, boolean mayDelete, boolean mayRelate, boolean mayExport) {
        this.path = FxFormatUtils.escapeTreePath(path);
        this.label = label;
        this.lock = lock;
        this.reference = reference;
        this.referenceLifeCycleInfo = referenceLifeCycleInfo;
        this.referenceTypeId = referenceTypeId;
        this.acls = acls != null ? FxArrayUtils.toPrimitiveLongArray(acls) : new long[0];
        this.mode = mode;
        this.position = position;
        this.children = children;
        this.childIds = childIds;
        this.dirty = dirty;
        this.id = id;
        this.parentNodeId = parentNodeId;
        this.name = FxFormatUtils.escapeTreePath(name);
        this.modifiedAt = modifiedAt;
        this.data = data;
        this.depth = depth;
        this.directChildCount = directChildCount;
        this.leaf = leaf;
        this.temporary = false;
        this.markForDelete = false;
        this.mayCreate = mayCreate;
        this.mayDelete = mayDelete;
        this.mayEdit = mayEdit;
        this.mayExport = mayExport;
        this.mayRelate = mayRelate;
        this.partialLoaded = position == PARTIAL_LOADED_POS;
    }

    /**
     * Get the position of this node
     *
     * @return position of this node
     */
    public int getPosition() {
        return position;
    }

    /**
     * Is this FxTreeNode only partially loaded?
     * Loading a (sub)tree is usually performed with partial loading enabled (only the calling
     * users current language filled in the label)
     *
     * @return if this FxTreeNode is only partially loaded
     */
    public boolean isPartialLoaded() {
        return this.partialLoaded;
    }

    /**
     * Is this node "live"?
     *
     * @return if the node is "live"
     */
    public boolean isLive() {
        return mode == FxTreeMode.Live;
    }

    /**
     * Is this node flagged as dirty?
     *
     * @return node flagged as dirty?
     */
    public boolean isDirty() {
        return dirty;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long getId() {
        return id;
    }

    /**
     * Return the ACL id(s) of the referenced content.
     *
     * @return the ACL id(s) of the referenced content.
     */
    public List<Long> getACLIds() {
        return Arrays.asList(ArrayUtils.toObject(acls));
    }

    /**
     * Get the id of the parent node
     *
     * @return id of the parent node
     */
    public long getParentNodeId() {
        return parentNodeId;
    }

    /**
     * Get the name of this node
     *
     * @return name of this node
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * Is a reference set for this node (Description)
     *
     * @return if a reference is set
     */
    public boolean hasReference() {
        return reference != null;
    }

    /**
     * Get the referenced content
     *
     * @return referenced content
     */
    public FxPK getReference() {
        return reference;
    }

    /**
     * Get the LifeCycleInfo of the referenced content
     *
     * @return LifeCycleInfo of the referenced content
     * @since 3.1
     */
    public LifeCycleInfo getReferenceLifeCycleInfo() {
        return referenceLifeCycleInfo;
    }

    /**
     * Get the type id of the referenced content
     *
     * @return type id of the referenced content
     */
    public long getReferenceTypeId() {
        return referenceTypeId;
    }

    /**
     * Get the lock of the reference, if set
     *
     * @return lock of the reference, if set
     * @since 3.1
     */
    public FxLock getLock() {
        return lock;
    }

    /**
     * Get the timestamp of the last modification
     *
     * @return timestamp of the last modification
     */
    public long getModifiedAt() {
        return modifiedAt;
    }

    /**
     * Is data assigned to this node?
     *
     * @return if data is assigned to this node
     */
    public boolean hasData() {
        return data != null;
    }

    /**
     * Get the template assigned to this node, can be <code>null</code>
     *
     * @return template assigned to this node, can be <code>null</code>
     */
    public String getData() {
        return data;
    }

    /**
     * Returns the depth of the node within the complete tree
     * The root node has depth 1
     *
     * @return the depth of the node
     */
    public int getDepth() {
        return depth;
    }

    /**
     * Get the number of child nodes directly attached to this node
     *
     * @return the number of child nodes directly attached to this node
     */
    public int getDirectChildCount() {
        return directChildCount;
    }

    /**
     * Is this a leaf node?
     *
     * @return if this node is a leaf node
     */
    public boolean isLeaf() {
        return leaf;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized FxString getLabel() {
        return label;
    }

    /**
     * Get the path of this node
     *
     * @return path
     */
    public String getPath() {
        return path;
    }

    /**
     * Returns the children of this node, but only if they are a part of the resultset - use
     * isLeaf(), getDirectChildCount() and getTotalChildCount() to find out if the node has
     * children.
     * This function never returns null, but a empty List when no children are available
     *
     * @return the children of this node, but only if they are a part of the resultset
     */
    public List<FxTreeNode> getChildren() {
        return children;
    }

    /**
     * Returns the child Id's of this node, but only if they are a part of the resultset.
     *
     * @return the child Id's of this node, but only if they are a part of the resultset
     */
    public synchronized List<Long> getChildIds() {
        if (childIds == null) {
            childIds = new ArrayList<Long>(children.size());
            for (FxTreeNode child : getChildren())
                childIds.add(child.getId());
        }
        return childIds;
    }

    /**
     * ACL: Edit permission for the calling user
     *
     * @return ACL: Edit permission for the calling user
     */
    public boolean isMayEdit() {
        return mayEdit;
    }

    /**
     * ACL: Delete permission for the calling user
     *
     * @return ACL: Delete permission for the calling user
     */
    public boolean isMayDelete() {
        return mayDelete;
    }

    /**
     * ACL: Relate permission for the calling user
     *
     * @return ACL: Relate permission for the calling user
     */
    public boolean isMayRelate() {
        return mayRelate;
    }

    /**
     * ACL: Export permission for the calling user
     *
     * @return ACL: Export permission for the calling user
     */
    public boolean isMayExport() {
        return mayExport;
    }

    /**
     * ACL: Create permission for the calling user
     *
     * @return ACL: Create permission for the calling user
     */
    public boolean isMayCreate() {
        return mayCreate;
    }

    /**
     * Is this node marked to be deleted? (only used in GUI, not persisted!)
     *
     * @return marked for delete
     */
    public boolean isMarkForDelete() {
        return markForDelete;
    }

    /**
     * Mark to be deleted for UI (only used in GUI, not persisted!)
     *
     * @param markForDelete delete?
     * @return this
     */
    public FxTreeNode setMarkForDelete(boolean markForDelete) {
        this.markForDelete = markForDelete;
        return this;
    }

    /**
     * Set the active flag of this tree node (only used in GUI, not persisted!)
     *
     * @param activate activate flag
     */
    public void setActivate(boolean activate) {
        this.activate = activate;
    }

    /**
     * Is the node marked as active? (only used in GUI, not persisted!)
     *
     * @return active status
     */
    public boolean isActivate() {
        return activate;
    }

    /**
     * Is this node temporary only? (only used in GUI, not persisted!)
     *
     * @return if this node is temporary only
     */
    public boolean isTemporary() {
        return temporary;
    }

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

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null || (!(obj instanceof SelectableObject)))
            return false;

        SelectableObject comp = (SelectableObject) obj;
        return this.getId() == comp.getId();
    }

    /**
     * Create a temporary error node to be used in UI
     *
     * @param nodeId  desired node id
     * @param message error message
     * @return FxTreeNode
     */
    public static FxTreeNode createErrorNode(long nodeId, String message) {
        return new FxTreeNode(FxTreeMode.Edit, FxLock.noLockPK(), nodeId, 0, FxPK.createNewPK(),
                new FxContentVersionInfo.NewLifeCycleInfoImpl(), 0L,
                Arrays.asList(ACLCategory.INSTANCE.getDefaultId()), "Error", message, new FxString(false, "Error"),
                Integer.MAX_VALUE, new ArrayList<FxTreeNode>(0), new ArrayList<Long>(0), 0, 0, true, true,
                System.currentTimeMillis(), "", true, true, true, true, true);
    }

    /**
     * Get the tree mode (live or edit)
     *
     * @return tree mode
     */
    public FxTreeMode getMode() {
        return mode;
    }

    /**
     * Make this node editable
     *
     * @return FxTreeNodeEdit
     */
    public FxTreeNodeEdit asEditable() {
        return new FxTreeNodeEdit(this);
    }

    /**
     * Flag this FxTreeNode as temporary
     *
     * @return this
     */
    private FxTreeNode flagTemporary() {
        this.temporary = true;
        return this;
    }

    /**
     * Create a temporary node below the given parent node
     *
     * @param parentNode the parent node
     * @return temporary node
     */
    public static FxTreeNode createNewTemporaryChildNode(FxTreeNode parentNode) {
        FxTreeNode n = new FxTreeNode(parentNode.getMode(), FxLock.noLockPK(), (System.currentTimeMillis() * -1),
                parentNode.getId(), FxPK.createNewPK(), new FxContentVersionInfo.NewLifeCycleInfoImpl(), 0L,
                Arrays.asList(ACLCategory.INSTANCE.getDefaultId()), "@@TMP", "",
                new FxString(parentNode.getLabel().isMultiLanguage(), ""), Integer.MAX_VALUE,
                new ArrayList<FxTreeNode>(0), new ArrayList<Long>(0), 0, 0, true, true, System.currentTimeMillis(),
                "", true, true, true, true, true).flagTemporary();
        n.path = parentNode.getPath() + (parentNode.getPath().endsWith("/") ? "" : "/") + "*";
        return n;
    }

    /**
     * Internal method only used during loading phase of a tree
     *
     * @param node child node to add
     */
    public synchronized void _addChild(FxTreeNode node) {
        children.add(node);
        childIds.add(node.getId());
    }

    /**
     * Internal method to recursively apply a path from the root to all children
     *
     * @param path path to apply (includes name already!)
     */
    public void _applyPath(String path) {
        this.path = path;
        for (FxTreeNode node : this.getChildren())
            node._applyPath(path + (path.endsWith("/") ? node.getName() : "/" + node.getName()));
    }

    /**
     * Internal method to recursively apply positions
     *
     * @param position position to apply to this node
     */
    public void _applyPosition(int position) {
        this.position = position;
        int pos = 0;
        for (FxTreeNode node : this.getChildren())
            node._applyPosition(pos++);
    }

    /**
     * Returns an iterator over this node and its children.
     *
     * @return an iterator over this node and its children.
     */
    @Override
    public Iterator<FxTreeNode> iterator() {
        return new NodeIterator();
    }

    private class NodeIterator implements Iterator<FxTreeNode>, Serializable {
        private static final long serialVersionUID = -3147200089772263291L;
        private int index = -1;
        private Iterator<FxTreeNode> childIterator;

        @Override
        public boolean hasNext() {
            return index == -1 || (index >= 0 && !children.isEmpty() && ((children.size() > index)
                    || (children.size() == index && childIterator != null && childIterator.hasNext())));
        }

        @Override
        public FxTreeNode next() {
            if (index == -1) {
                index++;
                return FxTreeNode.this;
            } else {
                if (childIterator == null || !childIterator.hasNext()) {
                    childIterator = children.get(index).iterator();
                    index++;
                }
                return childIterator.next();
            }
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}