Chapter 9: Using DOM to Generate Programs

Summary

A program generator typically consists of three parts: the parser, the analyzer/transformer, and the code generator. An XML document can be read and parsed using either DOM data structures or custom data structures using SAX. For most program generators it is sufficient and easier to use DOM rather than SAX. The analyzer/transformer will examine the XML document for problems and may perform some transformations. The transformations may include the creation of custom data structures. Finally, the code generator will use either the DOM or custom data structures for generating a program.



Chapter 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Example 9-3: A Simple Program Checking for Errors in an XML Document
Example 9-8: The DOM_Util class
Example 9-9: The PlayData1 Class (original version)
Example 9-9X: The PlayData1 Class (edited version)
Example 9-10: The PlayGen1.java Class

Example 9-3: A Simple Program Checking for Errors in an XML Document

import com.ibm.xml.parser.Parser;
import org.w3c.dom.*;
import java.io.*;
import java.util.*;

public class Checker {
    int errorcount = 0;
    Document doc;
    
    /** Verify that every "friend" element has a unique
        phone attribute with numeric characters */
    public void check() {
        Hashtable phones = new Hashtable(); // map phones to nodes
        NodeList nodes = doc.getElementsByTagName("friend");
        for (int j=0; j<nodes.getLength(); ++j) {
            Element n = (Element) nodes.item(j);
            String phone = n.getAttribute("phone");
            if (phone==null || phone.length()==0) {
                error("No phone for friend: "+n);
            } else {
                for (int k=0; k<phone.length(); ++k) {
                    char c = phone.charAt(k);
                    if ("0123456789".indexOf(c) == -1) {
                        error("Invalid character in phone: "+phone);
                        break;
                    }
                }
                if (phones.get(phone)==null) {
                    phones.put(phone, n);
                } else {
                    error("duplicate phone: "+phone);
                }
            }
        }
    }
    
    /** Emit error messages */
    public void error(String m) {
        ++errorcount;
        System.out.println("Error: "+m);
    }
                
    /** Read and parse an XML file */
    public Checker(String filename)
                    throws Exception {
        InputStream is = new FileInputStream(filename);
        Parser parser = new Parser(filename);
        doc = parser.readStream(is);
    }
    
    public static void main(String[] args) throws Exception {
        Checker c = new Checker(args[0]);
        c.check();
        if (c.errorcount==0) {
            System.out.println("No errors");
        } else {
            System.out.println("Sorry, there were "
                             +c.errorcount+" errors.");
        }
    }
}

Example 9-8: The DOM_Util class

package com.craigc.progen;
import com.ibm.xml.parser.Parser;
import org.w3c.dom.*;
import java.io.*;

/**
 * XML DOM Utility Class
 */
public class DOM_Util {
    
    /** Get an attribute value from a node, using default if no node */    
    public static String getAttr(Node n, String attrName,
                                    String defaultVal) {
        if (n instanceof Document) {
            n = ((Document) n).getDocumentElement();
        }
        String v = null;
        if (n instanceof Element) {
            v = ((Element)n).getAttribute(attrName);
        }
        if (v==null || v.equals("")) {
            return defaultVal;
        }
        return v;
    }
    
    /** Find and get an integer attribute value */
    public static int getIntAttr(Node n, String tagName,
                               int defaultValue) {
        String s = getAttr(n, tagName, "");
        return Config.parseInt(s, defaultValue);
    }
    
    /** Find and get content of a node, or defaultValue if not found */
    public static String get(Node n, String tagName,
                                String defaultValue) {
        if (n instanceof Document) {
            n = ((Document) n).getDocumentElement();
        }
        if (n instanceof Element) {
            NodeList nodes = ((Element)n).getElementsByTagName(tagName);
            if (nodes.getLength()==0) {
                return defaultValue;
            } else {
                return getContent(nodes.item(0));
            }
        } else return defaultValue;
    }
    
    /** Find and get an integer value */
    public static int getInt(Node n, String tagName, int defaultValue) {
        String s = get(n, tagName, "");
        return Config.parseInt(s, defaultValue);
    }
    
    /** Get content of a node */
    public static String getContent(Node n) {
        StringBuffer buf = new StringBuffer();
        getContent1(n, buf);
        return buf.toString();
    }
    
    private static void getContent1(Node n, StringBuffer buf) {
        for (Node c = n.getFirstChild(); c!=null;
                                        c = c.getNextSibling()) {
            if (c instanceof Element || c instanceof EntityReference) {
                getContent1(c, buf);
            } else if (c instanceof Text) {
                buf.append(c.getNodeValue());
            }
        }
    }
    
    /** Read and parse an XML file */
    public static Document readDocument(String filename)
                    throws Exception {
        InputStream is = new FileInputStream(filename);
        Parser parser = new Parser(filename);
        return parser.readStream(is);
    }
}

Example 9-9: The PlayData1 Class (original version)

package com.craigc.progen;
import org.w3c.dom.*;

public class PlayData1 extends HandlerBase {
    public String name;
    public String title;
    public int width;
    public int height;
    public SceneData startScene;
    
    public PropData[] props;
    public SceneData[] scenes;
    
    public class PropData {
        public String name;
        public String label;      // initial value
        public ScriptData script; // only action script in Phase I
    }
    
    public class SceneData {
        public String name;
        public String color;
        public PropData[] addprops;
    }
    
    public class ScriptData {
        public SceneData nextScene;
        public TraitData[] traits;
    }
    
    public class TraitData {
        public PropData prop;
        public String newValue;
    }
    
    public PropData findProp(String n) {
        for (int j=0; j<props.length; ++j) {
            if (props[j].name.equals(n)) {
                return props[j];
            }
        }
        return null;
    }
    
    public SceneData findScene(String n) {
        for (int j=0; j<scenes.length; ++j) {
            if (scenes[j].name.equals(n)) {
                return scenes[j];
            }
        }
        return null;
    }
    
    public PlayData1(Document d) {
        convertDocument(d);
    }
    
    public PlayData1(String filename) throws Exception {
        convertDocument(DOM_Util.readDocument(filename));
    }
    
    /** Convert XML data using DOM to PlayData1 */
    public void convertDocument(Document d) {
        Element playNode = (Element) d.getDocumentElement();
        title = DOM_Util.get(playNode, "title", "Play Example");
        width = DOM_Util.getIntAttr(playNode, "width", 500);
        height = DOM_Util.getIntAttr(playNode, "height", 250);
        name = DOM_Util.getAttr(playNode, "name", "Unknown");
        
        // First must create data structures for all props & scenes
        // before doing scripts or traits
        
        // Create SceneData
        NodeList e = playNode.getElementsByTagName("scene");
        scenes = new SceneData[e.getLength()];
        // first create all scenes
        for (int j=0; j<e.getLength(); ++j) {
            Node bnode = e.item(j);
            SceneData n = new SceneData();
            n.name = DOM_Util.getAttr(bnode, "name", "?");
            n.color = DOM_Util.getAttr(bnode, "color", "?");
            scenes[j] = n;
        }
        
        // Create PropData
        e = playNode.getElementsByTagName("prop");
        props = new PropData[e.getLength()];
        for (int j=0; j<e.getLength(); ++j) {
            Node bnode = e.item(j);
            PropData n = new PropData();
            n.name = DOM_Util.getAttr(bnode, "name", "?");
            n.label = getLabel(bnode);
            props[j] = n;
        }
        
        for (int j=0; j<e.getLength(); ++j) {
            props[j].script = getScript(e.item(j), props[j].name);
        }
        
        // next do addprops & scripts with goto next scene
        e = playNode.getElementsByTagName("scene");
        for (int j=0; j<e.getLength(); ++j) {
            Element bnode = (Element) e.item(j);
            // Create AddPropData
            NodeList e2 = bnode.getElementsByTagName("addprop");
            scenes[j].addprops = new AddPropData[e2.getLength()];
            for (int j2=0; j2<e2.getLength(); ++j2) {
                Node anode = e2.item(j2);
                String pname = DOM_Util.getAttr(anode, "name", "?");
                scenes[j].addprops[j2] = findProp(pname);
            }
        }
        startScene = findScene(DOM_Util.getAttr(playNode,
                                 "start", "?"));
    }
    
    /** Gets label value of trait if any */
    String getLabel(Node n) {
        // ==> wrong way
        return DOM_Util.get(n, "trait", null);
        // return "?";
    }
    
    ScriptData getScript(Node n, String dname) {
        NodeList e = ((Element)n).getElementsByTagName("script");
        // only include the Action script
        for (int j=0; j<e.getLength(); ++j) {
            Element snode = (Element) e.item(j);
            // assume first and only script is Action script
            ScriptData sd = new ScriptData();
            sd.nextScene = findScene(DOM_Util.getAttr(snode,
                                                "goto", ""));
            NodeList e2 = snode.getElementsByTagName("trait");
            sd.traits = new TraitData[e2.getLength()];
            for (int j2=0; j2<e2.getLength(); ++j2) {
                Node tnode = e2.item(j2);
                sd.traits[j2] = new TraitData();
                sd.traits[j2].prop = findProp(
                            DOM_Util.getAttr(tnode, "prop", dname));
                sd.traits[j2].newValue = DOM_Util.getContent(tnode);
            }
            return sd;
        }
        return null;
    }
}

Example 9-9X: The PlayData1 Class (edited version)

package com.craigc.progen;
import org.w3c.dom.*;

public class PlayData1 {
    public String name;
    public String title;
    public int width;
    public int height;
    public SceneData startScene;
    
    public PropData[] props;
    public SceneData[] scenes;
    
    public class PropData {
        public String name;
        public String label;      // initial value
        public ScriptData script; // only action script in Phase I
    }
    
    public class SceneData {
        public String name;
        public String color;
        public PropData[] addprops;
    }
    
    public class ScriptData {
        public SceneData nextScene;
        public TraitData[] traits;
    }
    
    public class TraitData {
        public PropData prop;
        public String newValue;
    }
    
    public PropData findProp(String n) {
        for (int j=0; j<props.length; ++j) {
            if (props[j].name.equals(n)) {
                return props[j];
            }
        }
        return null;
    }
    
    public SceneData findScene(String n) {
        for (int j=0; j<scenes.length; ++j) {
            if (scenes[j].name.equals(n)) {
                return scenes[j];
            }
        }
        return null;
    }
    
    public PlayData1(Document d) {
        convertDocument(d);
    }
    
    public PlayData1(String filename) throws Exception {
        convertDocument(DOM_Util.readDocument(filename));
    }
    
    /** Convert XML data using DOM to PlayData1 */
    public void convertDocument(Document d) {
        Element playNode = (Element) d.getDocumentElement();
        title = DOM_Util.get(playNode, "title", "Play Example");
        width = DOM_Util.getIntAttr(playNode, "width", 500);
        height = DOM_Util.getIntAttr(playNode, "height", 250);
        name = DOM_Util.getAttr(playNode, "name", "Unknown");
        
        // First must create data structures for all props & scenes
        // before doing scripts or traits
        
        // Create SceneData
        NodeList e = playNode.getElementsByTagName("scene");
        scenes = new SceneData[e.getLength()];
        // first create all scenes
        for (int j=0; j<e.getLength(); ++j) {
            Node bnode = e.item(j);
            SceneData n = new SceneData();
            n.name = DOM_Util.getAttr(bnode, "name", "?");
            n.color = DOM_Util.getAttr(bnode, "color", "?");
            scenes[j] = n;
        }
        
        // Create PropData
        e = playNode.getElementsByTagName("prop");
        props = new PropData[e.getLength()];
        for (int j=0; j<e.getLength(); ++j) {
            Node bnode = e.item(j);
            PropData n = new PropData();
            n.name = DOM_Util.getAttr(bnode, "name", "?");
            n.label = getLabel(bnode);
            props[j] = n;
        }
        
        for (int j=0; j<e.getLength(); ++j) {
            props[j].script = getScript(e.item(j), props[j].name);
        }
        
        // next do addprops & scripts with goto next scene
        e = playNode.getElementsByTagName("scene");
        for (int j=0; j<e.getLength(); ++j) {
            Element bnode = (Element) e.item(j);
            // Create AddPropData
            NodeList e2 = bnode.getElementsByTagName("addprop");
            scenes[j].addprops = new PropData[e2.getLength()];
            for (int j2=0; j2<e2.getLength(); ++j2) {
                Node anode = e2.item(j2);
                String pname = DOM_Util.getAttr(anode, "name", "?");
                scenes[j].addprops[j2] = findProp(pname);
            }
        }
        startScene = findScene(DOM_Util.getAttr(playNode,
                                 "start", "?"));
    }
    
    /** Gets label value of trait if any */
    String getLabel(Node n) {
        return DOM_Util.get(n, "trait", null);
    }
    
    ScriptData getScript(Node n, String dname) {
        NodeList e = ((Element)n).getElementsByTagName("script");
        // only include the Action script
        for (int j=0; j<e.getLength(); ++j) {
            Element snode = (Element) e.item(j);
            // assume first and only script is Action script
            ScriptData sd = new ScriptData();
            sd.nextScene = findScene(DOM_Util.getAttr(snode,
                                                "goto", ""));
            NodeList e2 = snode.getElementsByTagName("trait");
            sd.traits = new TraitData[e2.getLength()];
            for (int j2=0; j2<e2.getLength(); ++j2) {
                Node tnode = e2.item(j2);
                sd.traits[j2] = new TraitData();
                sd.traits[j2].prop = findProp(
                            DOM_Util.getAttr(tnode, "prop", dname));
                sd.traits[j2].newValue = DOM_Util.getContent(tnode);
            }
            return sd;
        }
        return null;
    }
}

Example 9-10: The PlayGen1.java Class

package com.craigc.progen;
import java.io.*;

/**
 * Generator for Play domain, phase I
 */
public class PlayGen1 extends DOM_Util {
            
    public static void main(String args[]) {
        PlayData1 d = null;
        PrintWriter out;
        if (args.length>1) {
            try {
                // read and parse Play specification
                d = new PlayData1(args[0]);
                
                // open up output file
                out = new PrintWriter(new FileOutputStream(args[1]));
            } catch (Exception e) {
                System.out.println("Exception: "+args[0]);
                e.printStackTrace();
                return;
            }
        } else {
            System.out.println("Usage: PlayGen1 xml-file output-file");
            return;
        }
        
        out.println("package com.craigc.progen;");
        out.println("import java.awt.*;");
        out.println("import java.awt.event.*;");
        out.println("");
        out.println("class "+d.name+"Play2 extends Frame {");
        out.println("    ");
        out.println("    /* The Props for "+d.name+" **************/");
        out.println("    ");
        for (int j=0; j<d.props.length; ++j) {
            PlayData1.PropData prop = d.props[j];
            out.println("    Button "+prop.name+"Prop = new Button(\""
                +prop.label+"\");");
        }
        out.println("");
        out.println("    /* The Events in the "
                                     +d.name+" ***************/");
        out.println("");
        out.println("    class PropEvent implements ActionListener {");
        out.println("        public void actionPerformed(ActionEvent "
                                                            +"evt) {");
        out.println("            Object prop = evt.getSource();");
        for (int j=0; j<d.props.length; ++j) {
            PlayData1.PropData prop = d.props[j];
            out.print("            ");
            if (j!=0) {
                out.print("} else ");
            }
            out.println("if (prop=="+prop.name+"Prop) {");
            if (prop.script!=null) {
                for (int k=0; k<prop.script.traits.length; ++k) {
                    String pname = prop.script.traits[k].prop==null
                        ? prop.name : prop.script.traits[k].prop.name;
                    out.println("                "
                        +pname+"Prop.setLabel(\""
                        +prop.script.traits[k].newValue+"\");");
                }
                if (prop.script.nextScene!=null) {
                    out.println("                enterNewScene(\""
                         +prop.script.nextScene.name+"\");");
                }
            }
        }
        out.println("            } else {");
        out.println("                System.out.println("
                                        +"\"Invalid prop\");");
        out.println("            }");
        out.println("        }");
        out.println("    }");
        out.println("    ");
        out.println("    /* Creating and starting up the "+d.name
                                                    +" ****/");
        out.println("    ");
        out.println("    String currentScene;");
        out.println("    ");
        out.println("    public "+d.name+"Play2() {");
        out.println("        super(\""+d.title+"\");");
        out.println("        setSize("+d.width+", "+d.height+");");
        out.println("        setLayout(new FlowLayout());");
        out.println("        ");
        out.println("        // initialize props");
        out.println("        PropEvent a = new PropEvent();");
        for (int j=0; j<d.props.length; ++j) {
            PlayData1.PropData prop = d.props[j];
            out.println("        "+prop.name
                                  +"Prop.addActionListener(a);");
        }
        out.println("");
        out.println("        // start scene");
        out.println("        enterNewScene(\""+d.startScene.name
                                                        +"\");");
        out.println("    }");
        out.println("");
        out.println("    public void enterNewScene(String scene) {");
        out.println("        removeAll();  // remove previous scene");
        out.println("        currentScene = scene;");
        for (int k=0; k<d.scenes.length; ++k) {
            PlayData1.SceneData scene = d.scenes[k];
            out.print("        ");
            if (k!=0) {
                out.print("} else ");
            }
            out.println("if (scene.equals(\""+scene.name+"\")) {");
            for (int p2=0; p2<scene.addprops.length; ++p2) {
                PlayData1.PropData prop2 = scene.addprops[p2];
                out.println("            add("+prop2.name+"Prop);");
            }
            out.println("            setBackground(Color.decode(\""
                +scene.color+"\"));");
        }
        out.println("        } else {");
        out.println("            System.out.println(\"Invalid scene: \""
                                                           +"+scene);");
        out.println("        }");
        out.println("        show();");
        out.println("    }");
        out.println("");
        out.println("    public static void main(String[] args) {");
        out.println("        new "+d.name+"Play2();");
        out.println("    }");
        out.println("}");
        out.close();
    }
    
    public static String genScript(PlayData1.PropData n,
                                        PrintWriter out) {
        if (n.script==null) {
            return "";
        }
        for (int j=0; j<n.script.traits.length; ++j) {
            genTrait(n.script.traits[j], n, "                ", out);
        }
        return n.script.nextScene==null ? "" : n.script.nextScene.name;
    }
    
    public static void genTrait(PlayData1.TraitData trait,
            PlayData1.PropData prop, String indent, PrintWriter out) {
        String propName = trait.prop==null?prop.name:trait.prop.name;
        out.println(indent+propName+"Prop.setLabel(\""
                            +trait.newValue+"\");");
    }
}