How to Serialize Visual Library Scenes

The many Visual Library samples in "contrib" include one called "serialization". I used that sample today to serialize one of my own scenes.

Before showing the code, some screenshots. First, I drag and drop one or two items from the palette onto my scene:

Next, after resizing them (code for which you can find via the NetBeans Platform Tutorials page, bearing in mind this issue), I save them, using the Save menu item:

Then, simply as a result of using the above menu item, this XML file is created, containing the state of my objects, which I can then load into the scene via the Load menu item above:

Here's how to do it (but, again, have a look at the sample pointed out above, in the first sentence of this blog entry). Before looking at the code below, you need to be aware that I have a business object called "AWidget", which has a name, an icon, a width, a height, and a location. I also have a scene called "AWidgetGraphScene", which extends "GraphScene", using my business object for the node of the scene. The scene has no edges.

  1. In my scene constructor:
    final JPopupMenu menu = new JPopupMenu();
    
    JMenuItem load = new JMenuItem("Load scene...");
    load.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            load ();
        }
    });
    menu.add(load);
    
    JMenuItem save = new JMenuItem("Save scene...");
    save.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            save();
        }
    });
    menu.add(save);
    
    getActions().addAction(ActionFactory.createPopupMenuAction(new PopupMenuProvider() {
        @Override
        public JPopupMenu getPopupMenu(Widget widget, Point localLocation) {
            return menu;
        }
    }));

  2. Here are the definitions of "load()" and "save()":
    private void load () {
        JFileChooser chooser = new JFileChooser ();
        chooser.setDialogTitle ("Load Scene ...");
        chooser.setMultiSelectionEnabled (false);
        chooser.setFileSelectionMode (JFileChooser.FILES_ONLY);
        if (chooser.showOpenDialog (getView ()) == JFileChooser.APPROVE_OPTION) {
            for (String edge : new ArrayList (getEdges ()))
                removeEdge (edge);
            for (AWidget node : new ArrayList (getNodes ()))
                removeNode (node);
            SceneSerializer.deserialize (this, chooser.getSelectedFile ());
            validate ();
        }
    }
    
    private void save() {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle("Save Scene ...");
        chooser.setMultiSelectionEnabled(false);
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (chooser.showSaveDialog(getView()) == JFileChooser.APPROVE_OPTION) {
            SceneSerializer.serialize(this, chooser.getSelectedFile());
        }
    }

  3. And here, almost identical to the original referred to above, is the "SceneSerializer" class:
    import org.netbeans.api.visual.widget.Widget;
    import org.openide.util.Exceptions;
    import org.openide.xml.XMLUtil;
    import org.w3c.dom.\*;
    import org.xml.sax.ErrorHandler;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.SAXParseException;
    
    import java.awt.\*;
    import java.io.\*;
    
    public class SceneSerializer {
    
        private static final String SCENE_ELEMENT = "Scene"; // NOI18N
        private static final String VERSION_ATTR = "version"; // NOI18N
    
        private static final String SCENE_NODE_COUNTER_ATTR = "nodeIDcounter"; // NOI18N
    
        private static final String NODE_ELEMENT = "Node"; // NOI18N
        private static final String NODE_ID_ATTR = "id"; // NOI18N
        private static final String NODE_X_ATTR = "x"; // NOI18N
        private static final String NODE_Y_ATTR = "y"; // NOI18N
        private static final String NODE_WIDTH = "w"; // NOI18N
        private static final String NODE_HEIGHT = "h"; // NOI18N
        private static final String NODE_ICON = "icon"; // NOI18N
    
        private static final String VERSION_VALUE_1 = "1"; // NOI18N
    
        // call in AWT to serialize scene
        public static void serialize (AWidgetGraphScene scene, File file) {
            Document document = XMLUtil.createDocument (SCENE_ELEMENT, null, null, null);
    
            Node sceneElement = document.getFirstChild ();
            setAttribute (document, sceneElement, VERSION_ATTR, VERSION_VALUE_1);
            setAttribute (document, sceneElement, SCENE_NODE_COUNTER_ATTR, Long.toString (scene.nodeIDcounter));
    
            for (AWidget node : scene.getNodes ()) {
                Element nodeElement = document.createElement (NODE_ELEMENT);
                setAttribute (document, nodeElement, NODE_ID_ATTR, node.getType());
                Widget widget = scene.findWidget (node);
                Point location = widget.getPreferredLocation ();
                setAttribute (document, nodeElement, NODE_WIDTH, Integer.toString (widget.getClientArea().width));
                setAttribute (document, nodeElement, NODE_HEIGHT, Integer.toString (widget.getClientArea().height));
                setAttribute (document, nodeElement, NODE_X_ATTR, Integer.toString (location.x));
                setAttribute (document, nodeElement, NODE_Y_ATTR, Integer.toString (location.y));
                setAttribute (document, nodeElement, NODE_ICON, node.getIcon());
                sceneElement.appendChild (nodeElement);
            }
    
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream (file);
                XMLUtil.write (document, fos, "UTF-8"); // NOI18N
            } catch (Exception e) {
                Exceptions.printStackTrace (e);
            } finally {
                try {
                    if (fos != null) {
                        fos.close ();
                    }
                } catch (Exception e) {
                    Exceptions.printStackTrace (e);
                }
            }
        }
    
        // call in AWT to deserialize scene
        public static void deserialize (AWidgetGraphScene scene, File file) {
            Node sceneElement = getRootNode (file);
            if (! VERSION_VALUE_1.equals (getAttributeValue (sceneElement, VERSION_ATTR)))
                return;
            scene.nodeIDcounter = Long.parseLong (getAttributeValue (sceneElement, SCENE_NODE_COUNTER_ATTR));
            for (Node element : getChildNode (sceneElement)) {
                if (NODE_ELEMENT.equals (element.getNodeName ())) {
                    String type = getAttributeValue (element, NODE_ID_ATTR);
                    int x = Integer.parseInt (getAttributeValue (element, NODE_X_ATTR));
                    int y = Integer.parseInt (getAttributeValue (element, NODE_Y_ATTR));
                    int width = Integer.parseInt (getAttributeValue (element, NODE_WIDTH));
                    int height = Integer.parseInt (getAttributeValue (element, NODE_HEIGHT));
                    String icon = getAttributeValue (element, NODE_ICON);
                    AWidget aWidget = new AWidget();
                    aWidget.setType(type);
                    aWidget.setIcon(icon);
                    Widget nodeWidget = scene.addNode(aWidget);
                    nodeWidget.setPreferredLocation (new Point (x, y));
                    nodeWidget.setPreferredSize (new Dimension (width, height));
                } 
            }
        }
    
        private static void setAttribute (Document xml, Node node, String name, String value) {
            NamedNodeMap map = node.getAttributes ();
            Attr attribute = xml.createAttribute (name);
            attribute.setValue (value);
            map.setNamedItem (attribute);
        }
    
        private static Node getRootNode (File file) {
            FileInputStream is = null;
            try {
                is = new FileInputStream (file);
                Document doc = XMLUtil.parse (new InputSource (is), false, false, new ErrorHandler() {
                    public void error (SAXParseException e) throws SAXException {
                        throw new SAXException (e);
                    }
    
                    public void fatalError (SAXParseException e) throws SAXException {
                        throw new SAXException (e);
                    }
    
                    public void warning (SAXParseException e) {
                        Exceptions.printStackTrace (e);
                    }
                }, null);
                return doc.getFirstChild ();
            } catch (Exception e) {
                Exceptions.printStackTrace (e);
            } finally {
                try {
                    if (is != null)
                        is.close ();
                } catch (IOException e) {
                    Exceptions.printStackTrace (e);
                }
            }
            return null;
        }
    
        private static String getAttributeValue (Node node, String attr) {
            try {
                if (node != null) {
                    NamedNodeMap map = node.getAttributes ();
                    if (map != null) {
                        node = map.getNamedItem (attr);
                        if (node != null)
                            return node.getNodeValue ();
                    }
                }
            } catch (DOMException e) {
                Exceptions.printStackTrace (e);
            }
            return null;
        }
    
        private static Node[] getChildNode (Node node) {
            NodeList childNodes = node.getChildNodes ();
            Node[] nodes = new Node[childNodes != null ? childNodes.getLength () : 0];
            for (int i = 0; i < nodes.length; i++)
                nodes[i] = childNodes.item (i);
            return nodes;
        }
    
    }

That's it. Put the generated XML page into a MultiViewElement and then you have the makings of a GUI editor.

Comments:

You may want to check out how the UML component serializes the diagram information. It was designed to be extensible since the serialization mechanism needed to support multiple types of nodes. Including container nodes.

Posted by Trey Spiva on August 25, 2009 at 05:31 AM PDT #

Hi, i nedd to display a object images (objet compose with varios basic shape, circle, arc,etc), and the other funcionaliti its cahnge the color depend of arbritari value. its possible?

thanks

Posted by Daniel on August 26, 2009 at 04:43 AM PDT #

using this method how can you serialize/deserialize edges and pins for the widgets?

Posted by Donald on September 09, 2009 at 06:08 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today