Package org.structr.core.entity

Source Code of org.structr.core.entity.LinkedTreeNode

/**
* Copyright (C) 2010-2014 Morgner UG (haftungsbeschränkt)
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr 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 General Public License
* along with Structr.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.core.entity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.neo4j.helpers.collection.Iterables;
import org.structr.common.error.FrameworkException;
import org.structr.core.app.App;
import org.structr.core.app.StructrApp;
import org.structr.core.entity.relationship.AbstractChildren;
import org.structr.core.entity.relationship.AbstractListSiblings;
import org.structr.core.graph.RelationshipInterface;
import org.structr.core.property.PropertyMap;

/**
* Abstract base class for a multi-dimensional ordered tree datastructure.
*
* @author Christian Morgner
*/
public abstract class LinkedTreeNode<R extends AbstractChildren<T, T>, S extends AbstractListSiblings<T, T>, T extends LinkedTreeNode> extends LinkedListNode<S, T> {

  public abstract Class<R> getChildLinkType();

  public T treeGetParent() {

    AbstractChildren prevRel = getIncomingRelationship(getChildLinkType());
    if (prevRel != null) {

      return (T) prevRel.getSourceNode();
    }

    return null;
  }

  public void treeAppendChild(final T childElement) throws FrameworkException {

    final T lastChild = treeGetLastChild();

    PropertyMap properties = new PropertyMap();
    properties.put(AbstractChildren.position, treeGetChildCount());

    // create child relationship
    linkNodes(getChildLinkType(), (T) LinkedTreeNode.this, childElement, properties);

    // add new node to linked list
    if (lastChild != null) {
      LinkedTreeNode.super.listInsertAfter(lastChild, childElement);
    }
   
    ensureCorrectChildPositions();
  }

  public void treeInsertBefore(final T newChild, final T refChild) throws FrameworkException {

    List<R> rels = treeGetChildRelationships();
    boolean found = false;
    int position = 0;

    // when there are no child rels, this is an append operation
    if (rels.isEmpty()) {

      // we have no children, but the ref child is non-null => can't be ours.. :)
      if (refChild != null) {
        throw new FrameworkException(404, "Referenced child is not a child of parent node.");
      }

      treeAppendChild(newChild);
      return;
    }

    for (R rel : rels) {

      T node = rel.getTargetNode();
      if (node.equals(refChild)) {

        // will be used only once here..
        PropertyMap properties = new PropertyMap();
        properties.put(AbstractChildren.position, position);

        linkNodes(getChildLinkType(), (T) LinkedTreeNode.this, newChild, properties);

        found = true;

        position++;
      }

      rel.setProperty(AbstractChildren.position, position);

      position++;
    }

    // if child is not found, raise an exception
    if (!found) {
      throw new FrameworkException(404, "Referenced child is not a child of parent node.");
    }

    // insert new node in linked list
    LinkedTreeNode.super.listInsertBefore(refChild, newChild);
   
    ensureCorrectChildPositions();
  }

  public void treeInsertAfter(final T newChild, final T refChild) throws FrameworkException {

    List<R> rels = treeGetChildRelationships();
    int position = 0;

    // when there are no child rels, this is an append operation
    if (rels.isEmpty()) {

      treeAppendChild(newChild);
      return;
    }

    for (R rel : rels) {

      T node = rel.getTargetNode();

      rel.setProperty(AbstractChildren.position, position);
      position++;

      if (node.equals(refChild)) {

        // will be used only once here..
        PropertyMap properties = new PropertyMap();
        properties.put(AbstractChildren.position, position);

        linkNodes(getChildLinkType(), (T) LinkedTreeNode.this, newChild, properties);

        position++;
      }
    }

    // insert new node in linked list
    LinkedTreeNode.super.listInsertAfter(refChild, newChild);
   
    ensureCorrectChildPositions();

  }

  public void treeRemoveChild(final T childToRemove) throws FrameworkException {

    // remove element from linked list
    LinkedTreeNode.super.listRemove(childToRemove);

    unlinkNodes(getChildLinkType(), (T) LinkedTreeNode.this, childToRemove);

    ensureCorrectChildPositions();

  }

  public void treeReplaceChild(final T newChild, final T oldChild) throws FrameworkException {

    // save old position
    int oldPosition = treeGetChildPosition(oldChild);

    // remove old node
    unlinkNodes(getChildLinkType(), (T) LinkedTreeNode.this, oldChild);

    // insert new node with position from old node
    PropertyMap properties = new PropertyMap();
    properties.put(AbstractChildren.position, oldPosition);

    linkNodes(getChildLinkType(), (T) LinkedTreeNode.this, newChild, properties);

    // replace element in linked list as well
    LinkedTreeNode.super.listInsertBefore(oldChild, newChild);
    LinkedTreeNode.super.listRemove(oldChild);
   
    ensureCorrectChildPositions();
  }

  public T treeGetFirstChild() {
    return treeGetChild(0);
  }

  public T treeGetLastChild() {

    int last = treeGetChildCount() - 1;
    if (last >= 0) {

      return treeGetChild(last);
    }

    return null;
  }

  public T treeGetChild(final int position) {

    for (R rel : getOutgoingRelationships(getChildLinkType())) {

      Integer pos = rel.getProperty(AbstractChildren.position);

      if (pos != null && pos == position) {

        return (T) rel.getTargetNode();
      }
    }

    return null;
  }

  public int treeGetChildPosition(final T child) {

    final R rel = child.getIncomingRelationship(getChildLinkType());
    if (rel != null) {

      Integer pos = rel.getProperty(AbstractChildren.position);
      if (pos != null) {

        return pos;
      }
    }

    return 0;
  }

  public List<T> treeGetChildren() {

    List<T> abstractChildren = new ArrayList<>();

    for (R rel : treeGetChildRelationships()) {

      abstractChildren.add(rel.getTargetNode());
    }

    return abstractChildren;
  }

  public int treeGetChildCount() {
    return (int) Iterables.count(getOutgoingRelationships(getChildLinkType()));
  }

  public List<R> treeGetChildRelationships() {

    // fetch all relationships
    List<R> childRels = Iterables.toList(getOutgoingRelationships(getChildLinkType()));

    // sort relationships by position
    Collections.sort(childRels, new Comparator<R>() {

      @Override
      public int compare(R o1, R o2) {

        Integer pos1 = o1.getProperty(AbstractChildren.position);
        Integer pos2 = o2.getProperty(AbstractChildren.position);

        if (pos1 != null && pos2 != null) {

          return pos1.compareTo(pos2);
        }

        return 0;
      }

    });

    return childRels;
  }

  /**
   * Ensures that the position attributes of the AbstractChildren of this
   * node are correct. Please note that this method needs to run in the
   * same transaction as any modifiying operation that changes the order
   * of child nodes, and therefore this method does _not_ create its own
   * transaction. However, it will not raise a NotInTransactionException
   * when called outside of modifying operations, because each setProperty
   * call creates its own transaction.
   */
  private void ensureCorrectChildPositions() throws FrameworkException {

    List<R> childRels = treeGetChildRelationships();
    int position = 0;

    for (R childRel : childRels) {
      childRel.setProperty(AbstractChildren.position, position++);
    }
  }

  private void unlinkNodes(final Class<R> linkType, final T startNode, final T endNode) throws FrameworkException {

    final App app = StructrApp.getInstance(securityContext);

    for (RelationshipInterface rel : startNode.getRelationships(linkType)) {

      if (rel != null && rel.getTargetNode().equals(endNode)) {
        app.delete(rel);
      }
    }
  }

  /**
   * Return a set containing all child nodes of this node.
   *
   * This node is not included.
   * There are no duplicates.
   *
   * @return child nodes
   */
  public Set<T> getAllChildNodes() {

    Set<T> allChildNodes = new HashSet();

    List<T> childNodes = treeGetChildren();

    for (final T child : childNodes) {
     
      allChildNodes.add(child);
      allChildNodes.addAll(child.getAllChildNodes());
     
    }

    return allChildNodes;
  }
}
TOP

Related Classes of org.structr.core.entity.LinkedTreeNode

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.