XML in GUI Construction

Graphical user interfaces consist of graphical components laid out in a container. The creation, positioning, and connecting of these graphical components can be specified with a declarative syntax. XML is well suited to this task. This separation of an application into pluggable parts is an application-level example of Model-View-Controller. The view (graphical user object) can be separate from the application (functional operations).

In this section, we examine both a small and large implementation of this idea. The small implementation is a markup language to describe a GUI's menu. The large example is Bluestone's XwingML, which allows the creation of an entire Swing-based graphical user interface from an XML document.

The Menu Markup Language (MenuML)

The Menu Markup Language is a simple language that describes the elements and containment strategy of a menu bar. There are only three elements in the language: menubar, menu, and menu item. A menu bar must contain one or more menus. A menu must contain one or more menu items or menus (menu is a recursive definition). Menu must be a recursive concept to allow cascading menus (menu in a menu). A menu item is an empty element. A menu has a name attribute. A menu item has a name attribute and an action attribute. The action attribute allows you to assign a different action command (processed by the ActionListener) than the default, which is the name of the menu item. Listing 7.6 is the Document Type Definition (DTD) for the Menu Markup Language. One potential improvement to this language is to add a classname and method attribute to a menu item that would allow dynamic behavior to be loaded and linked into the Java Virtual Machine (JVM). The downside to this improvement is that it would tie the language to a Java implementation.

Code Listing 7.6. Menu Markup Language DTD
<!ELEMENT MENUBAR (MENU)+>
<!ELEMENT MENU (MENU-ITEM|MENU)+>
<!ATTLIST MENU
          name CDATA #REQUIRED>         
<!ELEMENT MENU-ITEM EMPTY>
<!ATTLIST MENU-ITEM
          name CDATA #REQUIRED
          action CDATA #IMPLIED>

Listing 7.7 is a document instance of the Menu Markup Language that tests all the capabilities of the language, including cascading menus and an alternate action command. The result of processing this document is shown in Figure 7.5.

Figure 7.5. A menu generated from a MenuML document.


Code Listing 7.7. Menu Markup Language Document
<?xml version="1.0" ?>
<!DOCTYPE MENUBAR SYSTEM "MenuML.dtd">

<MENUBAR>
        <MENU name="File">
                <MENU-ITEM name="Open"/>
                <MENU-ITEM name="Save" action="SaveDoc"/>
                <MENU-ITEM name="Quit"/>
        </MENU>
        <MENU name="Edit">
                <MENU-ITEM name="Cut"/>
                <MENU-ITEM name="Copy"/>
                <MENU-ITEM name="Paste"/>
        </MENU>
        <MENU name="Record">
                <MENU name="New">
                        <MENU-ITEM name="database"/>
                        <MENU-ITEM name="table"/>
                        <MENU-ITEM name="row"/>
                </MENU>
                <MENU-ITEM name="Next"/>
                <MENU-ITEM name="Prev"/>
        </MENU>
        <MENU name="Help">
                <MENU-ITEM name="Contents"/>
                <MENU-ITEM name="Search"/>
        </MENU>
</MENUBAR>

The DomMenu Application

The DomMenu application uses a MenuML document to generate a menu bar, menus, and menu items. Figure 7.5 is a snapshot of the application that uses the menu described in Listing 7.7.

Listing 7.8 is the source code for the DomMenu application. The application provides a factory method to generate a menu for any Menu Markup Language document.

Code Listing 7.8. DomMenu.java
/* DomMenu.java */
package sams.chp7;

import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import com.sun.xml.tree.XmlDocument;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.w3c.dom.*;
public class DomMenu extends JFrame implements ActionListener
{
    XmlDocument doc;
    static final String MENU_NAME_ATTRIBUTE="name";
    static final String MENU_ACTION_ATTRIBUTE="action";
    static final String MENU_ITEM_NAME = "MENU-ITEM";
    static final String MENU_NAME = "MENU";


    public Insets getInsets()
    {
        return new Insets(25,5,5,5);
    }

    public static JMenuBar menuFactory(Document menuMLDocument,
                                       ActionListener al)
    {
        JMenuBar mbar = new JMenuBar();

        Element root = menuMLDocument.getDocumentElement();
        // process top-level menus
        NodeList menus = root.getChildNodes();
        int numMenus = menus.getLength();
        for (int i=0; i < numMenus; i++)
        {
            Node menuNode = menus.item(i);
            NamedNodeMap attrs = menuNode.getAttributes();
            Node name = attrs.getNamedItem(MENU_NAME_ATTRIBUTE);
            JMenu menu = new JMenu(name.getNodeValue());
            mbar.add(menu);
            // recursively process this menu
            processMenu(menuNode, menu, al);
        }

        return mbar;
    }

    private static void processMenu(Node menuNode, JMenu menu,
                                    ActionListener al)
    {
        NodeList children = menuNode.getChildNodes();
        int len = children.getLength();
        for (int i=0; i < len; i++)
        {
            Node n = children.item(i);
            if (n.getNodeName().equals(MENU_ITEM_NAME))
            {
                NamedNodeMap attrs = n.getAttributes();
                Node action = attrs.getNamedItem(MENU_ACTION_ATTRIBUTE);
                Node name = attrs.getNamedItem(MENU_NAME_ATTRIBUTE);
                JMenuItem item = new JMenuItem(name.getNodeValue());
                item.addActionListener(al);
                if (action != null)
                    item.setActionCommand(action.getNodeValue());
                menu.add(item);
            }
            else if (n.getNodeName().equals(MENU_NAME))
            {
                NamedNodeMap attrs = n.getAttributes();
                Node name = attrs.getNamedItem(MENU_NAME_ATTRIBUTE);
                JMenu submenu = new JMenu(name.getNodeValue());
                menu.add(submenu);
                processMenu(n, submenu, al);
            }
        }
    }

    public void actionPerformed(ActionEvent evt)
    {
        String cmd = evt.getActionCommand();
        System.out.println("Action: " + cmd);
        if (cmd.equals("Quit"))
            System.exit(0);
    }

    public DomMenu(String fileName) throws Exception
    {
        super(fileName);

        // create a SAX InputSource
        InputSource is = new
                         InputSource(new File(fileName).toURL().toString());

        // create a DOM Document
        doc = XmlDocument.createXmlDocument(is, true);
        DomUtil.normalizeDocument(doc.getDocumentElement());

        JMenuBar mbar = menuFactory(doc, this);
        setJMenuBar(mbar);

        addWindowListener(new WindowAdapter()
                          {
                            public void windowClosing(WindowEvent we)
                            { System.exit(1); }
                          });

        setLocation(100,100);
        setSize(600,400);
        setVisible(true);
    }

    public static void main(String args[])
    {
        if (args.length < 1)
        {
            System.out.println("USAGE: java sams.chp7.DomMenu xmlfile");
            System.exit(1);
        }

        try
        {
            new DomMenu(args[0]);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}

Note the following points about Listing 7.8:

  • The main() method instantiates a DomMenu object and passes in the arguments received from the command line (an XML filename).

  • The DomMenu constructor parses the MenuML document and then passes a reference to the XmlDocument and an ActionListener to a static method called menuFactory(). This method implements the factory pattern to instantiate a JMenuBar object from a MenuML document. The reference to the ActionListener is necessary to attach the menu items to an object that can execute the action commands specified in the MenuML document.

  • The static menuFactory() method creates a menu bar, and then recurses through the MenuML document creating the associated menus and menu items described in the document. The first level of child nodes ais processed sequentially with a for loop because those are the menus that will be added to the menu bar. Then each menu is sent for further processing to a processMenu() method.

  • The processMenu() method adds menu items or menus (which is why it is recursive) to the JMenu that is passed in. In addition to creating and adding the JMenuItem object, the action listener is registered using the addActionListener() method on the JMenuItem. If a name different from the name of the menu item is desired, it can be specified in an action attribute.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.42.168