/*
* Copyright (c) 2006, 2010 Borland Software Corporation and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alexander Shatalin (Borland) - initial API and implementation
*/
package org.eclipse.gmf.internal.codegen.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.gmf.codegen.gmfgen.GenCommonBase;
import org.eclipse.gmf.codegen.gmfgen.GenContainerBase;
import org.eclipse.gmf.codegen.gmfgen.GenDiagram;
import org.eclipse.gmf.codegen.gmfgen.GenLink;
import org.eclipse.gmf.codegen.gmfgen.GenNavigator;
import org.eclipse.gmf.codegen.gmfgen.GenNavigatorChildReference;
import org.eclipse.gmf.codegen.gmfgen.GenNavigatorReferenceType;
import org.eclipse.gmf.codegen.gmfgen.GenNode;
/**
* GenNavigatorChildReference may relate parent and child not directly connected to each other (but not completely unrelated, though),
* consider child nodes placed into compartments (which we don't want to visualize in the navigator tree).
* To accomplish that, we build connection paths for those non-directly related elements, and use these paths at code generation time.
* This class is basically an utility to find connection path for any parent-child pair of navigator elements demanded by user with
* GenNavigatorChildReference in its genmodel.
*/
public class GenModelGraphAnalyzer {
/**
* @deprecated
*/
public GenModelGraphAnalyzer(GenDiagram diagram) {
}
public static List<List<GenCommonBase>> getConnectionPaths(GenNavigatorChildReference reference) {
assert reference.getParent() != null;
if (reference.getReferenceType() == GenNavigatorReferenceType.CHILDREN_LITERAL) {
return getChildConnectionPaths(reference.getParent(), reference.getChild(), reference.getNavigator());
} else if (reference.getReferenceType() == GenNavigatorReferenceType.IN_SOURCE_LITERAL) {
return getInSourceLinkConnectionPaths(reference.getParent(), reference.getChild(), reference.getNavigator());
} else if (reference.getReferenceType() == GenNavigatorReferenceType.OUT_TARGET_LITERAL) {
return getOutTargetLinkConnectionPaths(reference.getParent(), reference.getChild(), reference.getNavigator());
}
return Collections.emptyList();
}
private static List<List<GenCommonBase>> getOutTargetLinkConnectionPaths(GenCommonBase source, GenCommonBase target, GenNavigator genNavigator) {
return new LinkedConnectionFinder(genNavigator, true).findConnectionPaths(source, target);
}
private static List<List<GenCommonBase>> getInSourceLinkConnectionPaths(GenCommonBase source, GenCommonBase target, GenNavigator genNavigator) {
return new LinkedConnectionFinder(genNavigator, false).findConnectionPaths(source, target);
}
private static List<List<GenCommonBase>> getChildConnectionPaths(GenCommonBase source, GenCommonBase target, GenNavigator genNavigator) {
return new ChildConnectionFinder(genNavigator).findConnectionPaths(source, target);
}
private static abstract class AbstractConnectionFinder {
private Set<GenCommonBase> myVisiting;
private GenNavigator myNavigator;
public AbstractConnectionFinder(GenNavigator genNavigator) {
myVisiting = new LinkedHashSet<GenCommonBase>();
myNavigator = genNavigator;
}
protected abstract Collection<GenCommonBase> getConnectedNodes(GenCommonBase source);
public List<List<GenCommonBase>> findConnectionPaths(GenCommonBase source, GenCommonBase target) {
if (isConnectionFound(source, target)) {
// Direct connection found
List<GenCommonBase> path = new ArrayList<GenCommonBase>();
path.add(target);
List<List<GenCommonBase>> connections = new ArrayList<List<GenCommonBase>>();
connections.add(path);
return connections;
}
if (isVisiting(source) || stopIterating(source)) {
// Loop found
return new ArrayList<List<GenCommonBase>>();
}
startVisiting(source);
try {
// Looking for indirect connection + extending connection with
// current node
Collection<GenCommonBase> connectedNodes = getConnectedNodes(source);
List<List<GenCommonBase>> connections = new ArrayList<List<GenCommonBase>>();
for (GenCommonBase nextConnectedNode : connectedNodes) {
connections.addAll(findConnectionPaths(nextConnectedNode, target));
}
for (List<GenCommonBase> path : connections) {
path.add(0, source);
}
return connections;
} finally {
stopVisiting(source);
}
}
private boolean stopIterating(GenCommonBase source) {
if (myVisiting.size() > 0) {
for (GenNavigatorChildReference nextReference : myNavigator.getChildReferences()) {
if (nextReference.getParent() == source) {
return true;
}
}
}
return false;
}
private boolean isVisiting(GenCommonBase node) {
return myVisiting.contains(node);
}
private void startVisiting(GenCommonBase node) {
myVisiting.add(node);
}
private void stopVisiting(GenCommonBase node) {
myVisiting.remove(node);
}
protected Iterable<GenCommonBase> getPath() {
return myVisiting;
}
/**
* myVisiting.size() > 0 checked return non-empty paths if source and
* target nodes are the same
*/
private boolean isConnectionFound(GenCommonBase source, GenCommonBase target) {
return myVisiting.size() > 0 && source == target;
}
}
private static class ChildConnectionFinder extends AbstractConnectionFinder {
public ChildConnectionFinder(GenNavigator genNavigator) {
super(genNavigator);
}
protected Collection<GenCommonBase> getConnectedNodes(GenCommonBase source) {
Collection<GenCommonBase> children = new ArrayList<GenCommonBase>();
if (source instanceof GenContainerBase) {
children.addAll(((GenContainerBase) source).getContainedNodes());
}
if (source instanceof GenDiagram) {
children.addAll(((GenDiagram) source).getLinks());
}
if (source instanceof GenNode) {
children.addAll(((GenNode) source).getCompartments());
children.addAll(((GenNode) source).getLabels());
}
if (source instanceof GenLink) {
children.addAll(((GenLink) source).getLabels());
}
return children;
}
}
private static class LinkedConnectionFinder extends AbstractConnectionFinder {
private GenDiagram myDiagram;
/**
* true: looking for the connection from source to target using all
* links in natural direction (outgoing links -> link target -> outgoing
* links...)
*
* false: opposite direction (incomming links -> link source -> ..)
*/
private boolean myIsInLinkDirection;
public LinkedConnectionFinder(GenNavigator genNavigator, boolean inLinkDirection) {
super(genNavigator);
myDiagram = genNavigator.getEditorGen().getDiagram();
myIsInLinkDirection = inLinkDirection;
}
protected Collection<GenCommonBase> getConnectedNodes(GenCommonBase source) {
if (source instanceof GenNode) {
return getPotentiallyConnectedLinks((GenNode) source);
} else if (source instanceof GenLink) {
return getPotentiallyConnectedNodes((GenLink) source);
}
return Collections.emptyList();
}
private Collection<GenCommonBase> getPotentiallyConnectedLinks(GenNode node) {
for (GenCommonBase nextPathElement : getPath()) {
if (nextPathElement instanceof GenLink) {
// Only one link allowed in the path
return Collections.emptyList();
}
}
// TODO: this method could be moved to GenNode
Collection<GenCommonBase> potentialLinks = new ArrayList<GenCommonBase>();
for (GenLink nextLink : myDiagram.getLinks()) {
if (nextLink.getModelFacet() == null) {
potentialLinks.add(nextLink);
} else if (node.getModelFacet() != null) {
GenClass genClass = myIsInLinkDirection ? nextLink.getModelFacet().getSourceType() : nextLink.getModelFacet().getTargetType();
if (genClass != null && Extras.isSuperTypeOf(genClass.getEcoreClass(), node.getDomainMetaClass().getEcoreClass())) {
potentialLinks.add(nextLink);
}
}
}
return potentialLinks;
}
// TODO: this method could be moved to GenLink
private Collection<GenCommonBase> getPotentiallyConnectedNodes(GenLink link) {
Collection<GenCommonBase> potentialNodes = new ArrayList<GenCommonBase>();
if (link.getModelFacet() == null) {
potentialNodes.addAll(myDiagram.getAllNodes());
} else {
GenClass genClass = myIsInLinkDirection ? link.getModelFacet().getTargetType() : link.getModelFacet().getSourceType();
if (genClass != null) {
for (GenNode nextNode : myDiagram.getAllNodes()) {
if (nextNode.getModelFacet() == null) {
// skipping pure design nodes - cannot be incorrect
// connection source/target
continue;
}
if (Extras.isSuperTypeOf(genClass.getEcoreClass(), nextNode.getDomainMetaClass().getEcoreClass())) {
potentialNodes.add(nextNode);
}
}
}
}
return potentialNodes;
}
}
}