问题
I have implemented a GEF editor for a graph-like EMF model, with a remove command for a certain type of node in the graph. I think I've done all the necessary steps in order to make this set up work (vainolo's blog has been a great help).
However, when I'm deleting a model element, the view doesn't get refreshed, i.e., the figure for the model element isn't removed from the editor view, and I have no idea why. I'd be extremely grateful if somebody could have a look at my sources and point me to any problems (and possibly solutions :)). Many thanks in advance!
Below are what I think are the important classes for this issue. Please do let me know should I add further code/edit the code, etc. (I've left out code that I thought doesn't help, e.g., getters and setters, class variables). Thanks!
DiagramEditPart
public class DiagramEditPart extends AbstractGraphicalEditPart {
public DiagramEditPart(Diagram model) {
this.setModel(model);
adapter = new DiagramAdapter();
}
@Override protected IFigure createFigure() {
Figure figure = new FreeformLayer();
return figure;
}
@Override protected void createEditPolicies() {
installEditPolicy(EditPolicy.LAYOUT_ROLE, new DiagramXYLayoutPolicy());
}
@Override protected List<EObject> getModelChildren() {
List<EObject> allModelObjects = new ArrayList<EObject>();
if (((Diagram) getModel()).getMyNodes() != null)
allModelObjects.addAll(((Diagram) getModel()).getMyNodes());
return allModelObjects;
}
@Override public void activate() {
if(!isActive()) {
((Diagram) getModel()).eAdapters().add(adapter);
}
super.activate();
}
@Override public void deactivate() {
if(isActive()) {
((Diagram) getModel()).eAdapters().remove(adapter);
}
super.deactivate();
}
public class DiagramAdapter implements Adapter {
@Override public void notifyChanged(Notification notification) {
switch (notification.getEventType()) {
case Notification.REMOVE: refreshChildren();
break;
default:
break;
}
}
@Override public Notifier getTarget() {
return (Diagram) getModel();
}
@Override public void setTarget(Notifier newTarget) {
// Do nothing.
}
@Override public boolean isAdapterForType(Object type) {
return type.equals(Diagram.class);
}
}
}
MyNodeEditPart
public class MyNodeEditPart extends AbstractGraphicalEditPart {
public MyNodeEditPart(MyNode model) {
this.setModel(model);
adapter = new MyNodeAdapter();
}
@Override protected IFigure createFigure() {
return new MyNodeFigure();
}
@Override protected void createEditPolicies() {
installEditPolicy(EditPolicy.COMPONENT_ROLE, new MyNodeComponentEditPolicy());
}
@Override protected void refreshVisuals() {
MyNodeFigure figure = (MyNodeFigure) getFigure();
DiagramEditPart parent = (DiagramEditPart) getParent();
Dimension labelSize = figure.getLabel().getPreferredSize();
Rectangle layout = new Rectangle((getParent().getChildren().indexOf(this) * 50),
(getParent().getChildren().indexOf(this) * 50), (labelSize.width + 20),
(labelSize.height + 20));
parent.setLayoutConstraint(this, figure, layout);
}
public List<Edge> getModelSourceConnections() {
if ((MyNode) getModel() != null && ((MyNode) getModel()).getDiagram() != null) {
ArrayList<Edge> sourceConnections = new ArrayList<Edge>();
for (Edge edge : ((MyNode) getModel()).getDiagram().getOutEdges(((MyNode) getModel()).getId())) {
sourceConnections.add(edge);
}
return sourceConnections;
}
return null;
}
// + the same method for targetconnections
@Override public void activate() {
if (!isActive()) {
((MyNode) getModel()).eAdapters().add(adapter);
}
super.activate();
}
@Override public void deactivate() {
if (isActive()) {
((MyNode) getModel()).eAdapters().remove(adapter);
}
super.deactivate();
}
public class MyNodeAdapter implements Adapter {
@Override
public void notifyChanged(Notification notification) {
refreshVisuals();
}
@Override
public Notifier getTarget() {
return (MyNode) getModel();
}
@Override
public void setTarget(Notifier newTarget) {
// Do nothing
}
@Override
public boolean isAdapterForType(Object type) {
return type.equals(MyNode.class);
}
}
}
MyNodeComponentEditPolicy
public class MyNodeComponentEditPolicy extends ComponentEditPolicy {
@Override
protected Command createDeleteCommand(GroupRequest deleteRequest) {
DeleteMyNodeCommand nodeDeleteCommand = new DeleteMyNodeCommand((MyNode) getHost().getModel());
return nodeDeleteCommand;
}
}
DeleteMyNodeCommand
public class DeleteMyNodeCommand extends Command {
public DeleteMyNodeCommand(MyNode model) {
this.node = model;
this.graph = node.getDiagram();
}
@Override public void execute() {
getMyNode().setDiagram(null);
System.out.println("Is the model still present in the graph? " + getGraph().getMyNodes().contains(getMyNode()));
// Returns false, i.e., graph doesn't contain model object at this point!
}
@Override public void undo() {
getMyNode().setDiagram(getGraph());
}
}
EDIT
Re execc's comment: Yes, refreshChildren()
is being called. I've tested this by overriding it and adding a simple System.err
line, which is being displayed on the console on deletion of a node:
@Override
public void refreshChildren() {
super.refreshChildren();
System.err.println("refreshChildren() IS being called!");
}
EDIT 2
The funny (well...) thing is, when I close the editor and persist the model, then re-open the same file, the node isn't painted anymore, and is not present in the model. But what does this mean? Am I working on a stale model? Or is refreshing/getting the model children not working properly?
EDIT 3
I've just found a peculiar thing, which might explain the isues I have? In the getModelChildren()
method I call allModelObjects.addAll(((Diagram) getModel()).getMyNodes());
, and getMyNodes()
returns an unmodifiable EList
. I found out when I tried to do something along the lines of ((Diagram) getModel()).getMyNodes().remove(getMyNode())
in the delete command, and it threw an UnsupportedOperationException
... Hm.
EDIT 4
Er, somebody kill me please?
I've double-checked whether I'm handling the same Diagram
object at all times, and while doing this I stumbled across a very embarassing thing:
The getModelChildren()
method in DiagramEditPart
in the last version read approx. like this:
@Override protected List<EObject> getModelChildren() {
List<EObject> allModelObjects = new ArrayList<EObject>();
EList<MyNode> nodes = ((Diagram) getModel()).getMyNodes();
for (MyNode node : nodes) {
if (node.getDiagram() != null); // ### D'Uh! ###
allModelObjects.add(node);
}
return allModelObjects;
}
I'd like to apologize for stealing everyone's time! Your suggestions were very helpful, and indeed helped my to finally track down the bug!
I've also learned a number of lessons, amongst them: Always paste the original code, over-simplifaction may cloak your bugs! And I've learned a lot about EMF, Adapter
, and GEF. Still:
回答1:
There is one semi-colon too many in line 5 of the following part of the code, namely after the if statement: if (node.getDiagram() != null);
:
1 @Override protected List<EObject> getModelChildren() {
2 List<EObject> allModelObjects = new ArrayList<EObject>();
3 EList<MyNode> nodes = ((Diagram) getModel()).getMyNodes();
4 for (MyNode node : nodes) {
5 if (node.getDiagram() != null);
6 allModelObjects.add(node);
7 }
8 return allModelObjects;
9 }
来源:https://stackoverflow.com/questions/11235861/gef-emf-why-doesnt-my-editor-remove-the-figure-for-a-removed-object-when-ref