blob: 10dc717af386745863592bf0df37879c9ed0824f [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
* Jonathan Co - update for Epsilon 2.0 compatibility
******************************************************************************/
package org.eclipse.hawk.epsilon.emc.pgetters;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.epsilon.eol.exceptions.EolIllegalPropertyException;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.types.EolSequence;
import org.eclipse.hawk.core.IModelIndexer;
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.util.Utils;
import org.eclipse.hawk.epsilon.emc.EOLQueryEngine.GraphNodeWrapper;
import org.eclipse.hawk.epsilon.emc.contextful.CEOLQueryEngine;
import org.eclipse.hawk.graph.FileNode;
import org.eclipse.hawk.graph.ModelElementNode;
import org.eclipse.hawk.graph.updater.DirtyDerivedFeaturesListener;
public class CGraphPropertyGetter extends GraphPropertyGetter {
private final CEOLQueryEngine engine;
public CGraphPropertyGetter(IGraphDatabase graph, CEOLQueryEngine m) {
super(graph, m);
engine = m;
}
@SuppressWarnings("unchecked")
@Override
public Object invoke(Object object, final String property) throws EolRuntimeException {
Object ret = null;
if (!(object instanceof GraphNodeWrapper))
throw new EolRuntimeException("a non GraphNodeWrapper object passed to GraphPropertyGetter!");
final IGraphNode node = ((GraphNodeWrapper) object).getNode();
// avoid using switch (in the outer-most structure) to allow partial
// matches and method calls in alternatives
if (property.equals("hawkFile")) {
String sep = "";
StringBuilder buff = new StringBuilder(32);
for (IGraphEdge e : node.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE)) {
buff.append(sep);
buff.append(e.getEndNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY).toString());
sep = ";";
}
ret = buff.toString();
} else if (property.equals("hawkRepo")) {
String sep = "";
StringBuilder buff = new StringBuilder(32);
for (IGraphEdge e : node.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE)) {
buff.append(sep);
buff.append(e.getEndNode().getProperty(FileNode.PROP_REPOSITORY).toString());
sep = ";";
}
ret = buff.toString();
} else if (property.equals("hawkFiles")) {
Set<String> files = new HashSet<>();
for (IGraphEdge e : node.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE))
files.add(e.getEndNode().getProperty(IModelIndexer.IDENTIFIER_PROPERTY).toString());
ret = files;
} else if (property.equals("hawkRepos")) {
Set<String> repos = new HashSet<>();
for (IGraphEdge e : node.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE))
repos.add(e.getEndNode().getProperty(FileNode.PROP_REPOSITORY).toString());
ret = repos;
} else if (property.startsWith(REVERSE_REFNAV_PREFIX)) {
List<GraphNodeWrapper> nodes = new EolSequence<GraphNodeWrapper>();
final String referenceName = property.substring(REVERSE_REFNAV_PREFIX.length());
for (IGraphEdge r : node.getIncomingWithType(referenceName)) {
GraphNodeWrapper n = wrapIfInScope(r.getStartNode());
if (n != null)
nodes.add(n);
}
for (IGraphEdge r : node.getIncomingWithType(ModelElementNode.DERIVED_EDGE_PREFIX + referenceName)) {
IGraphNode derivedNode = r.getStartNode();
IGraphNode elementNode = derivedNode.getIncoming().iterator().next().getStartNode();
GraphNodeWrapper n = wrapIfInScope(elementNode);
if (n != null) {
nodes.add(n);
}
}
ret = nodes;
} else if (property.equals("eContainer")) {
Iterable<IGraphEdge> inc = node.getIncoming();
for (IGraphEdge r : inc) {
if (r.getProperty(ModelElementNode.EDGE_PROPERTY_CONTAINMENT) != null) {
ret = wrapIfInScope(r.getStartNode());
break;
}
}
if (ret == null)
throw new EolRuntimeException("eContainer failed,\n" + object + "\nis not contained in current scope");
}
else if (property.equals("eContents")) {
ret = getContents(node);
}
else if (property.equals("eAllContents")) {
ret = addAllContents(node, new ArrayList<>());
}
else if (property.equals("eContainers")) {
for (IGraphEdge r : node.getIncoming()) {
if (r.getProperty(ModelElementNode.EDGE_PROPERTY_CONTAINMENT) != null) {
ret = wrapIfInScope(r.getStartNode());
}
}
if (ret == null) {
for (IGraphEdge r : node.getOutgoing()) {
if (r.getProperty(ModelElementNode.EDGE_PROPERTY_CONTAINER) != null) {
ret = wrapIfInScope(r.getEndNode());
break;
}
}
}
if (ret == null) {
ret = Collections.emptyList();
} else {
ret = Collections.singletonList(ret);
}
}
else if (property.equals("hawkIn") || property.equals("hawkOut")) {
final boolean isIncoming = property.equals("hawkIn");
final List<GraphNodeWrapper> results = new ArrayList<>();
final Iterable<IGraphEdge> edges = isIncoming ? node.getIncoming() : node.getOutgoing();
for (IGraphEdge r : edges) {
if (ModelElementNode.TRANSIENT_EDGE_LABELS.contains(r.getType())) {
continue;
}
final IGraphNode edgeNode = isIncoming ? r.getStartNode() : r.getEndNode();
final GraphNodeWrapper edgeNodeWrapper = wrapIfInScope(edgeNode);
results.add(edgeNodeWrapper);
}
ret = results;
}
else if (property.equals("hawkProxies")) {
// TODO perform context filtering here as well (we'd need to do our own pattern matching)
return new ModelElementNode(node).getProxies();
}
else if (canHaveDerivedAttr(node, property)) {
for (IGraphEdge r : node.getOutgoingWithType(property)) {
if (ret == null) {
final IGraphNode derivedNode = r.getEndNode();
ret = derivedNode.getProperty(property);
if (ret == null) {
List<GraphNodeWrapper> derivedTargets = new EolSequence<>();
for (IGraphEdge edge : derivedNode.getOutgoingWithType(ModelElementNode.DERIVED_EDGE_PREFIX + property)) {
derivedTargets.add(model.wrap(edge.getEndNode()));
ret = derivedTargets;
}
}
ret = retainScoped(ret);
} else {
throw new EolRuntimeException("WARNING: a derived property (arity 1) -- ( " + property
+ " ) has more than 1 links in store!");
}
}
if (ret == null) {
throw new EolRuntimeException("derived attribute lookup failed for: " + object + " # " + property);
} else if (ret instanceof String && ((String) ret).startsWith(DirtyDerivedFeaturesListener.NOT_YET_DERIVED_PREFIX)) {
// XXX IDEA: dynamically derive on the spot on access
System.err.println("attribute: " + property + " is NYD for node: " + node.getId());
}
}
else if (canHaveMixed(node, property)) {
ret = getCollectionForProperty(property);
final Collection<Object> retCollection = (Collection<Object>) ret;
if (node.getProperty(property) != null) {
final List<?> values = new Utils().asList(node.getProperty(property));
retCollection.addAll(values);
}
for (IGraphEdge r : node.getOutgoingWithType(property)) {
GraphNodeWrapper o = wrapIfInScope(r.getEndNode());
if (o != null)
retCollection.add(o);
}
}
else if (canHaveAttr(node, property)) {
if (node.getProperty(property) != null) {
// FIXMEdone handle collections / ordered etc
if (!(isMany(property)))
ret = node.getProperty(property);
else {
ret = getCollectionForProperty(property);
Object[] array = ((Object[]) node.getProperty(property));
for (int i = 0; i < array.length; i++)
((Collection<Object>) ret).add(array[i]);
}
} else
// return new NeoIdWrapper(0L, m);
{
// ret = "UNSET";
ret = null;
// throw new EolRuntimeException("unset property");
}
}
else if (canHaveRef(node, property)) {
GraphNodeWrapper otherNode = null;
Collection<Object> otherNodes = null;
// FIXMEdone arity etc in metamodel
// otherNodes = new EolBag<NeoIdWrapperDebuggable>();
if (isMany(property)) {
otherNodes = getCollectionForProperty(property);
}
for (IGraphEdge r : node.getOutgoingWithType(property)) {
IGraphNode n = r.getEndNode();
GraphNodeWrapper o = wrapIfInScope(n);
if (otherNodes != null) {
if (o != null)
otherNodes.add(o);
} else if (otherNode == null)
otherNode = o;
else
throw new EolRuntimeException(
"A relationship with arity 1 ( " + property + " ) has more than 1 links");
}
ret = otherNodes != null ? otherNodes : otherNode;
} else {
throw new EolIllegalPropertyException(object, property, null);
}
if (broadcastAccess)
broadcastAccess(object, property);
return ret;
}
private List<GraphNodeWrapper> addAllContents(IGraphNode node, List<GraphNodeWrapper> results) {
for (GraphNodeWrapper child : getContents(node)) {
results.add(child);
addAllContents(child.getNode(), results);
}
return results;
}
private List<GraphNodeWrapper> getContents(final IGraphNode node) {
final List<GraphNodeWrapper> results = new ArrayList<>();
Iterable<IGraphEdge> out = node.getOutgoing();
for (IGraphEdge r : out) {
if (r.getProperty(ModelElementNode.EDGE_PROPERTY_CONTAINMENT) != null) {
final GraphNodeWrapper endNode = wrapIfInScope(r.getEndNode());
if (endNode != null) {
results.add(endNode);
}
}
}
return results;
}
private Object retainScoped(Object ret) {
Collection<?> cRet = null;
if (ret instanceof Collection<?>)
cRet = (Collection<?>) ret;
if (cRet == null) {
if (ret instanceof GraphNodeWrapper)
ret = retainScoped((GraphNodeWrapper) ret);
} else {
Iterator<?> it = cRet.iterator();
while (it.hasNext()) {
Object r = it.next();
if (r instanceof GraphNodeWrapper)
if (retainScoped((GraphNodeWrapper) r) == null)
it.remove();
}
}
return cRet == null ? ret : cRet;
}
private Object retainScoped(GraphNodeWrapper ret) {
if (!engine.isTraversalScopingEnabled())
return ret;
// capture multiple file containment (ie for singleton nodes)
for (IGraphEdge e : graph.getNodeById(ret.getId()).getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE)) {
if (engine.getRawFileNodes().contains(e.getEndNode())) {
return ret;
}
}
return null;
}
private GraphNodeWrapper wrapIfInScope(IGraphNode node) {
if (!engine.isTraversalScopingEnabled())
return model.wrap(node);
// capture multiple file containment (ie for singleton nodes)
for (IGraphEdge e : node.getOutgoingWithType(ModelElementNode.EDGE_LABEL_FILE)) {
if (engine.getRawFileNodes().contains(e.getEndNode())) {
return model.wrap(node);
}
}
return null;
}
}