package fr.inria.atlanmod.neo4emf.drivers.impl;
/**
* Copyright (c) 2013 Atlanmod INRIA LINA Mines Nantes
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Atlanmod INRIA LINA Mines Nantes - initial API and implementation
* Descritpion ! To come
* @author Amine BENELALLAM
* */
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;
import org.neo4j.cypher.internal.pipes.matching.Relationships;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.traversal.Evaluators;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.kernel.Traversal;
import fr.inria.atlanmod.neo4emf.INeo4emfObject;
import fr.inria.atlanmod.neo4emf.RelationshipMapping;
import fr.inria.atlanmod.neo4emf.drivers.IPersistenceService;
import fr.inria.atlanmod.neo4emf.drivers.NEConfiguration;
import fr.inria.atlanmod.neo4emf.drivers.NEConnection;
import fr.inria.atlanmod.neo4emf.impl.Neo4emfObject;
public class PersistenceService implements IPersistenceService {
/**
* The database service connection.
*/
private final NEConnection connection;
private final RelationshipMapping mapping;
protected PersistenceService(GraphDatabaseService service, NEConfiguration configuration) {
this.connection = new NEConnection(service, configuration);
this.mapping = configuration.ePackage().getRelationshipMapping();
connection.open();
}
@Override
public Index<Node> getMetaIndex() {
return index().forNodes(META_ELEMENTS);
}
@Override
public Index<Relationship> getRelationshipIndex() {
return index().forRelationships(META_RELATIONSHIPS);
}
@Override
public Node getAttributeNode(Node n) {
Iterator<Relationship> setAttributeRels = n.getRelationships(SET_ATTRIBUTE).iterator();
while(setAttributeRels.hasNext()) {
return setAttributeRels.next().getEndNode();
}
return null;
}
private String getIdMetaValueFromClass(EClass c) {
return c.getEPackage().getName() + "_" + c.getClassifierID();
}
public Node createResourceNodeIfAbsent() {
if (getMetaIndex().get(ID_META, RESOURCE_NODE).getSingle() != null)
return getMetaIndex().get(ID_META, RESOURCE_NODE).getSingle();
Node n = createNode();
getMetaIndex().putIfAbsent(n, ID_META, RESOURCE_NODE);
return n;
}
@Override
public Node createNodeFromEObject(INeo4emfObject eObject) {
return createNodeFromEObject(eObject, false);
}
@Override
public Node createNodeFromEObject(INeo4emfObject eObject, boolean isTemporary) {
return connection.addObject(eObject,isTemporary);
}
@Override
public void createLink(INeo4emfObject from, INeo4emfObject to, EReference ref) {
//connection.createRelationship(from,to,getRelationshipFor(from.eClass().getClassifierID(), ref.getFeatureID()));
connection.createRelationship(from, to, getRelationshipFor(ref.getEContainingClass().getClassifierID(), ref.getFeatureID()));
}
@Override
public void removeLink(INeo4emfObject from, INeo4emfObject to, EReference ref) {
// connection.removeRelationship(from,to,getRelationshipFor(from.eClass().getClassifierID(), ref.getFeatureID()));
connection.createRelationship(from, to, getRelationshipFor(ref.getEContainingClass().getClassifierID(), ref.getFeatureID()));
}
@Override
public Node createAttributeNodeForEObject(INeo4emfObject eObject) {
return connection.addAttribute(eObject);
}
@Override
public void deleteNodeFromEObject(INeo4emfObject eObject) {
connection.removeObject(eObject);
}
@Override
public Relationship createAddLinkRelationship(INeo4emfObject from, INeo4emfObject to, EReference ref) {
return connection.addTmpRelationshipBetween(from, to, getRelationshipFor(ref.getEContainingClass().getClassifierID(), ref.getFeatureID()));
}
@Override
public Relationship createRemoveLinkRelationship(INeo4emfObject from, INeo4emfObject to, EReference ref) {
return connection.removeTmpRelationshipBetween(from, to, getRelationshipFor(from.eClass().getClassifierID(), ref.getFeatureID()));
}
@Override
public Relationship createDeleteRelationship(INeo4emfObject obj) {
return connection.addDeleteRelationship(obj);
}
@Override
public List<Relationship> getTmpRelationships() {
Iterator<Relationship> it = this.getRelationshipIndex().get(ID_META, TMP_RELATIONSHIP).iterator();
ArrayList<Relationship> rels = new ArrayList<Relationship>();
while(it.hasNext()) {
rels.add(it.next());
}
return rels;
}
@Override
public List<Node> getTmpNodes() {
Iterator<Node> it = this.getMetaIndex().get(ID_META, TMP_NODE).iterator();
ArrayList<Node> nodes = new ArrayList<Node>();
while(it.hasNext()) {
nodes.add(it.next());
}
return nodes;
}
@Override
public void flushTmpRelationships(List<Relationship> rels) {
connection.flushTmpRelationships(rels);
}
// TODO, check if it is still needed
private boolean isRoot(Neo4emfObject eObject) {
if (eObject.eContainer() == null && eObject.eResource() != null) {
return true;
} else {
return false;
}
}
@Override
public List<Node> getAllRootNodes() {
//Index<Node> index = getMetaIndex();
Node resourceNode = this.createResourceNodeIfAbsent();
assert resourceNode != null : "Null resource node";
return fetchNodesByRT(resourceNode.getId(), IS_ROOT);
}
private List<Node> fetchNodesByRT(long nodeId, RelationshipType relType, Direction direction) {
Traverser tvr = setUpTraversal(nodeId,relType,direction);
return traverseNodes(tvr);
}
private ArrayList<Node> fetchNodesWithAddLink(long nodeId, RelationshipType baseRelType, Direction direction) {
ArrayList<Node> nodes = new ArrayList<Node>();
Node startNode = getNodeById(nodeId);
Iterator<Relationship> it = startNode.getRelationships(ADD_LINK,Direction.OUTGOING).iterator();
while(it.hasNext()) {
Relationship r = it.next();
if(r.hasProperty("gen_rel") && r.getProperty("gen_rel").equals(baseRelType.name())) {
nodes.add(r.getEndNode());
}
}
return nodes;
}
private ArrayList<Node> fetchNodesWithRemoveLink(long nodeId, RelationshipType baseRelType, Direction direction) {
Traverser tvr = setUpTraversalRemoveLink(nodeId, direction);
return traverseNodesRemoveLink(tvr, baseRelType.name());
}
private List<Node> fetchNodesByRT(long nodeId, RelationshipType relType) {
return fetchNodesByRT(nodeId, relType, Direction.OUTGOING);
}
private ArrayList<Node> fetchNodesWithAddLink(long nodeId, RelationshipType baseRelType) {
return fetchNodesWithAddLink(nodeId, baseRelType, Direction.OUTGOING);
}
private ArrayList<Node> fetchNodesWithRemoveLink(long nodeId, RelationshipType baseRelType) {
return fetchNodesWithRemoveLink(nodeId, baseRelType, Direction.OUTGOING);
}
private List<Node> traverseNodes(Traverser tvr) {
ArrayList<Node> nodeList = new ArrayList<Node>();
for (Path path : tvr)
nodeList.add(path.endNode());
return nodeList;
}
private ArrayList<Node> traverseNodesRemoveLink(Traverser tvr, String baseRelTypeName) {
ArrayList<Node> nodeList = new ArrayList<Node>();
for(Path path : tvr) {
Relationship lastRelationship = path.lastRelationship();
if(lastRelationship.hasProperty("gen_rel") && lastRelationship.getProperty("gen_rel").equals(baseRelTypeName)) {
nodeList.add(path.endNode());
}
}
return nodeList;
}
protected Traverser setUpTraversal(long nodeId, RelationshipType relType,
Direction direction) {
Node startNode = getNodeById(nodeId);
TraversalDescription td = Traversal.description().breadthFirst()
.relationships(relType, direction)
.evaluator(Evaluators.excludeStartPosition())
.evaluator(Evaluators.toDepth(1));
return td.traverse(startNode);
}
protected Traverser setUpTraversalAddLink(long nodeId, Direction direction) {
Node startNode = getNodeById(nodeId);
TraversalDescription td = Traversal.description().breadthFirst()
.relationships(ADD_LINK,direction)
.evaluator(Evaluators.excludeStartPosition())
.evaluator(Evaluators.toDepth(1));
return td.traverse(startNode);
}
protected Traverser setUpTraversalRemoveLink(long nodeId, Direction direction) {
Node startNode = getNodeById(nodeId);
TraversalDescription td = Traversal.description().breadthFirst()
.relationships(REMOVE_LINK,direction)
.evaluator(Evaluators.excludeStartPosition())
.evaluator(Evaluators.toDepth(1));
return td.traverse(startNode);
}
// private Traverser setUpTraversal(long nodeId, RelationshipType relType) {
// return setUpTraversal(nodeId, relType, Direction.OUTGOING);
// }
@Override
public String getNodeType(Node n) {
List<Node> list = fetchNodesByRT(n.getId(), INSTANCE_OF);
return (String) (list.size() == 1 ? list.get(0)
.getProperty(ECLASS_NAME) : null);
}
@Override
public String getContainingPackage(Node n) {
List<Node> list = fetchNodesByRT(n.getId(), INSTANCE_OF);
return (String) (list.size() == 1 ? list.get(0).getProperty(NS_URI)
: null);
}
@Override
public List<Node> getNodesOnDemand(long nodeid,
RelationshipType relationshipType) {
return fetchNodesByRT(nodeid, relationshipType);
}
@Override
public List<Node> getAddLinkNodesOnDemand(long nodeid,
RelationshipType baseRelationshipType) {
return fetchNodesWithAddLink(nodeid, baseRelationshipType);
}
@Override
public List<Node> getRemoveLinkNodesOnDemand(long nodeid,
RelationshipType baseRelationshipType) {
return fetchNodesWithRemoveLink(nodeid,baseRelationshipType);
}
@Override
public boolean isRootNode(Node node) {
List<Node> nodes = fetchNodesByRT(node.getId(), IS_ROOT,
Direction.INCOMING);
if (nodes.size() == 0)
return false;
if (nodes.size() == 1 && nodes.get(0).equals(node))
return false;
return true;
}
@Override
public List<Node> getAllNodesOfType(EClass eClass) {
Node eClassNode = getMetaIndex().get(ID_META,
getIdMetaValueFromClass(eClass)).getSingle();
if (eClassNode == null)
return Collections.emptyList();
return fetchNodesByRT(eClassNode.getId(), INSTANCE_OF,
Direction.INCOMING);
}
public NETransaction createTransaction() {
return connection.createTransaction();
}
public void cleanIndexes() {
connection.cleanIndexes();
}
public RelationshipType getRelationshipFor(int classID, int referenceID) {
return mapping.relationshipAt(classID, referenceID);
}
public Node createNode() {
return connection.createNode();
}
public Node getNodeById(long id) {
return connection.getNodeById(id);
}
public Relationship getRelationshipById(long id) {
return connection.getRelationshipById(id);
}
public IndexManager index() {
return connection.index();
}
@Override
public void shutdown() {
connection.close();
}
}