- 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) 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;
|
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:
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);
}
}
}