A typical Java application is a domain-specific XML editor:

  • nobody wants to write the markup by hand
  • general-purpose XML editors are too clunky

We generalize the business card language to allow collections of business cards:



<cards>   <card>     <name>John Doe</name>     <title>CEO, Widget Inc.</title>     <email>john.doe@widget.com</email>     <phone>(202) 456-1414</phone>     <logo url="widget.gif" />   </card>   <card>     <name>Michael Schwartzbach</name>     <title>Associate Professor</title>     <email>mis@brics.dk</email>     <phone>+45 8610 8790</phone>     <logo url="http://www.brics.dk/~mis/portrait.gif" />   </card>   <card>     <name>Anders Møller</name>     <title>Research Assistant Professor</title>     <email>amoeller@brics.dk</email>     <phone>+45 8942 3475</phone>     <logo url="http://www.brics.dk/~amoeller/am.jpg"/>   </card> </cards>


We then write a Java ​​program​​ to edit such collections.

First, we need a high-level representation of a business card:



class Card {   public String name, title, email, phone, logo;    public Card(String name, String title, String email, String phone, String logo) {     this.name = name;     this.title = title;     this.email = email;     this.phone = phone;     this.logo = logo;   } }


An XML document must then be translated into a vector of such objects:



Vector doc2vector(Document d) {   Vector v = new Vector();   Iterator i = d.getRootElement().getChildren().iterator();   while (i.hasNext()) {     Element e = (Element)i.next();     String phone = e.getChildText("phone");     if (phone==null) phone="";     Element logo = e.getChild("logo");     String url;     if (logo==null) url = ""; else url = logo.getAttributeValue("url");     Card c = new Card(e.getChildText("name"),  // exploit schema,                       e.getChildText("title"), // assume validity                       e.getChildText("email"),                       phone,                       url);     v.add(c);   }   return v; }


And back into an XML document:



Document vector2doc() {   Element cards = new Element("cards");   for (int i=0; i<cardvector.size(); i++) {     Card c = (Card)cardvector.elementAt(i);     if (c!=null) {       Element card = new Element("card");       Element name = new Element("name");       name.addContent(c.name);       card.addContent(name);       Element title = new Element("title");       title.addContent(c.title);       card.addContent(title);       Element email = new Element("email");       email.addContent(c.email);       card.addContent(email);       if (!c.phone.equals("")) {         Element phone = new Element("phone");         phone.addContent(c.phone);         card.addContent(phone);       }       if (!c.logo.equals("")) {         Element logo = new Element("logo");         logo.setAttribute("url",c.logo);         card.addContent(logo);       }       cards.addContent(card);     }   }   return new Document(cards); }


A little logic and some GUI then completes the editor:

jdom解析xml_jar

Compile with: javac -classpath xerces.jar:jdom.jar BCedit.java

This example contains some general observations:


  • XML documents are parsed via JDOM into domain-specific data structures
  • if the input is known to validate according to some schema, then many runtime errors can be assumed never to occur
  • how do we ensure that the output of vector2doc is valid according to the schema? (well-formedness is for free) 
    - that's a current research challenge!
  • import java.awt.*;
    import java.awt.event.*;
    import java.io.*;
    import java.util.*;
    import org.jdom.*;
    import org.jdom.input.*;
    import org.jdom.output.*;

    class Card {
    public String name, title, email, phone, logo;

    public Card(String name, String title, String email, String phone, String logo) {
    this.name = name;
    this.title = title;
    this.email = email;
    this.phone = phone;
    this.logo = logo;
    }
    }

    public class BCedit extends Frame implements ActionListener {
    Button ok = new Button("ok");
    Button delete = new Button("delete");
    Button clear = new Button("clear");
    Button save = new Button("save");
    Button quit = new Button("quit");
    TextField name = new TextField(20);
    TextField title = new TextField(20);
    TextField email = new TextField(20);
    TextField phone = new TextField(20);
    TextField logo = new TextField(20);
    Panel cardpanel = new Panel(new GridLayout(0,1));
    String cardfile;
    Vector cardvector;
    int current = -1;

    public static void main(String[] args) { new BCedit(args[0]); }

    Vector doc2vector(Document d) {
    Vector v = new Vector();
    Iterator i = d.getRootElement().getChildren().iterator();
    while (i.hasNext()) {
    Element e = (Element)i.next();
    String phone = e.getChildText("phone");
    if (phone==null) phone="";
    Element logo = e.getChild("logo");
    String url;
    if (logo==null) url=""; else url=logo.getAttributeValue("url");
    Card c = new Card(e.getChildText("name"),
    e.getChildText("title"),
    e.getChildText("email"),
    phone,
    url);
    v.add(c);
    }
    return v;
    }

    Document vector2doc() {
    Element cards = new Element("cards");
    for (int i=0; i<cardvector.size(); i++) {
    Card c = (Card)cardvector.elementAt(i);
    if (c!=null) {
    Element card = new Element("card");
    Element name = new Element("name");
    name.addContent(c.name);
    card.addContent(name);
    Element title = new Element("title");
    title.addContent(c.title);
    card.addContent(title);
    Element email = new Element("email");
    email.addContent(c.email);
    card.addContent(email);
    if (!c.phone.equals("")) {
    Element phone = new Element("phone");
    phone.addContent(c.phone);
    card.addContent(phone);
    }
    if (!c.logo.equals("")) {
    Element logo = new Element("logo");
    logo.setAttribute("url",c.logo);
    card.addContent(logo);
    }
    cards.addContent(card);
    }
    }
    return new Document(cards);
    }

    void addCards() {
    cardpanel.removeAll();
    for (int i=0; i<cardvector.size(); i++) {
    Card c = (Card)cardvector.elementAt(i);
    if (c!=null) {
    Button b = new Button(c.name);
    b.setActionCommand(String.valueOf(i));
    b.addActionListener(this);
    cardpanel.add(b);
    }
    }
    this.pack();
    }

    public BCedit(String cardfile) {
    super("BCedit");
    this.cardfile=cardfile;
    try {
    cardvector = doc2vector(new SAXBuilder().build(new File(cardfile)));
    } catch (Exception e) {e.printStackTrace();}
    this.setLayout(new BorderLayout());
    ScrollPane s = new ScrollPane();
    s.setSize(200,0);
    s.add(cardpanel);
    this.add(s,BorderLayout.WEST);
    Panel l = new Panel(new GridLayout(5,1));
    l.add(new Label("Name"));
    l.add(new Label("Title"));
    l.add(new Label("Email"));
    l.add(new Label("Phone"));
    l.add(new Label("Logo"));
    this.add(l,BorderLayout.CENTER);
    Panel f = new Panel(new GridLayout(5,1));
    f.add(name);
    f.add(title);
    f.add(email);
    f.add(phone);
    f.add(logo);
    this.add(f,BorderLayout.EAST);
    Panel p = new Panel();
    ok.addActionListener(this);
    p.add(ok);
    delete.addActionListener(this);
    p.add(delete);
    clear.addActionListener(this);
    p.add(clear);
    save.addActionListener(this);
    p.add(save);
    quit.addActionListener(this);
    p.add(quit);
    this.add(p,BorderLayout.SOUTH);
    addCards();
    this.show();
    }

    public void actionPerformed(ActionEvent event) {
    Card c;
    String command = event.getActionCommand();
    if (command.equals("ok")) {
    c = new Card(name.getText(),
    title.getText(),
    email.getText(),
    phone.getText(),
    logo.getText());
    if (current==-1) {
    cardvector.add(c);
    } else {
    cardvector.setElementAt(c,current);
    }
    addCards();
    } else if (command.equals("delete")) {
    if (current!=-1) {
    cardvector.setElementAt(null,current);
    addCards();
    }
    } else if (command.equals("clear")) {
    current = -1;
    name.setText("");
    title.setText("");
    email.setText("");
    phone.setText("");
    logo.setText("");
    } else if (command.equals("save")) {
    try {
    new XMLOutputter().output(vector2doc(),new FileOutputStream(cardfile));
    } catch (Exception e) {e.printStackTrace();}
    } else if (command.equals("quit")) {
    System.exit(0);
    } else {
    current = Integer.parseInt(command);
    c = (Card)cardvector.elementAt(current);
    name.setText(c.name);
    title.setText(c.title);
    email.setText(c.email);
    phone.setText(c.phone);
    logo.setText(c.logo);
    }
    }
    }