Alternative Parsers

There are numerous XML parsers written in Java available for you to use. There are commercial parsers that are freely distributable for both commercial and non-commercial use. There are several open-source parsers. However, it does us no good to study another parser unless it has a value proposition different from the Java Standard extension. Otherwise, there is no use in learning another API. One alternative parser that has a clear value proposition is AElfred.

AElfred

AElfred is a Java-based XML parser from Microstar Software. It is free for both commercial and non-commercial use. It was originally written by David Megginson and is distributed with full source. The key value proposition of AElfred is that it is highly optimized for use with applets. The following are the design goals for AElfred:

  • AElfred must be as small as possible. AElfred is currently approximately 26KB in total. The compressed JAR file, including the optional classes, is 15KB.

  • AElfred must use as few class files as possible. AElfred consists of only two core class files—the main parser class (XmlParser.class) and a small interface for your own program to implement (XmlHandler.class). All other classes in the distribution are either optional or for demonstration only.

  • AElfred must be compatible with most Java implementations and platforms.

  • AElfred must use as little memory as possible, so that it does not take away resources from the rest of your program. To accomplish this, AElfred uses an event-based paradigm nearly identical to SAX.

  • AElfred must run as fast as possible, so that it does not slow down the rest of your program.

  • AElfred must produce correct output for well-formed and valid documents, but it need not reject every document that is not valid or not well-formed. AElfred is DTD-aware and handles all current XML features, including CDATA and INCLUDE/IGNORE marked sections, internal and external entities, proper whitespace treatment in element content, and default attribute values.

  • AElfred must provide full internationalization from the first release. AElfred correctly handles XML documents encoded using UTF-8, UTF-16, ISO-10646-UCS-2, ISO-10646-UCS-4 (as far as surrogates allow), and ISO-8859-1 (ISO Latin 1/Windows). With these character sets, AElfred can handle all of the world's major (and most of its minor) languages. To support other character encodings, you can supply your own reader.

Note

You can download AElfred from the Web at http://www.opentext.com/microstar.


As you can see from this list, AElfred is designed for production use and not for validation. For validation, the Java Standard Extension validating parser can be used.

AElfred also has a SAX 1.0 driver built in to the main distribution. The class name of the driver is com.microstar.xml.SAXDriver. However, the SAX interface is a front-end layer on top of Aelfred, so its use will slow performance. In our example, we use the AElfred API, which is nearly identical to SAX and therefore does not represent a new learning curve.

To learn how to use Aelfred, we will develop an applet that will process an XML document. Our applet will be a BookmarkViewer that allows us to view annotations on bookmarks to explain their value and optionally to jump to a bookmark URL that interests us. Our data source for the applet will be a single XML document that conforms to a simple DTD called the Bookmark List Markup Language (BLML). Listing 2.18 is our XML data source in a file called bookmarks.xml.

Code Listing 2.18. Bookmark List Markup Language Source Document
 1: <?xml version="1.0" ?>
 2: <!DOCTYPE BOOKMARK_LIST [
 3: <!ELEMENT BOOKMARK_LIST (FOLDER|BOOKMARK)+>
 4: <!ELEMENT FOLDER (FOLDER|BOOKMARK)+>
 5: <!ELEMENT BOOKMARK (COMMENT) >
 6: <!ELEMENT COMMENT (#PCDATA)>
 7: <!ATTLIST BOOKMARK
 8:         name CDATA #REQUIRED
 9:         href CDATA #REQUIRED>
10: <!ATTLIST FOLDER
11:         name CDATA #REQUIRED>
12: ]>
13:
14: <BOOKMARK_LIST>
15:         <FOLDER name = "Java" >
16:                 <BOOKMARK name="Java Technology Home"
17:                         href="http://java.sun.com" >
18:                         <COMMENT> Great site!
19:                                   The authoritative source of
20:                                   all things Java.  Both
21:                                   News and Technical Documentation
22:                                   and tutorials for Developers.
23:                                   Download all the latest Java
24:                                   Software.
25:                         </COMMENT>
26:                 </BOOKMARK>
27:         </FOLDER>
28:         <FOLDER name = "XML" >
29:                 <BOOKMARK name="IBM's XML site"
30:                           href="http://www.ibm.com/xml" >
31:                         <COMMENT> Cutting edge!
32:                                   IBM really showing its depth.
33:                                   Just like their very popular
34:                                   Java site in its completeness.
35:                         </COMMENT>
36:                 </BOOKMARK>
37:         </FOLDER>
38: ...
39: </BOOKMARK_LIST>

Although Listing 2.18 has some sections omitted for brevity, all the basic concepts of a bookmark file are there. We have the ability to have folders, subfolders, and bookmarks in our list. A bookmark has all the relevant information, such as name and href, and a subelement called COMMENT. Now we can write an applet to parse the file using the AElfred parser and display it with a GUI. Figure 2.1 shows the result of running our applet on Netscape Navigator.

Figure 2.1. BookmarkViewer applet running in Navigator.


Although this is only useful as an applet, the code will actually run as either an applet or an application. This is handy for testing the functionality before deploying it as an applet. Listing 2.19 is the source code for the BookmarkViewer applet.

Code Listing 2.19. Bookmark List Applet
  1: /* BookmarkViewer.java */
  2: package sams.chp2;
  3:
  4: import com.microstar.xml.*;
  5:
  6: import java.applet.*;
  7: import java.awt.*;
  8: import java.awt.event.*;
  9: import java.io.*;
 10: import java.net.*;
 11: import java.util.*;
 12:
 13: public class BookmarkViewer extends Applet implements XmlHandler,
 14:                                                       ActionListener,
 15:                                                       ItemListener
 16: {
 17:     private final boolean debug = false;
 18:     private boolean isApplet = true;
 19:     public final String datasource = "bookmarks.xml";
 20:     XmlParser parser = new XmlParser();
 21:     Vector bookmarks = new Vector();
 22:     Hashtable lookup = new Hashtable();
 23:
 24:     final int BOOKMARKLIST = 0;
 25:     final int FOLDER = 1;
 26:     final int BOOKMARK = 2;
 27:     final int COMMENT = 3;
 28:
 29:     public class Bookmark
 30:     {
 31:         String name;
 32:         String href;
 33:         String comment;
 34:     }
 35: 
 36:     Bookmark currentBookmark = null;
 37:     int currentElement = -1;
 38:     String lastName, lastHref;
 39:
 40:     // gui components
 41:     Label listLabel = new Label("Bookmarks");
 42:     Label commentLabel = new Label("Comment");
 43:     Label hrefLabel = new Label("URL");
 44:     java.awt.List bookmarkList = new java.awt.List(10);
 45:     TextField hrefTF = new TextField(30);
 46:     Button goButton = new Button("GO!");
 47:     TextArea commentTA = new TextArea(8, 60);
 48:
 49:     public void init()
 50:     {
 51:         String surl = null;
 52:
 53:         if (isApplet)
 54:         {
 55:             URL codebase = getCodeBase();
 56:             surl = codebase.toString() + "/" + datasource;
 57:         }
 58:         else
 59:         {
 60:             // use current directory
 61:             String currentDirectory = System.getProperty("user.dir");
 62:             String fileSep = System.getProperty("file.separator");
 63:             String file = currentDirectory.replace(fileSep.charAt(0), '/') + '/';
 64:             if (file.charAt(0) != '/')
 65:               file = "/" + file;
 66:             try
 67:             {
 68:                 URL baseURL = new URL("file", null, file);
 69:                 surl = new URL(baseURL,datasource).toString();
 70:             } catch (MalformedURLException mfue) { surl = null; }
 71:         }
 72:
 73:         parser.setHandler(this);
 74:         try
 75:         {
 76:             if (surl != null)
 77:                 parser.parse(surl, (String) null, (String) null);
 78:         } catch (Exception e)
 79:           {
 80:             e.printStackTrace();
 81:           }
 82:
 83:
 84:         // here we should have a full Vector
 85:         // set up our GUI
 86:         GridBagConstraints gbc = new GridBagConstraints();
 87:         gbc.fill = GridBagConstraints.HORIZONTAL;
 88:         gbc.weightx = 1.0; gbc.weighty = 1.0;
 89:         gbc.ipadx = 4; gbc.ipady = 4;
 90:         GridBagLayout gbl = new GridBagLayout();
 91:         setLayout(gbl);
 92:         gbc.gridx = 1; gbc.gridy = 1;
 93:         gbc.gridwidth = 5; gbc.gridheight = 1;
 94:         gbc.anchor = GridBagConstraints.WEST;
 95:         gbl.setConstraints(listLabel, gbc);
 96:         add(listLabel);
 97:         gbc.gridx = 1; gbc.gridy = 3;
 98:         gbc.gridwidth = 5; gbc.gridheight = 7;
 99:         gbc.fill = GridBagConstraints.BOTH;
100:         gbc.anchor = GridBagConstraints.CENTER;
101:         gbl.setConstraints(bookmarkList, gbc);
102:         add(bookmarkList);
103:         gbc.gridx = 7; gbc.gridy = 1;
104:         gbc.gridwidth = 1; gbc.gridheight = 1;
105:         gbc.fill = GridBagConstraints.NONE;
106:         gbc.anchor = GridBagConstraints.WEST;
107:         gbl.setConstraints(hrefLabel, gbc);
108:         add(hrefLabel);
109:         gbc.gridx = 8; gbc.gridy = 1;
110:         gbc.gridwidth = 4; gbc.gridheight = 1;
111:         gbc.fill = GridBagConstraints.HORIZONTAL;
112:         gbl.setConstraints(hrefTF, gbc);
113:         add(hrefTF);
114:         gbc.gridx = 13; gbc.gridy = 1;
115:         gbc.gridwidth = 1; gbc.gridheight = 1;
116:         gbl.setConstraints(goButton, gbc);
117:         add(goButton);
118:         goButton.addActionListener(this);
119:         gbc.gridx = 7; gbc.gridy = 3;
120:         gbc.gridwidth = 4; gbc.gridheight = 1;
121:         gbc.anchor = GridBagConstraints.CENTER;
122:         gbl.setConstraints(commentLabel, gbc);
123:         add(commentLabel);
124:         gbc.gridx = 7; gbc.gridy = 4;
125:         gbc.gridwidth = 8; gbc.gridheight = 6;
126:         gbc.fill = GridBagConstraints.BOTH;
127:         gbl.setConstraints(commentTA, gbc);
128:         add(commentTA);
129: 
130:         // fill the bookmark list
131:         bookmarkList.addItemListener(this);
132:         int count = bookmarks.size();
133:         if (count > 0)
134:         {
135:             for (int i=0; i < count; i++)
136:             {
137:                 Bookmark b = (Bookmark) bookmarks.elementAt(i);
138:                 /* addItem() replaced by add() in JDK1.2.
139:                    However, most browsers are still on 1.1. */
140:                 if (b != null && b.name != null)
141:                 {
142:                     bookmarkList.addItem(b.name);
143:                     // use the name as the key
144:                     lookup.put(b.name, b);
145:                 }
146:             }
147:         }
148:     }
149:
150:     public void actionPerformed(ActionEvent evt)
151:     {
152:         String cmd = evt.getActionCommand();
153: 
154:         if (isApplet)
155:         {
156:             if (cmd.equals("GO!"))
157:             {
158:                 try
159:                 {
160:                     AppletContext context = getAppletContext();
161:                     String surl = hrefTF.getText();
162:                     URL u = new URL(surl);
163:                     context.showDocument(u);
164:                 } catch (MalformedURLException mue)
165:                   { showStatus("Incorrect URL."); }
166:             }
167:         }
168:         else
169:             System.out.println("Operation only supported in an Applet.");
170:     }
171:
172:     public void itemStateChanged(ItemEvent evt)
173:     {
174:         if (evt.getStateChange() == ItemEvent.SELECTED)
175:         {
176:             // We can cheat here, we know the List is the source
177:             String key = bookmarkList.getSelectedItem();
178:             // Now get the object
179:             Bookmark b = (Bookmark) lookup.get(key);
180:             if (b != null)
181:             {
182:                 hrefTF.setText(b.href);
183:                 commentTA.setText(b.comment);
184:             }
185:         }
186:     }
187:
188:     public static void main(String args[])
189:     {
190:         BookmarkViewer viewer = new BookmarkViewer();
191:         viewer.isApplet = false;
192:
193:         // add panel to Frame
194:         Frame f = new Frame("Test Bookmark Viewer Frame");
195:
196:         viewer.init();
197:         f.add("Center", viewer);
198: 
199:         f.addWindowListener(new WindowAdapter()
200:                             {
201:                                 public void windowClosing(WindowEvent we)
202:                                 { System.exit(0); }
203:                             });
204:
205:         // show Frame
206:         f.setLocation(100,100);
207:         //f.setSize(500,400);
208:         f.pack();
209:         f.setVisible(true);
210:     }
211:
212:     private void setElementType(String ename)
213:     {
214:         if (ename.equals("BOOKMARKLIST"))
215:             currentElement = BOOKMARKLIST;
216:         else if (ename.equals("FOLDER"))
217:             currentElement = FOLDER;
218:         else if (ename.equals("BOOKMARK"))
219:             currentElement = BOOKMARK;
220:         else if (ename.equals("COMMENT"))
221:             currentElement = COMMENT;
222:     }
223:
224:     /** XmlHandler: Start the document */
225:     public void startDocument () throws java.lang.Exception
226:     {
227:         if (debug) System.out.println("startDocument()");
228:     }
229:
230:
231:     /**
232:     * XmlHandler: End the document.
233:     */
234:     public void endDocument () throws java.lang.Exception
235:     {
236:         if (debug) System.out.println("endDocument()");
237: 
238:     }
239:
240:
241:     /**
242:     * XmlHandler: Resolve an External Entity.
243:     */
244:     public Object resolveEntity (String publicId, String systemId)
245:     throws java.lang.Exception
246:     {
247:         if (debug) System.out.println("resolveEntity(publicId: "
248:         + publicId + ",systemId: " + systemId + ")");
249:         return null;
250:     }
251:
252:     /**
253:     * XmlHandler: Begin an external entity.
254:     */
255:     public void startExternalEntity (String systemId)
256:     throws java.lang.Exception
257:     {
258:         if (debug) System.out.println("startExternalEntity(systeId: " + systemId + ")");
259:
260:     }
261:
262:     /**
263:     * XmlHandler: End an external entity.
264:     */
265:     public void endExternalEntity (String systemId)
266:     throws java.lang.Exception
267:     {
268:         if (debug) System.out.println("endExternalEntity(systemId: " + systemId + ")");
269:
270:     }
271:
272: 
273:     /**
274:     * XmlHandler: Document type declaration.
275:     */
276:     public void doctypeDecl (String name, String publicId, String systemId)
277:     throws java.lang.Exception
278:     {
279:         if (debug) System.out.println("doctypeDecl(name: " + name +
280:                                       ",publicId: " + publicId +
281:                                       ",systemId: " + systemId + ")");
282:
283:     }
284:
285:     /**
286:     * XmlHandler: Attribute.
287:     */
288:     public void attribute (String aname, String value, boolean isSpecified)
289:     throws java.lang.Exception
290:     {
291:         if (debug) System.out.println("attribute(aname: " + aname +
292:                    ",value: " + value +
293:                    ",isSpecified: " + isSpecified + ")");
294:
295:         // Aelfred attribute events are BEFORE the startElement call
296:         if (aname.equals("name"))
297:             lastName = value;
298:         else if (aname.equals("href"))
299:             lastHref = value;
300:     }
301:
302:     /**
303:     * XmlHandler: Start an element.
304:     */
305:     public void startElement (String elname)
306:     throws java.lang.Exception
307:     {
308:         if (debug) System.out.println("startElement(elname: " + elname + ")");
309:         setElementType(elname);
310:         if (elname.equals("BOOKMARK"))
311:         {
312:             currentBookmark = new Bookmark();
313:             // NOTE: attribute events are before the startElement event.
314:             //       NON-INTUITIVE!  SAX uses a better way.
315:             if (lastName != null)
316:             {
317:                 currentBookmark.name = lastName;
318:                 lastName = null;
319:             }
320:
321:             if (lastHref != null)
322:             {
323:                 currentBookmark.href = lastHref;
324:                 lastHref = null;
325:             }
326:         }
327:     }
328:
329:     /**
330:     * XmlHandler: End an element.
331:     */
332:     public void endElement (String elname)
333:     throws java.lang.Exception
334:     {
335:         if (debug) System.out.println("endElement(elname: " + elname +")");
336:         if (elname.equals("BOOKMARK") && currentBookmark != null)
337:             bookmarks.addElement(currentBookmark);
338:     }
339:
340:
341:     /**
342:     * XmlHandler: Character data. */
343:     public void charData (char ch[], int start, int length)
344:     throws java.lang.Exception
345:     {
346:         if (debug) System.out.println("charData(ch:" +
347:                    new String(ch, start, length) + ")");
348:         switch (currentElement)
349:         {
350:             case BOOKMARKLIST:
351:                 break;
352:             case FOLDER:
353:                 break;
354:             case BOOKMARK:
355:                 break;
356:             case COMMENT:
357:                 if (currentBookmark != null)
358:                     currentBookmark.comment = new String(ch, start, length);
359:                 break;
360:         }
361:     }
362:
363:
364:     /** XmlHandler: Ignorable whitespace. */
365:     public void ignorableWhitespace (char ch[], int start, int length)
366:     throws java.lang.Exception
367:     {
368:         if (debug) System.out.println("ignorableWhitespace(...)");
369:
370:     }
371:
372:     /** XmlHandler: Processing instruction.*/
373:     public void processingInstruction (String target, String data)
374:     throws java.lang.Exception
375:     {
376:         if (debug) System.out.println("processingInstruction(target: "
377:         + target + ",data: " + data + ")");
378:
379:     }
380:
381: 
382:     /** XmlHandler: Fatal XML parsing error. */
383:     public void error (String message, String systemId, int line, int column)
384:     throws java.lang.Exception
385:     {
386:         if (debug) System.out.println("error(message:" + message + ")");
387:         if (!isApplet)
388:             System.exit(1);
389:     }
390: }

Note the following points about BookmarkViewer.java:

  • The best way to understand the program is to separate the parsing aspects from the GUI code. These two are really separate actions. In fact, the GUI is not even instantiated until after the parsing has occurred. When the user interface pops up, the parsing has already been completed.

  • The class BookmarkViewer implements three interfaces: two for GUI event handling (ActionListener and ItemListener) and one for AElfred event handling (XmlHandler). The XmlHandler interface has quite a few methods that closely resemble methods in SAX. In the code, all the methods belonging to the XmlHandler interface are clearly identified in a preceding comment.

  • The parsing completes and produces a vector of Bookmark objects. Bookmark is a simple inner class.

  • The init() method of the applet first parses the XML data source and then instantiates and lays out the user interface in the applet. The XmlParser class in AElfred is the parser. There are three steps to using this parser. First, instantiate an object of type XmlParser. Second, call the setHandler() method on the parser to set the current object as the parser event handler. Lastly, call the parse() method on the parser and pass in a URI. The rest of the init() method is concerned with properly placing the UI elements (an awt List, labels, text field, and text area) using a GridBagLayout.

  • The Go button in the GUI fires an ActionEvent. The actionPerformed() method uses the showDocument() method of the Applet class to jump to the specified URL.

  • The selection of a list item fires an ItemEvent. The itemStateChanged() method triggers the viewing of a specific bookmark when its name is selected in the list.

  • The main() method is used to run the class as an application. It brings up a frame and places the applet (which extends Panel) inside the center of the frame. This allows us to test the GUI before deploying it as an applet.

  • The rest of the code is the implementation of the XmlHandler methods. Most are unused. Only four methods are used: startElement(), endElement(), attribute(), and charData(). Note: attribute processing is different in AElfred than in SAX. In AElfred, the attribute events are fired before the startElement event (of the element that has the attributes). While this is non-intuitive, it is the way it is currently implemented in AElfred.

After we compile BookmarkViewer.java, we need to collect it and the AElfred classes into a jar file for distribution as an applet. We use the jar tool to add the three classes for BookmarkViewer and the AElfred classes into a single file called bookmark.jar. The bookmark.jar file is only 28,596 bytes (about 28KB). The last step to distribute this code as an applet is to create an HTML page that has an applet tag. Listing 2.20 is the HTML page for this applet.

Code Listing 2.20. HTML Page to Display the BoomarkViewer Applet
 1: <HTML>
 2: <HEAD>
 3: <TITLE> Book Mark Viewer Applet </TITLE>
 4: </HEAD>
 5: <BODY>
 6: <H2> Book Mark Viewer using XML. </H2>
 7: <APPLET code = "sams.chp2.BookmarkViewer.class"
 8:         archive = bookmark.jar
 9:         width = 400 height = 300>
10: </APPLET>
11: </BODY>
12: </HTML>

When loaded in a Web browser, Figure 2.1 is displayed.

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

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