blob: e84af033a47030cffc9dafcdb6af28e132dd9bc1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2018 The University of York, Aston University.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 3.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-3.0
*
* Contributors:
* Konstantinos Barmpis - initial API and implementation
* Antonio Garcia-Dominguez - improvements in proxy resolution
******************************************************************************/
package org.eclipse.hawk.graph.updater;
import java.io.File;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.eclipse.hawk.core.IModelIndexer;
import org.eclipse.hawk.core.IVcsManager;
import org.eclipse.hawk.core.VcsCommitItem;
import org.eclipse.hawk.core.graph.IGraphChangeListener;
import org.eclipse.hawk.core.graph.IGraphDatabase;
import org.eclipse.hawk.core.graph.IGraphEdge;
import org.eclipse.hawk.core.graph.IGraphNode;
import org.eclipse.hawk.core.graph.IGraphNodeIndex;
import org.eclipse.hawk.core.graph.IGraphTransaction;
import org.eclipse.hawk.core.graph.IGraphDatabase.Mode;
import org.eclipse.hawk.core.model.IHawkAttribute;
import org.eclipse.hawk.core.model.IHawkClass;
import org.eclipse.hawk.core.model.IHawkClassifier;
import org.eclipse.hawk.core.model.IHawkModelResource;
import org.eclipse.hawk.core.model.IHawkObject;
import org.eclipse.hawk.core.model.IHawkReference;
import org.eclipse.hawk.graph.FileNode;
import org.eclipse.hawk.graph.ModelElementNode;
import org.eclipse.hawk.graph.Slot;
import org.eclipse.hawk.graph.TypeNode;
import org.eclipse.hawk.graph.util.GraphUtil;
import org.eclipse.hawk.graph.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GraphModelBatchInjector {
private static final Logger LOGGER = LoggerFactory.getLogger(GraphModelBatchInjector.class);
public static final String FRAGMENT_DICT_NAME = "fragmentdictionary";
public static final String ROOT_DICT_FILE_KEY = FileNode.FILE_NODE_LABEL;
public static final String ROOT_DICT_NAME = "rootdictionary";
public static final String PROXY_DICT_NAME = "proxydictionary";
public static final String DERIVED_PROXY_DICT_NAME = "derivedproxydictionary";
// integer array containing the current number of added elements:
// (element,((ofType)M->MM)reference,((ofKind)M->MM)reference,(unset(M->M))reference)
private int[] objectCount = { 0, 0, 0, 0 };
private int unset;
private String repoURL;
private static enum ParseOptions {
MODELELEMENTS, MODELREFERENCES
};
private IGraphDatabase graph;
/*
* We don't keep the original objects, only the URIs. We split the URIs into path + fragment so
* we can use -XX:+UseStringDeduplication (new in Java 8u20) to save memory, and the underlying
* IGraphNodes only have identifiers (for further memory savings).
*/
private final Map<Pair<String, String>, IGraphNode> hash = new HashMap<>();
IGraphNodeIndex fileDictionary, proxyDictionary, rootDictionary, fragmentIdx,
derivedProxyDictionary;
long startTime;
private final IGraphChangeListener listener;
private final VcsCommitItem commitItem;
private final String tempDirURI;
private Mode previousMode = Mode.UNKNOWN;
private final TypeCache typeCache;
private boolean successState = true;
private void refreshIndexes() throws Exception {
// only do it if db state changed to avoid overhead
Mode currentMode = graph.currentMode();
if (previousMode != currentMode) {
previousMode = currentMode;
if (graph.currentMode().equals(Mode.TX_MODE)) {
try (IGraphTransaction t = graph.beginTransaction()) {
refreshIx();
t.success();
}
} else {
refreshIx();
}
}
}
private void refreshIx() {
fileDictionary = graph.getFileIndex();
proxyDictionary = graph.getOrCreateNodeIndex(PROXY_DICT_NAME);
rootDictionary = graph.getOrCreateNodeIndex(ROOT_DICT_NAME);
fragmentIdx = graph.getOrCreateNodeIndex(FRAGMENT_DICT_NAME);
derivedProxyDictionary = graph.getOrCreateNodeIndex(DERIVED_PROXY_DICT_NAME);
}
public GraphModelBatchInjector(IGraphDatabase g, TypeCache typeCache, VcsCommitItem s, IGraphChangeListener listener) throws Exception {
this.graph = g;
this.typeCache = typeCache;
this.commitItem = s;
this.listener = listener;
this.tempDirURI = new File(g.getTempDir()).toURI().toString();
refreshIndexes();
}
public GraphModelBatchInjector(IModelIndexer hawk, Supplier<DeletionUtils> deletionUtils, TypeCache typeCache, VcsCommitItem s, IHawkModelResource r, IGraphChangeListener listener, boolean verbose) throws Exception {
IGraphDatabase g = hawk.getGraph();
this.graph = g;
this.typeCache = typeCache;
this.commitItem = s;
this.listener = listener;
this.tempDirURI = new File(g.getTempDir()).toURI().toString();
startTime = System.nanoTime();
graph.enterBatchMode();
try {
listener.changeStart();
refreshIndexes();
boolean isNew = false;
repoURL = s.getCommit().getDelta().getManager().getLocation();
IGraphNode fileNode = null;
long filerevision = 0L;
try {
final String idQuery = repoURL + GraphModelUpdater.FILEINDEX_REPO_SEPARATOR + s.getPath();
fileNode = fileDictionary.get("id", idQuery).getSingle();
if (fileNode != null)
filerevision = (Long) fileNode.getProperty("revision");
} catch (Exception e) {
}
if (filerevision == 0L)
isNew = true;
if (isNew) {
// add file
if (fileNode == null) {
fileNode = addFileNode(s, listener);
}
try {
// add model elements
Iterable<IHawkObject> children = r.getAllContents();
startTime = System.nanoTime();
if (verbose) {
LOGGER.debug("Adding elements of file {}", s.getPath());
}
int[] addedElements = parseResource(fileNode, ParseOptions.MODELELEMENTS, children, hawk, r.providesSingletonElements());
fragmentIdx.flush();
if (verbose) {
LOGGER.debug("{} NODES AND {} M->MM REFERENCES! (took ~{}sec)",
addedElements[0], addedElements[1], addedElements[2],
(System.nanoTime() - startTime) / 1_000_000_000
);
}
// add references
startTime = System.nanoTime();
if (verbose) {
LOGGER.debug("Adding edges of file {}", s.getPath());
}
addedElements = parseResource(fileNode, ParseOptions.MODELREFERENCES, children, hawk,
r.providesSingletonElements());
setUnset(getUnset() + addedElements[3]);
if (verbose) {
LOGGER.debug("{} REFERENCES! (took ~{} sec)", addedElements[0], (System.nanoTime() - startTime) / 1_000_000_000);
}
listener.changeSuccess();
successState = true;
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
IGraphNode n = new Utils().getFileNodeFromVCSCommitItem(graph, s);
if (n != null) {
try (IGraphTransaction t = g.beginTransaction()) {
deletionUtils.get().deleteAll(n, s, listener);
t.success();
} catch (Exception e2) {
LOGGER.error("error in reverting from erroneous batch insert", e2);
}
}
listener.changeFailure();
successState = false;
}
} else /* if not new */ {
LOGGER.warn("GraphModelBatchInjector used with a model already in Hawk.");
listener.changeSuccess();
successState = true;
}
} catch (Exception ex) {
successState = false;
LOGGER.error(ex.getMessage(), ex);
listener.changeFailure();
}
}
private IGraphNode addFileNode(VcsCommitItem s, IGraphChangeListener listener) {
IGraphNode fileNode;
Map<String, Object> mapForFileNode = new HashMap<>();
mapForFileNode.put(IModelIndexer.IDENTIFIER_PROPERTY, s.getPath());
mapForFileNode.put("revision", s.getCommit().getRevision());
mapForFileNode.put(FileNode.PROP_REPOSITORY, repoURL);
fileNode = graph.createNode(mapForFileNode, FileNode.FILE_NODE_LABEL);
Map<String, Object> mapForDictionary = new HashMap<>();
mapForDictionary.put("id", repoURL + GraphModelUpdater.FILEINDEX_REPO_SEPARATOR + s.getPath());
fileDictionary.add(fileNode, mapForDictionary);
// propagate changes to listeners
listener.fileAddition(s, fileNode);
return fileNode;
}
/**
* Adds the resource to the graph according to whether it is a model or a
* metamodel resource
*
* @param originatingFile
*
* @param parseOption
* @param resource
* @param graph
* @return
*/
private int[] parseResource(IGraphNode originatingFile, ParseOptions parseOption, Iterable<IHawkObject> children,
IModelIndexer hawk, boolean resourceCanProvideSingletons) throws Exception {
graph.enterBatchMode();
objectCount[0] = 0;
objectCount[1] = 0;
objectCount[2] = 0;
objectCount[3] = 0;
final String fileID = originatingFile.getProperty(IModelIndexer.IDENTIFIER_PROPERTY) + "";
long init = System.nanoTime();
int lastprint = 0;
for (IHawkObject child : children) {
switch (parseOption) {
case MODELELEMENTS:
addEObject(originatingFile, child, resourceCanProvideSingletons);
break;
case MODELREFERENCES:
addEReferences(child, resourceCanProvideSingletons);
break;
default:
LOGGER.error("parse option {} not recognised!", parseOption);
}
if (lastprint < objectCount[0] - 50000) {
lastprint = objectCount[0];
final String out = String.format("Adding %s: %d %d sec (%d sec total) to %s",
parseOption == ParseOptions.MODELELEMENTS ? "nodes" : "references", objectCount[0],
(System.nanoTime() - init) / 1_000_000_000, (System.nanoTime() - startTime) / 1_000_000_000,
fileID);
hawk.getCompositeStateListener().info(out);
init = System.nanoTime();
}
}
return objectCount;
}
/**
*
* @param eObject
* @return the URI ID of an eObject
*/
private String getEObjectId(IHawkObject eObject) {
return eObject.getUriFragment();
}
/**
* Creates a node in the graph database with the given eObject's attributes
* in it. Also indexes it in the 'dictionary' index.
*
* @param eObject
* @return the Node
* @throws Exception
*/
private IGraphNode createEObjectNode(IHawkObject eObject, IGraphNode typenode) throws Exception {
try {
final List<IHawkAttribute> normalAttributes = new ArrayList<IHawkAttribute>();
final List<IHawkAttribute> indexedAttributes = new ArrayList<IHawkAttribute>();
IGraphNode node = createBasicEObjectNode(eObject, normalAttributes, indexedAttributes);
// Derived features and indexed attributes
final IHawkClassifier classifier = eObject.getType();
addDerivedFeatureNodes(node, typenode, classifier.getPackageNSURI(), typeCache.getEClassNodeSlots(graph, classifier));
for (IHawkAttribute a : indexedAttributes) {
IGraphNodeIndex i = graph.getOrCreateNodeIndex(String.format("%s##%s##%s",
classifier.getPackageNSURI(), eObject.getType().getName(), a.getName()));
final Object rawValue = eObject.get(a);
i.add(node, a.getName(), convertValue(a, rawValue));
}
return node;
} catch (Exception e) {
LOGGER.error("Error in inserting attributes", e);
}
return null;
}
/**
* Creates the model element node in the graph. Does not index nor add derived feature nodes.
*/
private IGraphNode createBasicEObjectNode(IHawkObject eObject, final List<IHawkAttribute> normalAttributes, final List<IHawkAttribute> indexedAttributes) throws Exception {
final String eObjectId = getEObjectId(eObject);
final Map<String, Object> nodeMap = new HashMap<>();
nodeMap.put(IModelIndexer.IDENTIFIER_PROPERTY, eObjectId);
nodeMap.put(IModelIndexer.SIGNATURE_PROPERTY, eObject.signature());
classifyAttributes(eObject, normalAttributes, indexedAttributes);
for (IHawkAttribute a1 : normalAttributes) {
final Object value1 = eObject.get(a1);
nodeMap.put(a1.getName(), convertValue(a1, value1));
}
try {
IGraphNode node = graph.createNode(nodeMap, ModelElementNode.OBJECT_VERTEX_LABEL);
if (eObject.isFragmentUnique()) {
fragmentIdx.add(node, "id", eObject.getUriFragment());
}
// propagate changes to listeners
listener.modelElementAddition(commitItem, eObject, node, false);
for (String s : nodeMap.keySet()) {
Object value = nodeMap.get(s);
listener.modelElementAttributeUpdate(commitItem, eObject, s, null, value, node,
ModelElementNode.TRANSIENT_ATTRIBUTES.contains(s));
}
return node;
} catch (Throwable ex) {
LOGGER.error(ex.getMessage(), ex);
}
return null;
}
private Object convertValue(IHawkAttribute attribute, final Object value) {
if (!attribute.isMany()) {
final Class<?> valueClass = value.getClass();
if (GraphUtil.isPrimitiveOrWrapperType(valueClass)) {
return value;
} else if (value instanceof Date) {
return formatDate((Date)value);
} else {
return value.toString();
}
} else {
Collection<Object> collection;
if (attribute.isUnique()) {
collection = new LinkedHashSet<Object>();
} else {
collection = new LinkedList<Object>();
}
final Collection<?> srcCollection = (Collection<?>) value;
Class<?> elemClass = null;
boolean primitiveOrWrapperClass = false;
if (!srcCollection.isEmpty()) {
final Object first = srcCollection.iterator().next();
elemClass = first.getClass();
primitiveOrWrapperClass = GraphUtil.isPrimitiveOrWrapperType(elemClass);
if (primitiveOrWrapperClass) {
for (Object o : srcCollection) {
collection.add(o);
}
} else if (first instanceof Date) {
for (Object o : srcCollection) {
collection.add(formatDate((Date)o));
}
} else {
for (Object o : srcCollection) {
collection.add(o.toString());
}
}
}
Object r = null;
if (primitiveOrWrapperClass && elemClass != null) {
r = Array.newInstance(elemClass, collection.size());
} else {
r = Array.newInstance(String.class, collection.size());
}
Object ret = collection.toArray((Object[]) r);
return ret;
}
}
protected Object formatDate(final Date value) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
return sdf.format(value);
}
/**
* Adds all set attributes to <code>allAttributes</code>, and all set and
* indexed attributes to <code>indexedAttributes</code>.
*/
private void classifyAttributes(IHawkObject eObject, final List<IHawkAttribute> allAttributes, final List<IHawkAttribute> indexedAttributes) throws Exception {
final IHawkClass iHawkClass = (IHawkClass) eObject.getType();
for (final IHawkAttribute eAttribute : iHawkClass.getAllAttributes()) {
if (eObject.isSet(eAttribute)) {
final Map<String, Slot> slots = typeCache.getEClassNodeSlots(graph, eObject.getType());
final Slot slot = slots.get(eAttribute.getName());
if (slot == null) {
LOGGER.error("Attribute {} is not within the properties of the node for type {}, skipping",
eAttribute.getName(), iHawkClass.getName());
} else {
allAttributes.add(eAttribute);
if (slot.isIndexed()) {
indexedAttributes.add(eAttribute);
}
}
}
}
}
private void addDerivedFeatureNodes(IGraphNode node, IGraphNode typenode, String metamodelURI, Map<String, Slot> slots) {
TypeNode tn = new TypeNode(typenode);
for (Slot slot : slots.values()) {
if (!slot.isDerived()) {
continue;
}
Map<String, Object> dfnAttributes = new HashMap<>();
dfnAttributes.put("isMany", slot.isMany());
dfnAttributes.put("isOrdered", slot.isOrdered());
dfnAttributes.put("isUnique", slot.isUnique());
dfnAttributes.put(GraphModelInserter.DERIVED_ATTR_TYPE, slot.getType());
dfnAttributes.put("derivationlanguage", slot.getDerivationLanguage());
dfnAttributes.put(GraphModelInserter.DERIVED_ATTR_LOGIC, slot.getDerivationLogic());
/*
* We cannot use tn.getMetamodelURI() - in Neo4j batch mode, we can't get the
* outgoing edges of a certain type and instead we get all outgoing edges. Instead,
* we rely on the metamodel URI reported by the IHawkClassifier.
*/
final String idxName = String.format("%s##%s##%s", metamodelURI, tn.getTypeName(), slot.getName());
dfnAttributes.put(GraphModelInserter.DERIVED_IDXNAME_NODEPROP, idxName);
dfnAttributes.put(slot.getName(),
DirtyDerivedFeaturesListener.NOT_YET_DERIVED_PREFIX + slot.getDerivationLogic());
IGraphNode derivedattributenode = graph.createNode(dfnAttributes, "derivedattribute");
graph.createRelationship(node, derivedattributenode, slot.getName(),
Collections.singletonMap(GraphModelInserter.DERIVED_FEATURE_EDGEPROP, true));
addToProxyAttributes(derivedattributenode);
}
}
private void addToProxyAttributes(IGraphNode node) {
Map<String, Object> m = new HashMap<>();
m.put("derived", "_");
derivedProxyDictionary.add(node, m);
}
/**
* Creates a node with the eObject, adds it to the hash and adds it the the
* appropriate eClass in the metatracker collection
*
* @param originatingFile
*
* @param eObject
* @return
* @throws Exception
*/
public IGraphNode addEObject(IGraphNode originatingFile, IHawkObject eObject,
boolean resourceCanProvideSingletons) throws Exception {
refreshIndexes();
IGraphNode eClass = typeCache.getEClassNode(graph, eObject.getType());
IGraphNode node = null;
// if a unique element is already in hawk set the node to that element
// and return it
if (resourceCanProvideSingletons && eObject.isFragmentUnique()) {
node = getFromFragmentIndex(eObject);
// if this node is from a file not yet registered, add this file
// reference
if (node != null && originatingFile != null) {
boolean found = false;
for (IGraphEdge e : node.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE))
if (e.getEndNode().equals(originatingFile))
found = true;
if (!found)
createReference(ModelElementNode.EDGE_LABEL_FILE, node, originatingFile,
new HashMap<String, Object>(), true);
}
}
if (node == null) {
node = createEObjectNode(eObject, eClass);
if (node == null) {
LOGGER.error("The node for {} is null", eObject);
} else {
hash.put(splitURI(eObject.getUri()), node);
final HashMap<String, Object> emptyMap = new HashMap<String, Object>();
createReference(ModelElementNode.EDGE_LABEL_OFTYPE, node, eClass, emptyMap, true);
createReference(ModelElementNode.EDGE_LABEL_OFKIND, node, eClass, emptyMap, true);
if (originatingFile != null) {
createReference(ModelElementNode.EDGE_LABEL_FILE, node, originatingFile, emptyMap, true);
}
objectCount[1]++;
// use metamodel to infer all supertypes for fast search and log them
for (IHawkClass superType : ((IHawkClass) eObject.getType()).getAllSuperTypes()) {
eClass = typeCache.getEClassNode(graph, superType);
createReference(ModelElementNode.EDGE_LABEL_OFKIND, node, eClass, emptyMap, true);
objectCount[2]++;
}
objectCount[0]++;
if (eObject.isRoot()) {
rootDictionary.add(node, ROOT_DICT_FILE_KEY, originatingFile.getId().toString());
}
}
}
return node;
}
private Pair<String, String> splitURI(String uri) {
final String[] parts = uri.split("#", 1);
if (parts.length == 1) {
return new Pair<>(parts[0], "");
} else {
return new Pair<>(parts[0], parts[1]);
}
}
private IGraphNode getFromFragmentIndex(IHawkObject eObject) {
IGraphNode node = null;
final Iterator<? extends IGraphNode> itr = fragmentIdx.get("id", eObject.getUriFragment()).iterator();
while (itr.hasNext()) {
if (node == null)
node = itr.next();
else {
LOGGER.warn("isFragmentUnique returned more than one node, keeping first one.");
break;
}
}
return node;
}
/**
* Creates an edge with the parameters given and links it to the appropriate
* nodes. Both nodes are contained in the same resource.
*
* @param from
* @param to
* @param edgelabel
* @throws Exception
*/
private void addEdge(IHawkObject from, IHawkObject to, final String edgelabel, boolean isContainment,
boolean isContainer) throws Exception {
IGraphNode source = null;
IGraphNode destination = null;
source = hash.get(splitURI(from.getUri()));
destination = hash.get(splitURI(to.getUri()));
if (source == null && destination == null) {
LOGGER.warn(
"hash error 1, not found from (class: {}) and to (class: {}) on reference: {}, source = {}, destination = {}",
from.getType().getName(), ((IHawkObject) to).getType().getName(), edgelabel, source, destination
);
} else if (source == null) {
LOGGER.warn(
"hash error 2, not found from (class: {}) and to (class: {}) on reference: {}, source = {}, destination = {}",
from.getType().getName(), ((IHawkObject) to).getType().getName(), edgelabel, source, destination
);
} else if (destination == null) {
// the modelling technology managed to resolve a cross-file proxy
// early (before it was inserted into hawk -- handle it like any
// other proxy)
addProxyRef(from, to, edgelabel, isContainment, isContainer);
} else {
Map<String, Object> props = new HashMap<String, Object>();
if (isContainment)
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINMENT, "true");
if (isContainer)
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINER, "true");
createReference(edgelabel, source, destination, props, false);
objectCount[0]++;
}
}
private void createReference(final String edgelabel, IGraphNode source, IGraphNode destination,
Map<String, Object> props, boolean isTransient) {
graph.createRelationship(source, destination, edgelabel, props);
listener.referenceAddition(commitItem, source, destination, edgelabel, isTransient);
}
/**
* Iterates through all of the references the eObject has and inserts them
* into the graph -- not using hash -- for transactional update
*
* @param source
* @param addedNodesHash
*
* @param eObject
* @return
* @throws Exception
*/
protected boolean addEReferences(IGraphNode fileNode, IGraphNode node, IHawkObject source,
Map<String, IGraphNode> addedNodesHash, Map<String, IGraphNode> nodes) throws Exception {
refreshIndexes();
boolean ret = true;
try {
for (final IHawkReference eReference : ((IHawkClass) source.getType()).getAllReferences()) {
if (source.isSet(eReference)) {
String edgelabel = eReference.getName();
Object destinationObject = source.get(eReference, false);
if (destinationObject instanceof Iterable<?>) {
for (Object destinationEObject : ((Iterable<?>) destinationObject)) {
final IHawkObject destinationHawkObject = (IHawkObject) destinationEObject;
if (!destinationHawkObject.isInDifferentResourceThan(source)) {
IGraphNode dest = null;
dest = addedNodesHash.get(destinationHawkObject.getUriFragment());
if (dest == null)
dest = nodes.get(destinationHawkObject.getUriFragment());
Map<String, Object> props = new HashMap<String, Object>();
if (eReference.isContainment()) {
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINMENT, "true");
}
if (eReference.isContainer()) {
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINER, "true");
}
createReference(edgelabel, node, dest, props, false);
} else {
addProxyRef(node, destinationHawkObject, edgelabel, eReference.isContainment(),
eReference.isContainer());
}
}
} else {
final IHawkObject destinationHawkObject = (IHawkObject) destinationObject;
if (!destinationHawkObject.isInDifferentResourceThan(source)) {
IGraphNode dest = addedNodesHash.get(destinationHawkObject.getUriFragment());
if (dest == null)
dest = nodes.get(destinationHawkObject.getUriFragment());
Map<String, Object> props = new HashMap<String, Object>();
if (eReference.isContainment()) {
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINMENT, "true");
}
if (eReference.isContainer()) {
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINER, "true");
}
createReference(edgelabel, node, dest, props, false);
} else {
addProxyRef(node, destinationHawkObject, edgelabel, eReference.isContainment(),
eReference.isContainer());
}
}
}
}
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
ret = false;
}
return ret;
}
/**
* Iterates through all of the references the eObject has and inserts them
* into the graph -- for batch updates
*
* @param originatingFile
*
* @param source
* @throws Exception
*/
public boolean addEReferences(IHawkObject source, boolean resourceCanProvideSingletons) throws Exception {
boolean atLeastOneSetReference = false;
if (source.isFragmentUnique() && resourceCanProvideSingletons && hash.get(splitURI(source.getUri())) == null) {
// Avoid trying to add references from a singleton object we already had
return atLeastOneSetReference;
}
for (final IHawkReference eReference : ((IHawkClass) source.getType()).getAllReferences()) {
if (source.isSet(eReference)) {
atLeastOneSetReference = true;
final String edgelabel = eReference.getName();
final Object destinationObject = source.get(eReference, false);
if (destinationObject instanceof Iterable<?>) {
for (Object destinationEObject : ((Iterable<?>) destinationObject)) {
final IHawkObject destinationHawkObject = (IHawkObject) destinationEObject;
if (!destinationHawkObject.isInDifferentResourceThan(source)) {
addEdge(source, destinationHawkObject, edgelabel, eReference.isContainment(),
eReference.isContainer());
} else {
addProxyRef(source, destinationHawkObject, edgelabel, eReference.isContainment(),
eReference.isContainer());
}
}
} else /* if destination is not iterable */ {
final IHawkObject destinationHawkObject = (IHawkObject) destinationObject;
if (!destinationHawkObject.isInDifferentResourceThan(source)) {
addEdge(source, destinationHawkObject, edgelabel, eReference.isContainment(),
eReference.isContainer());
} else {
addProxyRef(source, destinationHawkObject, edgelabel, eReference.isContainment(),
eReference.isContainer());
}
}
} else /* if reference is not set */ {
objectCount[3]++;
}
}
return atLeastOneSetReference;
}
private boolean addProxyRef(IHawkObject from, IHawkObject destinationObject, String edgelabel,
boolean isContainment, boolean isContainer) {
IGraphNode withProxy = hash.get(splitURI(from.getUri()));
return addProxyRef(withProxy, destinationObject, edgelabel, isContainment, isContainer);
}
private boolean addProxyRef(IGraphNode node, IHawkObject destinationObject, String edgelabel, boolean isContainment,
boolean isContainer) {
try {
final String uri = destinationObject.getUri();
String relativeObjectURI = uri;
if (destinationObject.isFragmentUnique()) {
/*
* In this scenario, we don't care about the file anymore: we
* need to flag it properly for the later resolution.
*/
relativeObjectURI = GraphModelUpdater.PROXY_FILE_WILDCARD + "#"
+ relativeObjectURI.substring(relativeObjectURI.indexOf("#") + 1);
} else if (!destinationObject.URIIsRelative()) {
/*
* This is an absolute file-based path: strip out the prefix, which
* may be the temporary import directory or a VCS-specific prefix.
*/
if (relativeObjectURI.startsWith(tempDirURI)) {
relativeObjectURI = relativeObjectURI.substring(tempDirURI.length());
} else {
final IVcsManager vcs = commitItem.getCommit().getDelta().getManager();
relativeObjectURI = vcs.getRepositoryPath(relativeObjectURI);
}
}
final String relativeObjectFileURI =
relativeObjectURI.substring(0, relativeObjectURI.indexOf("#"));
final String fullObjectURI = repoURL + GraphModelUpdater.FILEINDEX_REPO_SEPARATOR + relativeObjectURI;
final String fullObjectFileURI = repoURL + GraphModelUpdater.FILEINDEX_REPO_SEPARATOR + relativeObjectFileURI;
Object proxies = node.getProperty(GraphModelUpdater.PROXY_REFERENCE_PREFIX + fullObjectFileURI);
proxies = new Utils().addToElementProxies((String[]) proxies, fullObjectURI, edgelabel, isContainment, isContainer);
node.setProperty(GraphModelUpdater.PROXY_REFERENCE_PREFIX + fullObjectFileURI, proxies);
proxyDictionary.add(node, Collections.singletonMap(
GraphModelUpdater.PROXY_REFERENCE_PREFIX, fullObjectFileURI));
} catch (Exception e) {
LOGGER.error("proxydictionary error", e);
return false;
}
return true;
}
/**
* Creates the edge resolving a proxy reference, if it is not already there.
*
* @return {@code true} if a new edge was created, or {@code false} if the edge was already there.
*/
protected boolean resolveProxyRef(IGraphNode source, IGraphNode target, String edgeLabel, boolean isContainment, boolean isContainer) throws Exception {
refreshIndexes();
for (IGraphEdge e : source.getOutgoingWithType(edgeLabel)) {
if (e.getEndNode().getId().equals(target.getId())) {
// Edge is already there: do nothing
return false;
}
}
final HashMap<String, Object> props = new HashMap<String, Object>();
if (isContainment) {
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINMENT, "true");
} else if (isContainer) {
props.put(ModelElementNode.EDGE_PROPERTY_CONTAINER, "true");
}
graph.createRelationship(source, target, edgeLabel, props);
return true;
}
public int getUnset() {
return unset;
}
private void setUnset(int unset) {
this.unset = unset;
}
public boolean getSuccess() {
return successState;
}
}