/*
* Copyright (c) 2005- michael lawley and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation
* which accompanies this distribution, and is available by writing to
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Contributors:
* michael lawley
*
*/
package com.dstc.emf.view;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.LayoutManager;
import org.eclipse.draw2d.LineBorder;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.swt.graphics.Color;
import org.eclipse.ui.PlatformUI;
/**
* @author lawley
*
*/
public class EMFFigure extends Figure {
private final static boolean DEBUG = false;
public final static FigureConfigurationAction DEFAULT_NODE_CONFIG = new FigureConfigurationAction() {
final private Dimension D = new Dimension(8, 8);
private Color classColor = new Color(null,255,206,255);
public void configure(IFigure figure) {
figure.setMinimumSize(D);
figure.setSize(D);
figure.setBorder(new LineBorder(ColorConstants.black,1));
figure.setBackgroundColor(classColor);
figure.setOpaque(true);
}
};
private boolean includeRefs = false;
private int maxdepth = Integer.MAX_VALUE;
public EMFFigure() {
super();
}
public void removeNotify() {
// We're going away -> remove our listener
for (Iterator itr = resNodeConfig.keySet().iterator(); itr.hasNext(); ) {
Resource res = (Resource) itr.next();
res.eAdapters().remove(resourceListener);
}
super.removeNotify();
}
private Map nodeFigMap = new HashMap();
private Map edgeFigMap = new HashMap();
private Map resNodeConfig = new HashMap();
private Adapter resourceListener = new Adapter() {
public void notifyChanged(Notification notification) {
int eventType = notification.getEventType();
if (Notification.ADD == eventType) {
Object newValue = notification.getNewValue();
if (newValue instanceof EObject) {
List l = new ArrayList(1);
l.add(newValue);
addAll(l);
}
} else if (Notification.REMOVE == eventType) {
Object oldValue = notification.getOldValue();
if (oldValue instanceof EObject) {
// REMOVE may be because object is now owned by another
// rather than directly by the resource
List l = new ArrayList(1);
l.add(oldValue);
updateAll(l);
}
} else if (Notification.SET == eventType) {
refresh();
}
}
public void setTarget(Notifier newTarget) {
}
public boolean isAdapterForType(Object type) {
return false;
}
public Notifier getTarget() {
return null;
}
};
private int resourceCount = 0;
private final List configActions = new ArrayList();
private Color theColor = new Color(null, 255, 127, 255);
private LayoutManager radialLayout = null;
public void addResource(Resource res) {
resourceCount++;
resNodeConfig.put(res, getConfigAction());
addAll(res.getContents());
res.eAdapters().add(resourceListener);
// res.setTrackingModification(true);
// System.out.println(res + ": " + res.isTrackingModification());
}
public void removeResource(final Resource res) {
resourceCount--;
resNodeConfig.remove(res);
res.eAdapters().remove(resourceListener);
removeAll(res.getContents());
}
private FigureConfigurationAction getConfigAction() {
FigureConfigurationAction action;
if (resourceCount > configActions.size()) {
final Color resColor = theColor;
theColor = new Color(null, 255 & (theColor.getRed() - 32), 255 & (theColor.getGreen() + 32), 255 - theColor.getRed());
action = new FigureConfigurationAction() {
public void configure(IFigure figure) {
DEFAULT_NODE_CONFIG.configure(figure);
figure.setBackgroundColor(resColor);
}
};
configActions.add(action);
} else {
action = (FigureConfigurationAction) configActions.get(resourceCount - 1);
}
return action;
}
public void clear() {
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
public void run() {
removeAll();
nodeFigMap.clear();
edgeFigMap.clear();
resourceCount = 0;
theColor = new Color(null, 255, 127, 255);
}
});
}
public void setLayoutManager(LayoutManager layoutManager) {
super.setLayoutManager(layoutManager);
if (layoutManager instanceof RadialTreeLayout) {
radialLayout = layoutManager;
} else {
radialLayout = null;
}
}
protected void addAll(final List nodes) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
try {
// System.out.println("Add");
// System.out.println("Before Nodes + Edges: " + getChildren().size());
addAllNodes(null, nodes);
addReferencedObjects();
addReferenceEdges();
depthBound();
// TODO find out how to force a repaint
// viewer.repaint();
invalidate();
repaint();
// System.out.println("After Nodes + Edges: " + getChildren().size());
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
protected void removeAll(final List nodes) {
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
public void run() {
// System.out.println("Remove");
// System.out.println("Before Nodes + Edges = " + getChildren().size());
removeAllNodes(nodes);
depthBound();
invalidate();
repaint();
// System.out.println("After Nodes + Edges = " + getChildren().size());
}
});
}
protected void removeAllNodes(final List nodes) {
for (Iterator itr = nodes.iterator(); itr.hasNext(); ) {
EObject obj = (EObject) itr.next();
removeReferenceEdges(obj, obj.eContents(), true);
removeReferenceEdges(obj, obj.eCrossReferences(), false);
removeAllNodes(obj.eContents());
Figure fig = (Figure) nodeFigMap.get(obj);
if (null != fig) {
remove(fig);
nodeFigMap.remove(obj);
}
}
}
protected void updateAll(final List nodes) {
// PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
// public void run() {
// ArrayList cells = new ArrayList();
//
// for (int i = nodes.size() - 1; i >= 0; i--) {
// Figure fig = addNode((EObject) nodes.get(i));
//
// //fig.g
// }
//
// // addReferencedObjects();
// // addContainmentEdges(nodes);
// // addReferenceEdges();
// }
// });
}
private void addAllNodes(EObject parent, List l) {
for (Iterator itr = l.iterator(); itr.hasNext(); ) {
EObject obj = (EObject) itr.next();
if (nodeFigMap.containsKey(obj)) {
continue;
}
addNode(obj);
// // add edge from parent if there is one
// if (null != parent) {
// addEdge(parent, obj, true);
// }
addAllNodes(obj, obj.eContents());
}
}
private void addReferencedObjects() {
if (includeRefs) {
List l = new ArrayList();
for (Iterator cellItr = nodeFigMap.keySet().iterator(); cellItr.hasNext(); ) {
EObject srcObj = (EObject) cellItr.next();
for (Iterator refItr = srcObj.eCrossReferences().iterator(); refItr.hasNext(); ) {
Object dstObj = refItr.next();
if (dstObj instanceof EObject) {
l.add(dstObj);
}
}
}
if (l.size() > 0) {
addAllNodes(null, l);
}
}
}
private void addReferenceEdges() {
for (Iterator cellItr = nodeFigMap.keySet().iterator(); cellItr.hasNext(); ) {
EObject srcObj = (EObject) cellItr.next();
addReferenceEdges(srcObj, srcObj.eContents(), true);
addReferenceEdges(srcObj, srcObj.eCrossReferences(), false);
}
}
private void addReferenceEdges(EObject srcObj, List refObjs, boolean containment) {
for (Iterator refItr = refObjs.iterator(); refItr.hasNext(); ) {
Object dstObj = refItr.next();
if (nodeFigMap.containsKey(dstObj)) {
addEdge(srcObj, (EObject) dstObj, containment);
}
}
}
private void removeReferenceEdges(EObject obj, List refObjs, boolean containment) {
for (Iterator refItr = refObjs.iterator(); refItr.hasNext(); ) {
EObject refObj = (EObject) refItr.next();
String key = edgeKey(obj, refObj, containment);
PolylineConnection connector = (PolylineConnection) edgeFigMap.get(key);
if (null != connector) {
if (null != radialLayout) {
NodeFigure fig = (NodeFigure) connector.getTargetAnchor().getOwner();
radialLayout.setConstraint(fig, null);
// System.out.println("-- " + fig + "\t" + connector.getSourceAnchor().getOwner());
}
remove(connector);
edgeFigMap.remove(key);
// if (DEBUG) System.out.println("Del edge: " + key);
// } else {
// System.out.println("No edge: " + key);
}
}
}
private NodeFigure addNode(EObject node) {
NodeFigure fig = (NodeFigure) nodeFigMap.get(node);
if (null == fig) {
fig = new NodeFigure(node);
Resource res = node.eResource();
if (null == res) {
System.err.println("Unexpected NULL Resource for " + node);
} else {
FigureConfigurationAction action = (FigureConfigurationAction) resNodeConfig.get(res);
if (null == action) {
System.err.println("Unexpected NULL FigureConfigurationAction for " + res);
} else {
action.configure(fig);
}
}
nodeFigMap.put(node, fig);
add(fig);
}
return fig;
}
private String edgeKey(EObject source, EObject target, boolean containment) {
return source.hashCode() + "\t" + target.hashCode() + "\t" + containment;
}
private void addEdge(EObject source, EObject target, boolean containment) {
if (!containment) return;
String keyST = edgeKey(source, target, containment);
// String keyTS = edgeKey(source, target, containment);
PolylineConnection connector = (PolylineConnection) edgeFigMap.get(keyST);
if (null == connector) {
NodeFigure sourceFigure = addNode(source);
NodeFigure targetFigure = addNode(target);
connector = new NodeConnection(sourceFigure, targetFigure);
edgeFigMap.put(keyST, connector);
// edgeFigMap.put(keyTS, connector);
add(connector);
// if (DEBUG) System.out.println("New edge: " + keyST);
if (containment && null != radialLayout) {
radialLayout.setConstraint(targetFigure, sourceFigure);
// System.out.println("++n " + targetFigure + "\t" + sourceFigure);
}
} else if (null == connector.getParent()) {
add(connector);
if (containment && null != radialLayout) {
NodeFigure sourceFigure = addNode(source);
NodeFigure targetFigure = addNode(target);
radialLayout.setConstraint(targetFigure, sourceFigure);
// System.out.println("++o " + targetFigure + "\t" + sourceFigure);
}
// if (DEBUG) System.out.println("Add edge: " + keyST);
} else {
// if (DEBUG) System.out.println("Old edge: " + keyST);
}
}
public void setMaxDepth(final int depth) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
maxdepth = depth;
depthBound();
revalidate();
repaint();
}
});
}
public void showDetails() {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
for (Iterator itr = nodeFigMap.entrySet().iterator(); itr.hasNext(); ) {
Map.Entry entry = (Map.Entry) itr.next();
EObject node = (EObject) entry.getKey();
NodeFigure fig = (NodeFigure) entry.getValue();
EClass eClass = node.eClass();
List attributes = eClass.getEAllAttributes();
for (Iterator attrItr = attributes.iterator(); attrItr.hasNext(); ) {
EAttribute attr = (EAttribute) attrItr.next();
fig.addKeyVal(attr.getName(), node.eGet(attr));
}
}
}
});
}
private void depthBound() {
for (Iterator itr = nodeFigMap.values().iterator(); itr.hasNext(); ) {
NodeFigure fig = (NodeFigure) itr.next();
fig.setVisible(fig.getRank() <= maxdepth);
}
for (Iterator itr = edgeFigMap.values().iterator(); itr.hasNext(); ) {
NodeConnection fig = (NodeConnection) itr.next();
fig.setVisible(fig.getRank() <= maxdepth);
}
}
public void refresh() {
addAll(Collections.EMPTY_LIST);
}
}