/******************************************************************************* | |
* 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; | |
} | |
} |