• nobody wants to write the markup by hand- general-purpose XML editors are too clunky
<cards>
  <card>
    <name>John Doe</name>
    <title>CEO, Widget Inc.</title>
    <email>john.doe@widget.com</email>
    <phone>(202.html) 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.html) {
    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.html) {
  Vector v = new Vector();
  Iterator i = d.getRootElement().getChildren().iterator();
  while (i.hasNext(.html)) {
    Element e = (Element.html)i.next();
    String phone = e.getChildText("phone".html);
    if (phone==null.html) phone="";
    Element logo = e.getChild("logo".html);
    String url;
    if (logo==null.html) url = ""; else url = logo.getAttributeValue("url".html);
    Card c = new Card(e.getChildText("name".html),  // exploit schema,
                      e.getChildText("title".html), // assume validity
                      e.getChildText("email".html),
                      phone,
                      url);
    v.add(c.html);
  }
  return v;
}

And back into an XML document:

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

A little logic and some GUI then completes the editor:

GUI

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.html) 
  • 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.html) {

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".html);
Button delete = new Button("delete".html);
Button clear = new Button("clear".html);
Button save = new Button("save".html);
Button quit = new Button("quit".html);
TextField name = new TextField(20.html);
TextField title = new TextField(20.html);
TextField email = new TextField(20.html);
TextField phone = new TextField(20.html);
TextField logo = new TextField(20.html);
Panel cardpanel = new Panel(new GridLayout(0,1.html));
String cardfile;
Vector cardvector;
int current = -1;

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

Vector doc2vector(Document d.html) {

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

}

Document vector2doc() {

Element cards = new Element(&quot;cards&quot;.html);
for (int i=0; i&lt;cardvector.size(.html); i&#43;&#43;) {
  Card c = (Card.html)cardvector.elementAt(i.html);
  if (c!=null.html) {
    Element card = new Element(&quot;card&quot;.html);
    Element name = new Element(&quot;name&quot;.html);
    name.addContent(c.name);
    card.addContent(name.html);
    Element title = new Element(&quot;title&quot;.html);
    title.addContent(c.title);
    card.addContent(title.html);
    Element email = new Element(&quot;email&quot;.html);
    email.addContent(c.email);
    card.addContent(email.html);
    if (!c.phone.equals(&quot;&quot;.html)) {
      Element phone = new Element(&quot;phone&quot;.html);
      phone.addContent(c.phone);
      card.addContent(phone.html);
    }
    if (!c.logo.equals(&quot;&quot;.html)) {
      Element logo = new Element(&quot;logo&quot;.html);
      logo.setAttribute(&quot;url&quot;,c.logo);
      card.addContent(logo.html);
    }
    cards.addContent(card.html);
  }
}
return new Document(cards.html);

}

void addCards() {

cardpanel.removeAll();
for (int i=0; i&lt;cardvector.size(.html); i&#43;&#43;) {
  Card c = (Card.html)cardvector.elementAt(i.html);
  if (c!=null.html) {
    Button b = new Button(c.name);
    b.setActionCommand(String.valueOf(i.html));
    b.addActionListener(this.html);
    cardpanel.add(b.html);
  }
}
this.pack();

}

public BCedit(String cardfile.html) {

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

}

public void actionPerformed(ActionEvent event.html) {

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

}
}