|
You now know how to write Java programs that read XML. Let us now turn to the opposite process, producing XML output. Of course, you could write an XML file simply by making a sequence of print calls, printing the elements, attributes, and text content, but that would not be a good idea. The code is rather tedious, and you can easily make mistakes if you don't pay attention to special symbols (such as " or <) in the attribute values and text content. A better approach is to build up a DOM tree with the contents of the document and then write out the tree contents. To build a DOM tree, you start out with an empty document. You can get an empty document by calling the newDocument method of the DocumentBuilder class. Document doc = builder.newDocument(); Use the createElement method of the Document class to construct the elements of your document. Element rootElement = doc.createElement(rootName); Element childElement = doc.createElement(childName); Use the createTextNode method to construct text nodes: Text textNode = doc.createTextNode(textContents); Add the root element to the document, and add the child nodes to their parents: doc.appendChild(rootElement); rootElement.appendChild(childElement); childElement.appendChild(textNode); As you build up the DOM tree, you may also need to set element attributes. Simply call the setAttribute method of the Element class: rootElement.setAttribute(name, value); Somewhat curiously, the DOM API currently has no support for writing a DOM tree to an output stream. To overcome this limitation, we use the XML Style Sheet Transformations (XSLT) API. We apply the "do nothing" transformation to the document and capture its output. To include a DOCTYPE node in the output, you also need to set the SYSTEM and PUBLIC identifiers as output properties. // construct the "do nothing" transformation Transformer t = TransformerFactory.newInstance().newTransformer(); // set output properties to get a DOCTYPE node t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemIdentifier); t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicIdentifier); // apply the "do nothing" transformation and send the output to a file t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(file))); For more information about XSLT, turn to the next section. Right now, consider this code a "magic incantation" to produce XML output. NOTE
Example 12-9 is a typical program that produces XML output. The program draws a modernist paintinga random set of colored rectangles (see Figure 12-6). To save a masterpiece, we use the Scalable Vector Graphics (SVG) format. SVG is an XML format to describe complex graphics in a device-independent fashion. You can find more information about SVG at http://www.w3c.org/Graphics/SVG. To view SVG files, download the Apache Batik viewer (http://xml.apache.org/batik) or the Adobe browser plug-in (http://www.adobe.com/svg/main.html). Figure 12-7 shows the Apache Batik viewer. Figure 12-6. Generating modern artFigure 12-7. The Apache Batik SVG viewerWe don't go into details about SVG. If you are interested in SVG, we suggest you start with the tutorial on the Adobe site. For our purposes, we just need to know how to express a set of colored rectangles. Here is a sample: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000802//EN" "http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd"> <svg width="300" height="150"> <rect x="231" y="61" width="9" height="12" fill="#6e4a13"/> <rect x="107" y="106" width="56" height="5" fill="#c406be"/> . . . </svg> As you can see, each rectangle is described as a rect node. The position, width, height, and fill color are attributes. The fill color is an RGB value in hexadecimal. NOTE
Here is the source code for the program. You can use the same technique whenever you need to generate XML output. Example 12-9. XMLWriteTest.java1. import java.awt.*; 2. import java.awt.geom.*; 3. import java.io.*; 4. import java.util.*; 5. import java.awt.event.*; 6. import javax.swing.*; 7. import javax.xml.parsers.*; 8. import javax.xml.transform.*; 9. import javax.xml.transform.dom.*; 10. import javax.xml.transform.stream.*; 11. import org.w3c.dom.*; 12. 13. 14. /** 15. This program shows how to write an XML file. It saves 16. a file describing a modern drawing in SVG format. 17. */ 18. public class XMLWriteTest 19. { 20. public static void main(String[] args) 21. { 22. XMLWriteFrame frame = new XMLWriteFrame(); 23. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 24. frame.setVisible(true); 25. } 26. } 27. 28. /** 29. A frame with a panel for showing a modern drawing. 30. */ 31. class XMLWriteFrame extends JFrame 32. { 33. public XMLWriteFrame() 34. { 35. setTitle("XMLWriteTest"); 36. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 37. 38. chooser = new JFileChooser(); 39. 40. // add panel to frame 41. 42. panel = new RectanglePanel(); 43. add(panel); 44. 45. // set up menu bar 46. 47. JMenuBar menuBar = new JMenuBar(); 48. setJMenuBar(menuBar); 49. 50. JMenu menu = new JMenu("File"); 51. menuBar.add(menu); 52. 53. JMenuItem newItem = new JMenuItem("New"); 54. menu.add(newItem); 55. newItem.addActionListener(new 56. ActionListener() 57. { 58. public void actionPerformed(ActionEvent event) { panel.newDrawing(); } 59. }); 60. 61. JMenuItem saveItem = new JMenuItem("Save"); 62. menu.add(saveItem); 63. saveItem.addActionListener(new 64. ActionListener() 65. { 66. public void actionPerformed(ActionEvent event) 67. { 68. try 69. { 70. saveDocument(); 71. } 72. catch (TransformerException e) 73. { 74. JOptionPane.showMessageDialog( 75. XMLWriteFrame.this, e.toString()); 76. } 77. catch (IOException e) 78. { 79. JOptionPane.showMessageDialog( 80. XMLWriteFrame.this, e.toString()); 81. } 82. } 83. }); 84. 85. JMenuItem exitItem = new JMenuItem("Exit"); 86. menu.add(exitItem); 87. exitItem.addActionListener(new 88. ActionListener() 89. { 90. public void actionPerformed(ActionEvent event) { System.exit(0); } 91. }); 92. } 93. 94. /** 95. Saves the drawing in SVG format. 96. */ 97. public void saveDocument() 98. throws TransformerException, IOException 99. { 100. if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return; 101. File f = chooser.getSelectedFile(); 102. Document doc = panel.buildDocument(); 103. Transformer t = TransformerFactory.newInstance().newTransformer(); 104. t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, 105. "http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd"); 106. t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD SVG 20000802//EN"); 107. 108. t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(f))); 109. } 110. 111. public static final int DEFAULT_WIDTH = 300; 112. public static final int DEFAULT_HEIGHT = 200; 113. 114. private RectanglePanel panel; 115. private JFileChooser chooser; 116. } 117. 118. /** 119. A panel that shows a set of colored rectangles 120. */ 121. class RectanglePanel extends JPanel 122. { 123. public RectanglePanel() 124. { 125. rects = new ArrayList<Rectangle2D>(); 126. colors = new ArrayList<Color>(); 127. generator = new Random(); 128. 129. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 130. try 131. { 132. builder = factory.newDocumentBuilder(); 133. } 134. catch (ParserConfigurationException e) 135. { 136. e.printStackTrace(); 137. } 138. } 139. 140. /** 141. Create a new random drawing. 142. */ 143. public void newDrawing() 144. { 145. int n = 10 + generator.nextInt(20); 146. rects.clear(); 147. colors.clear(); 148. for (int i = 1; i <= n; i++) 149. { 150. int x = generator.nextInt(getWidth()); 151. int y = generator.nextInt(getHeight()); 152. int width = generator.nextInt(getWidth() - x); 153. int height = generator.nextInt(getHeight() - y); 154. rects.add(new Rectangle(x, y, width, height)); 155. int r = generator.nextInt(256); 156. int g = generator.nextInt(256); 157. int b = generator.nextInt(256); 158. colors.add(new Color(r, g, b)); 159. } 160. repaint(); 161. } 162. 163. public void paintComponent(Graphics g) 164. { 165. if (rects.size() == 0) newDrawing(); 166. super.paintComponent(g); 167. Graphics2D g2 = (Graphics2D) g; 168. 169. // draw all rectangles 170. for (int i = 0; i < rects.size(); i++) 171. { 172. g2.setPaint(colors.get(i)); 173. g2.fill(rects.get(i)); 174. } 175. } 176. 177. /** 178. Creates an SVG document of the current drawing. 179. @return the DOM tree of the SVG document 180. */ 181. public Document buildDocument() 182. { 183. 184. Document doc = builder.newDocument(); 185. Element svgElement = doc.createElement("svg"); 186. doc.appendChild(svgElement); 187. svgElement.setAttribute("width", "" + getWidth()); 188. svgElement.setAttribute("height", "" + getHeight()); 189. for (int i = 0; i < rects.size(); i++) 190. { 191. Color c = colors.get(i); 192. Rectangle2D r = rects.get(i); 193. Element rectElement = doc.createElement("rect"); 194. rectElement.setAttribute("x", "" + r.getX()); 195. rectElement.setAttribute("y", "" + r.getY()); 196. rectElement.setAttribute("width", "" + r.getWidth()); 197. rectElement.setAttribute("height", "" + r.getHeight()); 198. rectElement.setAttribute("fill", colorToString(c)); 199. svgElement.appendChild(rectElement); 200. } 201. return doc; 202. } 203. 204. /** 205. Converts a color to a hex value. 206. @param c a color 207. @return a string of the form #rrggbb 208. */ 209. private static String colorToString(Color c) 210. { 211. StringBuffer buffer = new StringBuffer(); 212. buffer.append(Integer.toHexString(c.getRGB() & 0xFFFFFF)); 213. while(buffer.length() < 6) buffer.insert(0, '0'); 214. buffer.insert(0, '#'); 215. return buffer.toString(); 216. } 217. 218. private ArrayList<Rectangle2D> rects; 219. private ArrayList<Color> colors; 220. private Random generator; 221. private DocumentBuilder builder; 222. } javax.xml.parsers.DocumentBuilder 1.4
org.w3c.dom.Document 1.4
org.w3c.dom.Node 1.4
org.w3c.dom.Element 1.4
javax.xml.transform.TransformerFactory 1.4
javax.xml.transform.Transformer 1.4
javax.xml.transform.dom.DOMSource 1.4
javax.xml.transform.stream.StreamResult 1.4
|
|