blob: 241d243509ff0cbda2c38b73b101760669120623 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2020 The University of York.
*
* 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 - protect against null EPackage nsURIs
******************************************************************************/
package org.eclipse.hawk.graph.updater;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.eclipse.hawk.core.IMetaModelResourceFactory;
import org.eclipse.hawk.core.IModelIndexer;
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.IGraphIterable;
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.model.IHawkAttribute;
import org.eclipse.hawk.core.model.IHawkClass;
import org.eclipse.hawk.core.model.IHawkClassifier;
import org.eclipse.hawk.core.model.IHawkDataType;
import org.eclipse.hawk.core.model.IHawkMetaModelResource;
import org.eclipse.hawk.core.model.IHawkObject;
import org.eclipse.hawk.core.model.IHawkPackage;
import org.eclipse.hawk.core.model.IHawkReference;
import org.eclipse.hawk.core.model.IHawkStructuralFeature;
import org.eclipse.hawk.core.runtime.CompositeGraphChangeListener;
import org.eclipse.hawk.core.util.CompositeException;
import org.eclipse.hawk.graph.FileNode;
import org.eclipse.hawk.graph.ModelElementNode;
import org.eclipse.hawk.graph.Slot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GraphMetaModelResourceInjector {
private static final Logger LOGGER = LoggerFactory.getLogger(GraphMetaModelResourceInjector.class);
/**
* Represents the case when the insertion of a metamodel fails.
*/
public static class FailedMetamodelRegistrationException extends Exception {
private static final long serialVersionUID = 1L;
public FailedMetamodelRegistrationException(String metamodelURI, Throwable reason) {
super(String.format("Failed to register metamodel '%s': %s", metamodelURI, reason.getMessage()), reason);
}
}
/**
* Represents the situation when a particular metamodel is missing, so a certain
* EClass cannot be registered.
*/
public static class MissingMetamodelException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public MissingMetamodelException(String metamodelURI) {
super(String.format("Missing metamodel with URI '%s'", metamodelURI));
}
}
// 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;
private IModelIndexer hawk;
private IGraphDatabase graph;
private final long startTime = System.nanoTime();
private final CompositeGraphChangeListener listener;
public GraphMetaModelResourceInjector(IModelIndexer hawk, Set<IHawkMetaModelResource> set, CompositeGraphChangeListener listener) throws Exception {
this.hawk = hawk;
this.graph = hawk.getGraph();
this.listener = listener;
LOGGER.info("ADDING METAMODELS: ");
LOGGER.info("ADDING: ");
int nodes = insertMetamodels(set);
LOGGER.info("{} METAMODEL NODES! (took ~{} sec)", nodes, (System.nanoTime() - startTime) / 1_000_000_000);
}
public GraphMetaModelResourceInjector(IModelIndexer hawk, CompositeGraphChangeListener listener) {
this.hawk = hawk;
this.graph = hawk.getGraph();
this.listener = listener;
}
/**
* Removes all the metamodel resources and their dependent model elements from the index.
*/
public void removeMetamodels(Set<IHawkMetaModelResource> set) {
try (IGraphTransaction t = graph.beginTransaction()) {
listener.changeStart();
IGraphNodeIndex ePackageDictionary = graph.getMetamodelIndex();
Set<IGraphNode> epns = new HashSet<>();
for (IHawkMetaModelResource metamodelResource : set) {
// if (resourceset == null)
// resourceset = metamodelResource.getResourceSet();
Set<IHawkObject> children = metamodelResource.getAllContents();
for (IHawkObject child : children) {
// if (child.eIsProxy()) {
// throw new Exception("FAILED. PROXY UNRESOLVED: "
// + ((InternalEObject) child).eProxyURI()
// .fragment());
// }
// add the element
if (child instanceof IHawkPackage) {
Iterator<? extends IGraphNode> it = ePackageDictionary.get("id", ((IHawkPackage) child).getNsURI()).iterator();
if (!it.hasNext()) {
LOGGER.warn("Metamodel: {} with uri: {} not indexed. Nothing happened.",
((IHawkPackage) child).getName(), ((IHawkPackage) child).getNsURI());
} else {
IGraphNode epn = it.next();
LOGGER.info("Removing metamodel: {} with uri: {}", ((IHawkPackage) child).getName(), ((IHawkPackage) child).getNsURI());
epns.add(epn);
}
}
}
}
removeAll(epns);
t.success();
listener.changeSuccess();
} catch (Exception e1) {
listener.changeFailure();
LOGGER.error("Error in removing metamodels (all removal changes reverted)", e1);
}
}
/**
* Removes all the metamodels and models dependent on the <code>epsn</code> metamodel nodes.
*
* @return URIs of the repositories impacted by the removal.
*/
private Set<String> removeAll(Set<IGraphNode> epns) throws Exception {
Set<String> affectedRepositories = new HashSet<>();
DeletionUtils del = new DeletionUtils(graph);
for (IGraphNode epn : epns)
for (IGraphEdge rel : epn.getIncomingWithType(IModelIndexer.METAMODEL_DEPENDENCY_EDGE)) {
LOGGER.debug("dependency from {} to {}",
rel.getStartNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY),
epn.getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
// delete all dependent metamodels and models
IGraphNode depmm = rel.getStartNode();
del.delete(rel);
epns.add(depmm);
}
Set<IGraphNode> files = new HashSet<>();
for (IGraphNode n : epns)
files.addAll(remove(n));
for (IGraphNode file : files)
affectedRepositories.add(file.getProperty(FileNode.PROP_REPOSITORY).toString());
for (IGraphNode file : files)
del.delete(file);
return affectedRepositories;
}
private Set<IGraphNode> remove(IGraphNode epn) throws Exception {
long start = System.currentTimeMillis();
HashSet<IGraphNode> fileNodes = new HashSet<IGraphNode>();
try (IGraphTransaction transaction = graph.beginTransaction()) {
LOGGER.info("Deleting nodes from metamodel: {}", epn.getProperty(IModelIndexer.IDENTIFIER_PROPERTY));
HashSet<IGraphNode> metaModelElements = new HashSet<IGraphNode>();
HashSet<IGraphNode> modelElements = new HashSet<IGraphNode>();
DeletionUtils del = new DeletionUtils(graph);
for (IGraphEdge rel : epn.getIncomingWithType("epackage")) {
metaModelElements.add(rel.getStartNode());
del.delete(rel);
}
for (IGraphEdge rel : epn.getOutgoingWithType(IModelIndexer.METAMODEL_DEPENDENCY_EDGE)) {
del.delete(rel);
}
del.delete(epn);
for (IGraphNode metamodelelement : metaModelElements) {
for (IGraphEdge rel : metamodelelement.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFTYPE)) {
modelElements.add(rel.getStartNode());
del.delete(rel);
}
}
for (IGraphNode metamodelelement : metaModelElements) {
for (IGraphEdge rel : metamodelelement.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFKIND)) {
modelElements.add(rel.getStartNode());
del.delete(rel);
}
}
for (IGraphNode metaModelElement : metaModelElements)
del.delete(metaModelElement);
// remove model elements and update derived attributes
if (modelElements.size() > 0) {
LOGGER.info("Deleting nodes from relevant models...");
Set<IGraphNode> toBeUpdated = new HashSet<>();
final DirtyDerivedFeaturesListener l = new DirtyDerivedFeaturesListener(graph);
if (!hawk.getDerivedAttributes().isEmpty()) {
hawk.addGraphChangeListener(l);
}
for (IGraphNode modelElement : modelElements) {
Iterator<IGraphEdge> it = modelElement.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE)
.iterator();
if (it.hasNext()) {
IGraphEdge e = it.next();
fileNodes.add(e.getEndNode());
del.delete(e);
}
del.dereference(modelElement, listener, null);
}
for (IGraphNode modelElement : modelElements) {
del.delete(modelElement);
}
toBeUpdated.addAll(l.getNodesToBeUpdated());
listener.remove(l);
LOGGER.info("Updating any relevant derived attributes...");
try {
new GraphModelInserter(hawk, () -> del, new TypeCache(hawk))
.updateDerivedAttributes(hawk.getDerivedAttributeExecutionEngine(), toBeUpdated);
toBeUpdated = new HashSet<>();
} catch (Exception e) {
toBeUpdated = new HashSet<>();
LOGGER.error("Exception while updating derived attributes", e);
}
}
transaction.success();
}
LOGGER.info("deleted all, took: {}s", (System.currentTimeMillis() - start) / 1000.0);
return fileNodes;
}
/**
* Inserts a number of metamodels into the graph.
*
* @param metamodels Metamodel resources to be registered.
* @return Number of added objects.
* @throws A single exception, or a {@link CompositeException} with multiple
* exceptions that have happened for this set of metamodels.
*/
private int insertMetamodels(Set<IHawkMetaModelResource> metamodels) throws Exception {
// Expand set with dependencies if needed
Map<String, IHawkPackage> expanded = new HashMap<>();
expandWithDependencies(metamodels, expanded);
// TODO: simplify rest of code after testing early dependency detection
// Create the EPackage nodes
Set<IHawkPackage> addedPackages = new HashSet<>();
try (IGraphTransaction t = graph.beginTransaction()) {
listener.changeStart();
IGraphNodeIndex ePackageDictionary = graph.getMetamodelIndex();
for (IHawkPackage pkg : expanded.values()) {
if (addEPackage(pkg, ePackageDictionary)) {
addedPackages.add(pkg);
}
}
t.success();
listener.changeSuccess();
} catch (Exception ex) {
listener.changeFailure();
throw ex;
}
// Create the EClass nodes inside each EPackage
List<Exception> exceptions = new ArrayList<>();
Iterator<IHawkPackage> itPackage = addedPackages.iterator();
while (itPackage.hasNext()) {
IHawkPackage epackage = itPackage.next();
boolean revert = false;
final IGraphTransaction t = graph.beginTransaction();
try {
listener.changeStart();
addEClasses(epackage);
t.success();
listener.changeSuccess();
} catch (Exception eAddPackage) {
LOGGER.error(eAddPackage.getMessage(), eAddPackage);
t.failure();
listener.changeFailure();
exceptions.add(new FailedMetamodelRegistrationException(epackage.getNsURI(), eAddPackage));
itPackage.remove();
revert = true;
} finally {
t.close();
}
if (revert) {
// Revert the addition of the metamodel node
try (IGraphTransaction t2 = graph.beginTransaction()) {
IGraphNode ePackageNode = graph.getMetamodelIndex().get("id", epackage.getNsURI()).iterator().next();
new DeletionUtils(graph).delete(ePackageNode);
t2.success();
} catch (Exception eRevertAddition) {
LOGGER.error(eRevertAddition.getMessage(), eRevertAddition);
exceptions.add(eRevertAddition);
}
}
}
for (IHawkPackage ePackage : addedPackages) {
try (IGraphTransaction t = graph.beginTransaction()) {
final IGraphNode epackagenode = graph.getMetamodelIndex().get("id", ePackage.getNsURI()).getSingle();
// add resource to package
final Optional<String> s = ePackage.getResource().getMetaModelResourceFactory().dumpPackageToString(ePackage);
if (s.isPresent()) {
epackagenode.setProperty(IModelIndexer.METAMODEL_RESOURCE_PROPERTY, s.get());
}
t.success();
} catch (Exception eSaveResource) {
LOGGER.error("Cannot save package into the metamodel node", eSaveResource);
exceptions.add(eSaveResource);
}
}
// Throw any collected exceptions if we have them
if (!exceptions.isEmpty()) {
throw new CompositeException(exceptions);
}
return objectCount;
}
/**
* Performs dependency resolution on a set of metamodels, by applying a
* fixed-point algorithm where we keep adding metamodels as we find new
* dependencies, until we do not have any pending metamodels or we run into a
* metamodel that we cannot obtain.
*
* If we successfully exit from this method, it means that the value set of
* {@code expanded} is complete, perhaps reusing some packages that are already
* in the graph.
*
* @throws MissingMetamodelException A metamodel that this set depends upon
* could not be found.
* @throws Exception Another type of exception occurred during
* the dependency resolution. This will most
* likely be an issue with the node indices.
*/
private void expandWithDependencies(Set<IHawkMetaModelResource> metamodels, Map<String, IHawkPackage> expanded) throws Exception {
final Set<String> alreadyInGraph = new HashSet<>();
boolean firstBatch = false;
do {
for (Iterator<IHawkMetaModelResource> itM = metamodels.iterator(); itM.hasNext();) {
IHawkMetaModelResource m = itM.next();
for (IHawkObject child : m.getAllContents()) {
if (child instanceof IHawkPackage) {
final IHawkPackage pkg = (IHawkPackage) child;
if (pkg.getNsURI() == null) {
LOGGER.info("Package {} has null URI, ignoring", pkg.getName());
} else if (firstBatch && isMetamodelRegistered(pkg.getNsURI())) {
// First round: the graph may already have some of the provided metamodels
alreadyInGraph.add(pkg.getNsURI());
itM.remove();
LOGGER.info("Package {} already in the graph, ignoring", pkg.getNsURI());
} else {
expanded.put(pkg.getNsURI(), pkg);
}
}
}
}
firstBatch = false;
// Find out which metamodels this batch requires that we are missing so far
final Set<String> missingURIs = new HashSet<>();
addPendingDependencies(metamodels, expanded, missingURIs, alreadyInGraph);
// Try to locate the missing metamodels, or abort
metamodels.clear();
missingMetamodels:
for (String uri : missingURIs) {
for (IMetaModelResourceFactory mp : hawk.getMetaModelParsers()) {
IHawkMetaModelResource mr = mp.getMetamodel(uri);
if (mr != null) {
metamodels.add(mr);
continue missingMetamodels;
}
}
throw new MissingMetamodelException(uri);
}
} while (!metamodels.isEmpty());
}
private void addPendingDependencies(Set<IHawkMetaModelResource> metamodels, Map<String, IHawkPackage> expanded, final Set<String> missingURIs, final Set<String> alreadyInGraph) throws Exception {
for (IHawkMetaModelResource m : metamodels) {
for (IHawkObject child : m.getAllContents()) {
if (child instanceof IHawkPackage) {
final IHawkPackage pkg = (IHawkPackage) child;
for (IHawkClassifier pkgClassifier : pkg.getClasses()) {
if (pkgClassifier instanceof IHawkClass) {
IHawkClass eClass = (IHawkClass) pkgClassifier;
for (IHawkClass st : eClass.getAllSuperTypes()) {
addPendingDependencies(expanded, missingURIs, alreadyInGraph, st);
}
for (IHawkAttribute e : eClass.getAllAttributes()) {
if (e.getType() != null) {
addPendingDependencies(expanded, missingURIs, alreadyInGraph, e.getType());
}
}
for (IHawkReference r : eClass.getAllReferences()) {
addPendingDependencies(expanded, missingURIs, alreadyInGraph, r.getType());
}
}
}
}
}
}
}
private void addPendingDependencies(Map<String, IHawkPackage> toBeRegistered, final Set<String> missingURIs, final Set<String> alreadyInGraph, IHawkClassifier targetType) throws Exception {
final String nsURI = targetType.getPackageNSURI();
if (nsURI != null && !toBeRegistered.containsKey(nsURI) && !missingURIs.contains(nsURI) && !alreadyInGraph.contains(nsURI)) {
if (!isMetamodelRegistered(nsURI)) {
// Not in the set to be registered, not pending yet, and not in the graph: mark as missing
missingURIs.add(nsURI);
} else {
// Not in the set to be registered, but it is already in the graph
alreadyInGraph.add(nsURI);
}
}
}
private boolean isMetamodelRegistered(String nsURI) throws Exception {
boolean ret = false;
try (IGraphTransaction tx = graph.beginTransaction()) {
final IGraphIterable<? extends IGraphNode> iterMatches = graph.getMetamodelIndex().get("id", nsURI);
ret = iterMatches.iterator().hasNext();
tx.success();
}
return ret;
}
private void addEClasses(IHawkPackage ePackage) throws MissingMetamodelException {
for (IHawkClassifier child : ePackage.getClasses()) {
if (child instanceof IHawkClass) {
addMetaClass((IHawkClass) child);
} else if (child instanceof IHawkDataType) {
// FIXME need to handle datatypes?
if (child.getInstanceType() != null) {
LOGGER.warn(
"Hawk does not support custom data types yet: {}::{} will be handled as its instance type {}",
ePackage.getNsURI(), child.getName(), child.getInstanceType());
} else {
LOGGER.warn(
"Hawk does not support custom data types yet: {}::{} will be handled as a string",
ePackage.getNsURI(), child.getName());
}
} else {
LOGGER.error("Unknown classifier: ({}): {}", child.getName(), child.getClass());
}
}
}
/**
* Returns {@code true} iff we added a new package node to the graph.
*/
private boolean addEPackage(IHawkPackage ePackage, IGraphNodeIndex ePackageDictionary) throws IOException {
final String uri = ePackage.getNsURI();
if (uri == null) {
LOGGER.warn("ePackage {} has null nsURI, ignoring", ePackage);
return false;
}
if (ePackageDictionary.get("id", uri).iterator().hasNext() == false) {
Map<String, Object> fields = new HashMap<>();
fields.put(IModelIndexer.IDENTIFIER_PROPERTY, uri);
fields.put(IModelIndexer.METAMODEL_TYPE_PROPERTY, ePackage.getResource().getMetaModelResourceFactory().getType());
IGraphNode epackagenode = graph.createNode(fields, "epackage");
ePackageDictionary.add(epackagenode, "id", uri);
listener.metamodelAddition(ePackage, epackagenode);
return true;
} else {
LOGGER.warn(
"metamodel: {} ({}) already in store, updating it instead NYI -- doing nothing!",
(ePackage).getName(), uri);
return false;
}
}
/**
*
* @param eClass
* @return the URI ID of an eClass
*/
public String getEObjectId(IHawkClass eClass) {
return eClass.getPackageNSURI() + "/" + eClass.getName();
}
/**
* Creates a node in the graph database with the given eClass's attributes
* in it.
*
* @param eObject
* @param id
* @return the Node
*/
private void createEClassNode(IHawkClass eClass, String id) throws MissingMetamodelException {
final Map<String, Object> map = new HashMap<>();
map.put(IModelIndexer.IDENTIFIER_PROPERTY, id);
final IGraphNode node = graph.createNode(new HashMap<String, Object>(), "eclass");
final IGraphNode metamodelNode = graph.getMetamodelIndex().get("id", eClass.getPackageNSURI()).getSingle();
graph.createRelationship(node, metamodelNode, "epackage");
for (IHawkClass superType : eClass.getAllSuperTypes()) {
addMetamodelDependency(eClass, metamodelNode, superType);
}
for (IHawkAttribute e : eClass.getAllAttributes()) {
if (e.getType() != null) {
addMetamodelDependency(eClass, metamodelNode, e.getType());
}
String[] metadata = new Slot.MetadataBuilder()
.attribute()
.many(e.isMany())
.ordered(e.isOrdered())
.unique(e.isUnique())
.type(getPropertyType(e))
.build();
map.put(e.getName(), metadata);
}
for (IHawkReference r : eClass.getAllReferences()) {
addMetamodelDependency(eClass, metamodelNode, r.getType());
String[] metadata = new Slot.MetadataBuilder()
.reference()
.many(r.isMany())
.ordered(r.isOrdered())
.unique(r.isUnique())
.type(getPropertyType(r))
.build();
map.put(r.getName(), metadata);
}
for (String s : map.keySet()) {
node.setProperty(s, map.get(s));
}
listener.classAddition(eClass, node);
}
private String getPropertyType(IHawkStructuralFeature e) {
String propType = "unknown";
if (e.getType() != null) {
if (e.getType().getName() != null) {
// System.err.println(e.getType().getName());
propType = e.getType().getInstanceType();
} else {
LOGGER.warn(
"warning: unknown (null) type NAME found in metamodel parsing into db for attribute: {} of type: {}",
e.getName(), e.getType());
}
} else {
LOGGER.warn("warning: unknown (null) type found in metamodel parsing into db for attribute: {}", e.getName());
}
return propType;
}
private void addMetamodelDependency(IHawkClass sourceType, final IGraphNode metamodelNode, IHawkClassifier targetType) throws MissingMetamodelException {
final String targetTypeMetamodelURI = targetType.getPackageNSURI();
final IGraphIterable<? extends IGraphNode> metamodels = graph.getMetamodelIndex().get("id", targetTypeMetamodelURI);
if (metamodels.iterator().hasNext() == false) {
LOGGER.error(
"EClass {} has supertype {} which is in a package not registered yet, "
+ "reverting all changes to this package registration, please register "
+ "package with URI {} first",
sourceType.getName(), targetType.getName() == null ? targetType.getUri() : targetType.getName(), targetTypeMetamodelURI);
throw new MissingMetamodelException(targetTypeMetamodelURI);
} else if (!targetTypeMetamodelURI.equals(sourceType.getPackageNSURI())) {
final IGraphNode superTypeEPackage = metamodels.getSingle();
if (!metamodelDependencyExists(metamodelNode, superTypeEPackage)) {
LOGGER.debug("supertype dependency from {} to {}", sourceType.getPackageNSURI(), targetTypeMetamodelURI);
graph.createRelationship(metamodelNode, superTypeEPackage, IModelIndexer.METAMODEL_DEPENDENCY_EDGE);
}
}
}
private boolean metamodelDependencyExists(final IGraphNode metamodelNode, final IGraphNode superTypeEPackage) {
boolean alreadythere = false;
Iterable<IGraphEdge> depEdges = metamodelNode.getOutgoingWithType(IModelIndexer.METAMODEL_DEPENDENCY_EDGE);
for (IGraphEdge r : depEdges) {
if (r.getEndNode().equals(superTypeEPackage)) {
alreadythere = true;
break;
}
}
return alreadythere;
}
/**
* Turns an {@link IHawkClass} into a graph node.
*
* @param eClass Class to be added.
* @throws MissingMetamodelException A required metamodel is missing from the graph.
*/
private void addMetaClass(IHawkClass eClass) throws MissingMetamodelException {
String id = eClass.getName();
objectCount++;
createEClassNode(eClass, id);
}
/**
* Adds a new derived attribute to a type. Returns whether propagation to
* current instances of this type is necessary.
*
* @param metamodeluri
* @param typename
* @param attributename
* @param isMany
* @param isOrdered
* @param isUnique
* @param attributetype
* @param derivationlanguage
* @param derivationlogic
* @param graph
* @return
*/
public static boolean addDerivedAttribute(String metamodeluri, String typename, String attributename,
boolean isMany, boolean isOrdered, boolean isUnique, String attributetype, String derivationlanguage,
String derivationlogic, IGraphDatabase graph, IGraphChangeListener listener) {
boolean requiresPropagationToInstances = false;
try (IGraphTransaction t = graph.beginTransaction()) {
listener.changeStart();
IGraphIterable<? extends IGraphNode> ep = graph.getMetamodelIndex().get("id", metamodeluri);
IGraphNode packagenode = null;
if (ep.size() == 1) {
packagenode = ep.getSingle();
} else {
throw new Exception("metamodel not found:" + metamodeluri);
}
IGraphNode typenode = null;
for (IGraphEdge e : packagenode.getIncomingWithType("epackage")) {
if (e.getStartNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY).equals(typename)) {
typenode = e.getStartNode();
break;
}
}
if (typenode == null) {
LOGGER.error("type: {} in: {} does not exist, aborting operation: addDerivedAttribute", typename, metamodeluri);
} else {
/*
* at least one instance already present so derived attribute
* needs to be reconfigured for each element already present
* (whether the derived attribute is new or existed already and
* is being updated)
*/
if (typenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFTYPE).iterator().hasNext()
|| typenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFKIND).iterator().hasNext())
requiresPropagationToInstances = true;
String[] metadata = new String[7];
metadata[0] = "d";
metadata[1] = (isMany ? "t" : "f");
metadata[2] = (isOrdered ? "t" : "f");
metadata[3] = (isUnique ? "t" : "f");
metadata[4] = attributetype;
metadata[5] = derivationlanguage;
metadata[6] = derivationlogic;
if (typenode.getProperty(attributename) != null) {
LOGGER.warn("Attribute already derived, nothing happened!");
requiresPropagationToInstances = false;
} else {
typenode.setProperty(attributename, metadata);
if (LOGGER.isInfoEnabled()) {
final String logic = (derivationlogic.length() > 100
? derivationlogic.substring(0, 100) + "\n[! long script, snipped !]"
: derivationlogic);
LOGGER.info(
"Derived attribute added: {}::{}#{} (isMany={}|isOrdered={}|isUnique={}|type={}) {}#\n{}",
metamodeluri, typename, attributename,
isMany, isOrdered, isUnique, attributetype, derivationlanguage,
logic);
}
}
/** Must create the empty index so it will be known by the {@link ModelIndexerImpl} */
graph.getOrCreateNodeIndex(metamodeluri + "##" + typename + "##" + attributename);
}
t.success();
listener.changeSuccess();
} catch (Exception e1) {
LOGGER.error("error in adding a derived attribute to the metamodel", e1);
listener.changeFailure();
}
return requiresPropagationToInstances;
}
/**
* Adds a new indexed attribute to a type. Returns whether propagation to
* current instances of this type is necessary.
*
* @param metamodeluri
* @param typename
* @param attributename
* @param graph
* @param listener2
* @return
*/
public static boolean addIndexedAttribute(String metamodeluri, String typename, String attributename,
IGraphDatabase graph, IGraphChangeListener listener) {
boolean requiresPropagationToInstances = false;
try (IGraphTransaction t = graph.beginTransaction()) {
listener.changeStart();
IGraphNode packagenode = graph.getMetamodelIndex().get("id", metamodeluri).getSingle();
IGraphNode typenode = null;
for (IGraphEdge e : packagenode.getIncomingWithType("epackage")) {
if (e.getStartNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY).equals(typename)) {
typenode = e.getStartNode();
break;
}
}
if (typenode == null) {
LOGGER.error("type: {} in: {} does not exist, aborting operation: addIndexedAttribute", typename, metamodeluri);
} else {
// at least one instance already present so indexed attribute
// needs
// to be reconfigured for each element already present (whether
// the
// indexed attribute is new or existed already and is being
// updated)
String[] metadata = (String[]) typenode.getProperty(attributename);
if (metadata == null) {
LOGGER.error("attribute: {} in: {}#{} does not exist, aborting operation: addIndexedAttribute", attributename, metamodeluri, typename);
} else if (!metadata[0].equals("a")) {
LOGGER.error("{}#{} is not an attribute, aborting operation: addIndexedAttribute", metamodeluri, typename);
} else {
if (typenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFTYPE).iterator().hasNext()
|| typenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFKIND).iterator().hasNext())
requiresPropagationToInstances = true;
if (metadata.length == 6) {
if ("t".equals(metadata[5])) {
LOGGER.warn("attribute already indexed, nothing happened!");
requiresPropagationToInstances = false;
} else {
metadata[5] = "t";
typenode.setProperty(attributename, metadata);
//
graph.getOrCreateNodeIndex(metamodeluri + "##" + typename + "##" + attributename);
//
LOGGER.info("indexed attribute added: {}::{}#{}", metamodeluri, typename, attributename);
}
} else if (metadata.length == 7) {
LOGGER.warn("derived attributes are already indexed, nothing happened.");
} else {
// FIXME log actual exception
LOGGER.error("unknown exception in addIndexedAttribute of GraphMetamodelResourceInjector");
}
}
}
t.success();
listener.changeSuccess();
} catch (Exception e) {
LOGGER.error("Error in adding an indexed attribute", e);
listener.changeFailure();
}
return requiresPropagationToInstances;
}
/**
* Removes all the metamodels with the specified URIs.
*
* @return URIs of the repositories impacted by the removal.
*/
public Set<String> removeMetamodels(String[] mmuris) {
Set<String> ret = new HashSet<>();
try (IGraphTransaction t = graph.beginTransaction()) {
Set<String> indexNames = graph.getNodeIndexNames();
Set<IGraphNodeIndex> markedForRemoval = new HashSet<>();
listener.changeStart();
IGraphNodeIndex ePackageDictionary = graph.getMetamodelIndex();
Set<IGraphNode> epns = new HashSet<>();
for (String mmuri : mmuris) {
try {
for (Iterator<String> it = indexNames.iterator(); it.hasNext();) {
String s = it.next();
if (s.startsWith(mmuri + "##")) {
IGraphNodeIndex i = graph.getOrCreateNodeIndex(s);
markedForRemoval.add(i);
it.remove();
}
}
epns.add(ePackageDictionary.get("id", mmuri).getSingle());
} catch (Exception e) {
LOGGER.error("Metamodel with URI " + mmuri + " not indexed. Nothing happened.", e);
}
}
if (epns.size() > 0) {
LOGGER.info("Removing metamodels with URIs {}", Arrays.toString(mmuris));
ret = removeAll(epns);
}
for (IGraphNodeIndex i : markedForRemoval) {
LOGGER.info("Deleting index {} as its metamodel was removed.", i.getName());
i.delete();
}
t.success();
listener.changeSuccess();
} catch (Exception e) {
listener.changeFailure();
LOGGER.error("Error in removing metamodels " + Arrays.toString(mmuris) + "\n(ALL removal changes reverted)", e);
}
return ret;
}
public static boolean removeIndexedAttribute(String metamodelUri, String typename, String attributename,
IGraphDatabase graph, CompositeGraphChangeListener listener) {
boolean found = false;
try (IGraphTransaction t = graph.beginTransaction()) {
listener.changeStart();
IGraphNode packagenode = null;
IGraphIterable<? extends IGraphNode> ep = graph.getMetamodelIndex().get("id", metamodelUri);
if (ep.size() == 1) {
packagenode = ep.getSingle();
} else {
throw new Exception("metamodel not found:" + metamodelUri);
}
IGraphNode typenode = null;
for (IGraphEdge e : packagenode.getIncomingWithType("epackage")) {
if (e.getStartNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY).equals(typename)) {
typenode = e.getStartNode();
break;
}
}
if (typenode == null) {
LOGGER.error(
"type: {} in: {} does not exist, aborting operation: removeIndexedAttribute",
typename, metamodelUri
);
listener.changeFailure();
} else {
String[] metadata = (String[]) typenode.getProperty(attributename);
if (metadata == null) {
LOGGER.error("attribute: {} in: {}::{} does not exist, aborting operation: removeIndexedAttribute",
attributename, metamodelUri, typename);
listener.changeFailure();
} else if (!metadata[0].equals("a")) {
// System.err.println(Arrays.toString(metadata));
LOGGER.error("{}::{} is a reference not an attribute, aborting operation: removeIndexedAttribute",
metamodelUri, typename);
listener.changeFailure();
} else {
if (metadata.length == 6) {
if ("t".equals(metadata[5])) {
metadata[5] = "f";
typenode.setProperty(attributename, metadata);
//
String indexname = metamodelUri + "##" + typename + "##" + attributename;
if (graph.nodeIndexExists(indexname)) {
graph.getOrCreateNodeIndex(indexname).delete();
found = true;
}
t.success();
listener.changeSuccess();
} else {
LOGGER.error("attribute was not indexed, nothing happened!");
listener.changeFailure();
}
} else {
LOGGER.error("error in removeIndexedAttribute (metadata.length!=6), nothing happened!");
listener.changeFailure();
}
}
}
} catch (Exception e) {
LOGGER.error("Error while removing an indexed attribute", e);
listener.changeFailure();
}
return found;
}
public static boolean removeDerivedAttribute(String metamodelUri, String typeName, String attributeName,
IGraphDatabase graph, CompositeGraphChangeListener listener) {
boolean found = false;
try (IGraphTransaction t = graph.beginTransaction()) {
listener.changeStart();
IGraphIterable<? extends IGraphNode> ep = graph.getMetamodelIndex().get("id", metamodelUri);
IGraphNode packagenode = null;
if (ep.size() == 1) {
packagenode = ep.getSingle();
} else {
throw new Exception("metamodel not found:" + metamodelUri);
}
IGraphNode typenode = null;
for (IGraphEdge e : packagenode.getIncomingWithType("epackage")) {
if (e.getStartNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY).equals(typeName)) {
typenode = e.getStartNode();
break;
}
}
if (typenode == null) {
LOGGER.error(
"type: {} in: {} does not exist, aborting operation: removeDerivedAttribute",
typeName, metamodelUri);
listener.changeFailure();
} else {
String[] metadata = (String[]) typenode.getProperty(attributeName);
if (metadata != null) {
if (metadata.length == 7 && metadata[0].equals("d")) {
LOGGER.info("derived attribute removed: {}::{}", metamodelUri, typeName);
IGraphNodeIndex derivedAccessDictionary = graph.getOrCreateNodeIndex(GraphModelInserter.DERIVED_ACCESS_IDXNAME);
IGraphNodeIndex derivedProxyDictionary = graph.getOrCreateNodeIndex("derivedproxydictionary");
typenode.removeProperty(attributeName);
graph.getOrCreateNodeIndex(metamodelUri + "##" + typeName + "##" + attributeName).delete();
boolean noerror = true;
for (IGraphEdge e : (typenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFTYPE)))
noerror = noerror && removeDerivedAttribute(derivedAccessDictionary, derivedProxyDictionary,
attributeName, e);
for (IGraphEdge e : (typenode.getIncomingWithType(ModelElementNode.EDGE_LABEL_OFKIND)))
noerror = noerror && removeDerivedAttribute(derivedAccessDictionary, derivedProxyDictionary,
attributeName, e);
if (noerror)
found = true;
t.success();
listener.changeSuccess();
} else {
LOGGER.error("Error in removeDerivedAttribute, attribute metadata not valid");
listener.changeFailure();
}
} else {
LOGGER.error("Attribute was not already derived, nothing happened!");
listener.changeFailure();
}
}
} catch (Exception e1) {
LOGGER.error("Error in removing a derived attribute to the metamodel", e1);
listener.changeFailure();
}
return found;
}
/**
* Internal, to remove instance derived attributes
*/
private static boolean removeDerivedAttribute(IGraphNodeIndex derivedAccessDictionary,
IGraphNodeIndex derivedProxyDictionary, String attributeName, IGraphEdge instanceToTypeEdge) {
boolean error = true;
IGraphNode n = instanceToTypeEdge.getStartNode();
IGraphEdge dae = null;
for (IGraphEdge ed : n.getOutgoingWithType(attributeName)) {
if (dae == null)
dae = ed;
else {
LOGGER.error("multiple edges found for derived attribute: {} in node {}", attributeName, n);
dae = null;
break;
}
}
if (dae == null) {
LOGGER.error("derived attribute ({}) not found for node {}", attributeName, n);
} else {
IGraphNode dan = dae.getEndNode();
dae.delete();
derivedAccessDictionary.remove(dan);
derivedProxyDictionary.remove(dan);
dan.delete();
error = false;
}
return !error;
}
}