/* AWE - Amanzi Wireless Explorer
* http://awe.amanzi.org
* (C) 2008-2009, AmanziTel AB
*
* This library is provided under the terms of the Eclipse Public License
* as described at http://www.eclipse.org/legal/epl-v10.html. Any use,
* reproduction or distribution of the library constitutes recipient's
* acceptance of this agreement.
*
* This library is distributed WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.amanzi.neo.services.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.amanzi.neo.nodeproperties.IGeneralNodeProperties;
import org.amanzi.neo.nodetypes.INodeType;
import org.amanzi.neo.nodetypes.NodeTypeManager;
import org.amanzi.neo.nodetypes.NodeTypeNotExistsException;
import org.amanzi.neo.services.INodeService;
import org.amanzi.neo.services.exceptions.DatabaseException;
import org.amanzi.neo.services.exceptions.DuplicatedNodeException;
import org.amanzi.neo.services.exceptions.PropertyNotFoundException;
import org.amanzi.neo.services.exceptions.ServiceException;
import org.amanzi.neo.services.impl.internal.AbstractService;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.traversal.Evaluation;
import org.neo4j.graphdb.traversal.Evaluator;
import org.neo4j.graphdb.traversal.Evaluators;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.kernel.Traversal;
/**
* TODO Purpose of
* <p>
* </p>
*
* @author Nikolay Lagutko (nikolay.lagutko@amanzitel.com)
* @since 1.0.0
*/
public class NodeService extends AbstractService implements INodeService {
/**
* TODO Purpose of
* <p>
* </p>
*
* @author Nikolay Lagutko (nikolay.lagutko@amanzitel.com)
* @since 1.0.0
*/
private static final class ChainEvaluator implements Evaluator {
/** Node node field */
private final Node node;
/**
* @param node
*/
private ChainEvaluator(final Node node) {
this.node = node;
}
@Override
public Evaluation evaluate(final Path path) {
boolean toContinue = true;
boolean include = true;
if (path.lastRelationship() != null) {
final Relationship relation = path.lastRelationship();
if (relation.getStartNode().getId() == node.getId()) {
toContinue = relation.isType(NodeServiceRelationshipType.CHILD);
} else {
toContinue = relation.isType(NodeServiceRelationshipType.NEXT);
}
include = toContinue;
} else {
include = false;
}
return Evaluation.of(include, toContinue);
}
}
public enum MeasurementRelationshipType implements RelationshipType {
LOCATION;
}
public enum NodeServiceRelationshipType implements RelationshipType {
CHILD, NEXT;
}
private static final TraversalDescription OUTGOING_LEVEL_1_TRAVERSAL = Traversal.description().breadthFirst()
.evaluator(Evaluators.atDepth(1));
private static final TraversalDescription OUTGOUIN_NODES_TRAVERSAL = Traversal.description().breadthFirst();
private static final TraversalDescription CHAIN_TRAVERSAL = Traversal.description().depthFirst()
.evaluator(Evaluators.excludeStartPosition()).relationships(NodeServiceRelationshipType.CHILD, Direction.OUTGOING)
.relationships(NodeServiceRelationshipType.NEXT, Direction.OUTGOING);
/**
* @param graphDb
*/
public NodeService(final GraphDatabaseService graphDb, final IGeneralNodeProperties generalNodeProperties) {
super(graphDb, generalNodeProperties);
}
protected Node addToChain(final Node parentNode, final Node childNode) throws ServiceException {
RelationshipType relationType = NodeServiceRelationshipType.CHILD;
Node previousNode = parentNode;
final Long lastChildId = getNodeProperty(parentNode, getGeneralNodeProperties().getLastChildID(), null, false);
final Transaction tx = getGraphDb().beginTx();
try {
if (lastChildId != null) {
previousNode = getGraphDb().getNodeById(lastChildId);
relationType = NodeServiceRelationshipType.NEXT;
}
previousNode.createRelationshipTo(childNode, relationType);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
updateProperty(parentNode, getGeneralNodeProperties().getLastChildID(), childNode.getId());
updateProperty(childNode, getGeneralNodeProperties().getParentIDProperty(), parentNode.getId());
return childNode;
}
@Override
public Node createNode(final Node parentNode, final INodeType nodeType, final RelationshipType relationshipType)
throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert relationshipType != null;
final Map<String, Object> properties = new HashMap<String, Object>();
return createNode(parentNode, nodeType, relationshipType, properties);
}
@Override
public Node createNode(final Node parentNode, final INodeType nodeType, final RelationshipType relationshipType,
final Map<String, Object> parameters) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert parameters != null;
assert relationshipType != null;
final Transaction tx = getGraphDb().beginTx();
Node result = null;
try {
result = getGraphDb().createNode();
result.setProperty(getGeneralNodeProperties().getNodeTypeProperty(), nodeType.getId());
for (final Entry<String, Object> property : parameters.entrySet()) {
result.setProperty(property.getKey(), property.getValue());
}
parentNode.createRelationshipTo(result, relationshipType);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
return result;
}
@Override
public Node createNode(final Node parentNode, final INodeType nodeType, final RelationshipType relationshipType,
final String name) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert !StringUtils.isEmpty(name);
assert relationshipType != null;
final Map<String, Object> properties = new HashMap<String, Object>();
properties.put(getGeneralNodeProperties().getNodeNameProperty(), name);
return createNode(parentNode, nodeType, relationshipType, properties);
}
@Override
public Node createNode(final Node parentNode, final INodeType nodeType, final RelationshipType relationshipType,
final String name, final Map<String, Object> parameters) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert relationshipType != null;
assert !StringUtils.isEmpty(name);
assert parameters != null;
parameters.put(getGeneralNodeProperties().getNodeNameProperty(), name);
return createNode(parentNode, nodeType, relationshipType, parameters);
}
@Override
public Node createNodeInChain(final Node parentNode, final INodeType nodeType) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
final Map<String, Object> properties = new HashMap<String, Object>();
return createNodeInChain(parentNode, nodeType, properties);
}
@Override
public Node createNodeInChain(final Node parentNode, final INodeType nodeType, final Map<String, Object> parameters)
throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert parameters != null;
final Transaction tx = getGraphDb().beginTx();
Node result = null;
try {
result = getGraphDb().createNode();
result.setProperty(getGeneralNodeProperties().getNodeTypeProperty(), nodeType.getId());
for (final Entry<String, Object> property : parameters.entrySet()) {
result.setProperty(property.getKey(), property.getValue());
}
addToChain(parentNode, result);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
return result;
}
@Override
public Node createNodeInChain(final Node parentNode, final INodeType nodeType, final String name) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert !StringUtils.isEmpty(name);
final Map<String, Object> properties = new HashMap<String, Object>();
properties.put(getGeneralNodeProperties().getNodeNameProperty(), name);
return createNodeInChain(parentNode, nodeType, properties);
}
@Override
public Node createNodeInChain(final Node parentNode, final INodeType nodeType, final String name,
final Map<String, Object> parameters) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
assert !StringUtils.isEmpty(name);
assert parameters != null;
parameters.put(getGeneralNodeProperties().getNodeNameProperty(), name);
return createNodeInChain(parentNode, nodeType, parameters);
}
@Override
public void deleteChain(final Node root) throws DatabaseException {
assert root != null;
final Transaction tx = getGraphDb().beginTx();
try {
deleteNode(root);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
}
/**
* @param root
*/
private void deleteNode(final Node root) {
final List<Node> nextNode = new ArrayList<Node>();
for (final Relationship rel : root.getRelationships(Direction.OUTGOING)) {
nextNode.add(rel.getOtherNode(root));
rel.delete();
}
for (final Relationship rel : root.getRelationships()) {
rel.delete();
}
root.delete();
for (final Node node : nextNode) {
deleteNode(node);
}
}
@Override
public void deleteRelationship(final Relationship relation) throws ServiceException {
assert relation != null;
final Transaction tx = getGraphDb().beginTx();
try {
relation.delete();
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
}
@Override
public void deleteSingleNode(final Node node) throws ServiceException {
final Transaction tx = getGraphDb().beginTx();
try {
for (final Relationship relationship : node.getRelationships()) {
relationship.delete();
}
System.out.println(node.getRelationships());
node.delete();
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
}
@Override
public Relationship findLinkBetweenNodes(final Node startNode, final Node endNode, final RelationshipType relationshipType,
final Direction direction) throws ServiceException {
assert startNode != null;
assert endNode != null;
assert relationshipType != null;
assert direction != null;
assert !startNode.equals(endNode);
Relationship result = null;
try {
for (final Relationship singleRelation : startNode.getRelationships(direction, relationshipType)) {
if (singleRelation.getOtherNode(startNode).equals(endNode)) {
result = singleRelation;
break;
}
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
return result;
}
@Override
public Iterator<Node> getAllChildren(final Node node) throws ServiceException {
return getAllDownlinkTraversal().traverse(node).nodes().iterator();
}
protected TraversalDescription getAllDownlinkTraversal() {
return OUTGOUIN_NODES_TRAVERSAL;
}
@Override
public Node getChainParent(final Node node) throws ServiceException {
assert node != null;
final long parentId = getNodeProperty(node, getGeneralNodeProperties().getParentIDProperty(), null, true);
return getGraphDb().getNodeById(parentId);
}
@Override
public Node getChildByName(final Node parentNode, final String name, final INodeType nodeType) throws ServiceException {
return getChildByName(parentNode, name, nodeType, NodeServiceRelationshipType.CHILD);
}
@Override
public Node getChildByName(final Node parentNode, final String name, final INodeType nodeType,
final RelationshipType relationshipType) throws ServiceException {
assert parentNode != null;
assert !StringUtils.isEmpty(name);
assert nodeType != null;
Node result = null;
boolean throwDuplicatedException = false;
try {
final Iterator<Node> nodes = getChildrenTraversal(nodeType, relationshipType)
.evaluator(new PropertyEvaluator(getGeneralNodeProperties().getNodeNameProperty(), name)).traverse(parentNode)
.nodes().iterator();
if (nodes.hasNext()) {
result = nodes.next();
if (nodes.hasNext()) {
throwDuplicatedException = true;
}
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
if (throwDuplicatedException) {
throw new DuplicatedNodeException(getGeneralNodeProperties().getNodeNameProperty(), name);
}
return result;
}
@Override
public Node getChildInChainByName(final Node parentNode, final String name, final INodeType nodeType) throws ServiceException {
assert parentNode != null;
assert !StringUtils.isEmpty(name);
assert nodeType != null;
Node result = null;
boolean throwDuplicatedException = false;
try {
final Iterator<Node> nodes = getChildrenChainTraversal(nodeType, parentNode)
.evaluator(new PropertyEvaluator(getGeneralNodeProperties().getNodeNameProperty(), name)).traverse(parentNode)
.nodes().iterator();
if (nodes.hasNext()) {
result = nodes.next();
if (nodes.hasNext()) {
throwDuplicatedException = true;
}
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
if (throwDuplicatedException) {
throw new DuplicatedNodeException(getGeneralNodeProperties().getNodeNameProperty(), name);
}
return result;
}
@Override
public Iterator<Node> getChildren(final Node parentNode) throws ServiceException {
return getChildren(parentNode, NodeServiceRelationshipType.CHILD);
}
@Override
public Iterator<Node> getChildren(final Node parentNode, final INodeType nodeType) throws ServiceException {
return getChildren(parentNode, nodeType, NodeServiceRelationshipType.CHILD);
}
@Override
public Iterator<Node> getChildren(final Node parentNode, final INodeType nodeType, final RelationshipType relationshipType)
throws ServiceException {
assert parentNode != null;
try {
return getChildrenTraversal(nodeType, relationshipType).traverse(parentNode).nodes().iterator();
} catch (final Exception e) {
throw new DatabaseException(e);
}
}
@Override
public Iterator<Node> getChildren(final Node parentNode, final RelationshipType relationshipType) throws ServiceException {
assert parentNode != null;
try {
return getChildrenTraversal(relationshipType).traverse(parentNode).nodes().iterator();
} catch (final Exception e) {
throw new DatabaseException(e);
}
}
@Override
public Iterator<Node> getChildrenChain(final Node parentNode) throws ServiceException {
assert parentNode != null;
try {
return getChildrenChainTraversal(parentNode).traverse(parentNode).nodes().iterator();
} catch (final Exception e) {
throw new DatabaseException(e);
}
}
@Override
public Iterator<Node> getChildrenChain(final Node parentNode, final INodeType nodeType) throws ServiceException {
assert parentNode != null;
assert nodeType != null;
try {
return CHAIN_TRAVERSAL
.evaluator(new PropertyEvaluator(getGeneralNodeProperties().getNodeTypeProperty(), nodeType.getId()))
.traverse(parentNode).nodes().iterator();
} catch (final Exception e) {
throw new DatabaseException(e);
}
}
protected TraversalDescription getChildrenChainTraversal(final INodeType nodeType, final Node node) {
return getChildrenChainTraversal(node).evaluator(getPropertyEvaluatorForType(nodeType));
}
@Override
public TraversalDescription getChildrenChainTraversal(final Node node) {
return CHAIN_TRAVERSAL.evaluator(new ChainEvaluator(node));
}
@Override
public TraversalDescription getChildrenTraversal(final INodeType nodeType, final RelationshipType relationshipType) {
return getDownlinkTraversal().relationships(relationshipType, Direction.OUTGOING).evaluator(
getPropertyEvaluatorForType(nodeType));
}
protected TraversalDescription getChildrenTraversal(final RelationshipType relationshipType) {
return getDownlinkTraversal().relationships(relationshipType, Direction.OUTGOING);
}
@SuppressWarnings("unchecked")
protected <T extends Object> T getContainerProperty(final PropertyContainer container, final String propertyName,
final T defaultValue, final boolean throwExceptionIfNotExist) throws ServiceException {
assert container != null;
assert !StringUtils.isEmpty(propertyName);
assert !(throwExceptionIfNotExist && defaultValue != null);
boolean throwPropertyNotFoundException = false;
T result = null;
try {
final boolean exists = container.hasProperty(propertyName);
if (throwExceptionIfNotExist && !exists) {
throwPropertyNotFoundException = true;
} else {
if (exists) {
result = (T)container.getProperty(propertyName);
} else {
result = (T)container.getProperty(propertyName, defaultValue);
}
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
if (throwPropertyNotFoundException) {
throw new PropertyNotFoundException(propertyName, container);
}
return result;
}
protected TraversalDescription getDownlinkTraversal() {
return OUTGOING_LEVEL_1_TRAVERSAL;
}
@Override
public String getNodeName(final Node node) throws ServiceException {
return (String)getNodeProperty(node, getGeneralNodeProperties().getNodeNameProperty(), null, true);
}
/**
* Returns a Single Property of Node
*
* @note do not use this method to get multiple properties of a node
* @note will throw assertion error if throwExceptionIfNotExist is true and defaultValue not
* null
* @param node Node
* @param propertyName name of Property
* @param defaultValue default value of Property
* @param throwExceptionIfNotExist should method throw Exception
* @return
* @throws ServiceException
*/
@Override
public <T extends Object> T getNodeProperty(final Node node, final String propertyName, final T defaultValue,
final boolean throwExceptionIfNotExist) throws ServiceException {
return getContainerProperty(node, propertyName, defaultValue, throwExceptionIfNotExist);
}
@Override
public INodeType getNodeType(final Node node) throws ServiceException, NodeTypeNotExistsException {
final String nodeType = (String)getNodeProperty(node, getGeneralNodeProperties().getNodeTypeProperty(), null, true);
return NodeTypeManager.getInstance().getType(nodeType);
}
@Override
public Node getParent(final Node child, final RelationshipType relationshipType) throws ServiceException {
assert child != null;
Node parent = null;
try {
final Relationship relToParent = child.getSingleRelationship(relationshipType, Direction.INCOMING);
if (relToParent != null) {
parent = relToParent.getStartNode();
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
return parent;
}
@Override
public Node getReferencedNode() throws ServiceException {
try {
return getGraphDb().getReferenceNode();
} catch (final Exception e) {
throw new DatabaseException(e);
}
}
@Override
public <T> T getRelationshipProperty(final Relationship relationship, final String propertyName, final T defaultValue,
final boolean throwExceptionIfNotExist) throws ServiceException {
return getContainerProperty(relationship, propertyName, defaultValue, throwExceptionIfNotExist);
}
@Override
public Node getSingleChild(final Node parentNode, final INodeType nodeType, final RelationshipType relationshipType)
throws ServiceException {
assert parentNode != null;
assert nodeType != null;
boolean throwDuplicatedException = false;
Node result = null;
try {
final Iterator<Node> nodes = getDownlinkTraversal().relationships(relationshipType, Direction.OUTGOING)
.evaluator(getPropertyEvaluatorForType(nodeType)).traverse(parentNode).nodes().iterator();
if (nodes.hasNext()) {
result = nodes.next();
if (nodes.hasNext()) {
throwDuplicatedException = true;
}
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
if (throwDuplicatedException) {
throw new DuplicatedNodeException("child", nodeType.getId());
}
return result;
}
/*
* (non-Javadoc)
* @see org.amanzi.neo.services.INodeService#linkNodes(org.neo4j.graphdb.Node,
* org.neo4j.graphdb.Node, org.neo4j.graphdb.RelationshipType)
*/
@Override
public Relationship linkNodes(final Node startNode, final Node endNode, final RelationshipType relationshipType)
throws ServiceException {
assert startNode != null;
assert endNode != null;
assert relationshipType != null;
Relationship result = null;
final Transaction tx = getGraphDb().beginTx();
try {
result = startNode.createRelationshipTo(endNode, relationshipType);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
return result;
}
@Override
public void removeNodeProperty(final Node node, final String propertyName, final boolean throwExceptionIfNotExist)
throws ServiceException {
assert node != null;
assert !StringUtils.isEmpty(propertyName);
final boolean exists = node.hasProperty(propertyName);
if (exists) {
final Transaction tx = getGraphDb().beginTx();
try {
node.removeProperty(propertyName);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
} else if (throwExceptionIfNotExist) {
throw new PropertyNotFoundException(propertyName, node);
}
}
@Override
public void renameNodeProperty(final Node node, final String oldPropertyName, final String newPropertyName,
final boolean throwExceptionIfNotExist) throws ServiceException {
assert node != null;
assert !StringUtils.isEmpty(newPropertyName);
assert !StringUtils.isEmpty(oldPropertyName);
final Object value = getNodeProperty(node, oldPropertyName, null, false);
if (value != null) {
removeNodeProperty(node, oldPropertyName, false);
updateProperty(node, newPropertyName, value);
} else if (throwExceptionIfNotExist) {
throw new PropertyNotFoundException(oldPropertyName, node);
}
}
protected void updateContainerPropety(final PropertyContainer container, final String propertyName, final Object newValue)
throws DatabaseException {
assert container != null;
assert !StringUtils.isEmpty(propertyName);
assert newValue != null;
final boolean shouldWrite = !container.hasProperty(propertyName) || !container.getProperty(propertyName).equals(newValue);
if (shouldWrite) {
final Transaction tx = getGraphDb().beginTx();
try {
container.setProperty(propertyName, newValue);
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
}
}
@Override
public void updateProperties(final Node node, final Map<String, ? > properties) throws ServiceException {
Transaction tx = getGraphDb().beginTx();
try {
for (Entry<String, ? > property : properties.entrySet()) {
updateContainerPropety(node, property.getKey(), property.getValue());
}
tx.success();
} catch (final Exception e) {
tx.failure();
throw new DatabaseException(e);
} finally {
tx.finish();
}
}
@Override
public void updateProperty(final Node node, final String propertyName, final Object newValue) throws ServiceException {
updateContainerPropety(node, propertyName, newValue);
}
@Override
public void updateProperty(final Relationship relationship, final String propertyName, final Object newValue)
throws ServiceException {
updateContainerPropety(relationship, propertyName, newValue);
}
}