Horizontal casting via copy constructor

I’m allowing a user to change a selected JGoObject from one subclass of JGoBasicNode to another subclass, to effectively change it’s visual properties.

A node can have different visual properties (see init() method)…

[code]
public abstract class Node extends JGoBasicNode {
public Node() {
super("");
init();
}

public abstract void init();

public Node(Node that) {
    //super((JGoBasicNode)that);
    this();
    this.setLocation(that.getLocation());
    this.setPort(that.getPort());
}

}
public class BasicNode extends Node {
public BasicNode() {super(); }

public BasicNode(Node that) {
    super(that);
    init();
}

public void init() {
    setBrush(JGoBrush.white);
    setPen(JGoPen.darkGray);
}

}[/code]

is ‘converted’ (think horizontal casting, would such a thing exist)

myContextMenuItem.addSelectionListener(new SelectionAdapter() {
    public void widgetSelected(SelectionEvent e) {
        JGoDocument doc = constNode.getDocument();
        doc.startTransaction();
        doc.addObjectAtTail(<span style="font-weight: bold;">new BasicNode(constNode)</span>);
        doc.removeObject(constNode);
        doc.endTransaction("converted to basic node");
    }
});

I don’t care about any visual properties being copied, I’d actually prefer that the copied object start default as a clean JGoBasicNode. I do need to maintain the links, and any other ‘important’ (i.e. non-visual - the ‘soul’ of the object) assets or children that the JGoBasicNode had.

Is there a convenient way to do this? Am I going about it the right way, generally speaking? Right now the links are all discarded and it appears that the port object (that I can left drag new links from) is also discarded. I was hoping this would work by reusing the port object but that won’t work.

Is the best way to do this simply to iterate through each link attached to the port, and manually attach it to the new port, as if the user had dragged the link handle to a new port a la default behavior?

some pictures to demonstrate:

corrupted:

The easiest thing to do is to make sure you call the constructor overload that takes a String argument, since that will do the standard initialization for you.
There’s no easy way to “replace” or “splice” a node with another node. The general difficulty is that they might have different ports, or no way to identify or distinguish ports from each other, so there’s no way to tell which links need to be reconnected to which ports.
But presumably you do know that, particularly when there’s just one port on the original node and one port on the replacement node. Just add the new node and iterate over the links connected to the port. For each link, if the FromPort is that port, set it to be the new node’s port instead. Do the same for the link’s ToPort. When you are done you can remove the original node.

Hi Walter,

Thanks for the response (as always). I had basically already implemented what you said, but I had wondered if there is a sexier way, I guess not, no problem. Thing is that it is tempermental - sometimes it will copy all the links, sometimes not. I don’t really understand why, the code is very straightforward.

[code] public RMNode(RMNode that) {
this(); //calls JGoBasicNode("")
this.setLocation(that.getLocation());

    //this works sometimes and I don't know why
    //this does not work if we manually connect lots of links to it- some of them
    //are copied over but some aren't

    JGoListPosition pos=that.getPort().getFirstLinkPos();
    while(pos!=null) {
        JGoLink link = that.getPort().getLinkAtPos(pos);
        if (that.getPort()==link.getFromPort()) {
            link.setFromPort(this.getPort());
        } else if (that.getPort()==link.getToPort()) {
            link.setToPort(this.getPort());
        }
        pos=that.getPort().getNextLinkPos(pos);
    }
}[/code]

Ah, you are modifying the list while iterating over it.
Perhaps just changing the call of getNextLinkPos(pos) to getFirstLinkPos() would fix it.

can you point me to an example similar to this, to get me started in making this code redoable?

This works for me:
public void displaceNode(JGoBasicNode orig, JGoBasicNode sub) {
orig.getLayer().addObjectAtTail(sub);
JGoPort origport = orig.getPort();
JGoPort subport = sub.getPort();
JGoListPosition pos = origport.getFirstLinkPos();
while (pos != null) {
JGoLink link = origport.getLinkAtPos(pos);
pos = origport.getFirstLinkPos();
if (link.getFromPort() == origport) link.setFromPort(subport);
if (link.getToPort() == origport) link.setToPort(subport);
}
orig.getLayer().removeObject(orig);
}

hm, bug must be somewhere else then. my code was basically identical to your code, but i switched it out anyway, and same problem. the node “displaces” as expected, undoes fine, but on redo does not reconstruct the links.

bleh, i’ll figure it out. at least i know im not supposed to do the copyNewValue and fireupdate stuff.

Just to confirm, I put the code that creates a replacement node and then calls displaceNode, inside a transaction. Everything works the way I would expect with undo and redo.
When you say redo does not reconstruct the links, do you mean that they do not show up again in the document? Or that they do appear in the document/view, but they are not connected to the proper node?
And upon an undo, as far as you can tell the original node is back in the document and properly linked up?

OOPS

I described it wrong. I’ll leave it to the screenshots to clarify

before anything happens

now we convert to a start node (this works 100%)

now I undo, this is obviously wrong - the connected links are gone. the original node appears though

now I redo, links still gone, but the converted node is here

I only used the meat of your code (that copies over the links) - I am calling startTransaction, calling the copy constructor, (the copy constructor uses your little algorithm to iterate through the links and switches their connections to ports on a brand new node), then ending transaction.

I’m going to repeat some code that was pasted above for clarity, but I’m pretty sure we are on the same page, and the bug is not to be found here. here it is anyway as a sanity check.

this triggers a ‘displacement’ when we activate an item in the context menu

public void widgetSelected(SelectionEvent e) { JGoDocument doc = constNode.getDocument(); doc.startTransaction(); doc.addObjectAtTail(new StartNode(constNode)); doc.removeObject(constNode); doc.endTransaction("converted to Start node"); }
StartNode extends RMNode with its own style (i.e. label text, green BkColor) by overriding the init() method, it doesn’t touch the links or anything else, so I won’t paste it here.

[code]public abstract class RMNode extends JGoBasicNode {
JGoText label;
public RMNode(String label) {
super("");
this.label=new JGoText(label);
//getPort().setSize(21, 21);
//setSize(12, 12);
hideLabel();
init();
}
public RMNode(RMNode that) {
//super((JGoBasicNode)that);

    this("");
    this.setLocation(that.getLocation());

    JGoPort thisport = this.getPort();
    JGoPort thatport = that.getPort();
    
     //http://www.nwoods.com/forum/forum_posts.asp?TID=1430&P N=1
    JGoListPosition pos=thatport.getFirstLinkPos();
    while(pos!=null) {
        JGoLink link = thatport.getLinkAtPos(pos);
        pos=that.getPort().getFirstLinkPos();
        
        if (link.getFromPort()==thatport) link.setFromPort(this.getPort());
        if (link.getToPort()==thatport) link.setToPort(this.getPort());
    }
}


}[/code]

As I said earlier, it doesn’t make any sense, because my method is basically identical to yours. I must be doing something stupid elsewhere; I’ll keep looking. Hopefully we are on the same page though now.

Oh, I see. The problem is that you are doing those operations in a constructor.
The UndoManager is associated with a JGoDocument. The only way for undo/redo changes to be recorded is if the JGoObject is part of a JGoDocument. During the constructor a JGoObject is just another chunk of memory since it isn’t part of a JGoDocument yet.
So allocate and initialize your RMNode first, and then call my displaceNode method, which will add the substitute node to the document before relinking everything on the original node.

wow, thanks.