KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > XMLMemento


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.ui;
13
14 import java.io.IOException JavaDoc;
15 import java.io.PrintWriter JavaDoc;
16 import java.io.Reader JavaDoc;
17 import java.io.Writer JavaDoc;
18 import java.util.ArrayList JavaDoc;
19
20 import javax.xml.parsers.DocumentBuilder JavaDoc;
21 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
22 import javax.xml.parsers.ParserConfigurationException JavaDoc;
23
24 import org.eclipse.ui.internal.WorkbenchMessages;
25 import org.eclipse.ui.internal.WorkbenchPlugin;
26 import org.w3c.dom.Attr JavaDoc;
27 import org.w3c.dom.Document JavaDoc;
28 import org.w3c.dom.Element JavaDoc;
29 import org.w3c.dom.NamedNodeMap JavaDoc;
30 import org.w3c.dom.Node JavaDoc;
31 import org.w3c.dom.NodeList JavaDoc;
32 import org.w3c.dom.Text JavaDoc;
33 import org.xml.sax.InputSource JavaDoc;
34 import org.xml.sax.SAXException JavaDoc;
35
36 /**
37  * This class represents the default implementation of the
38  * <code>IMemento</code> interface.
39  * <p>
40  * This class is not intended to be extended by clients.
41  * </p>
42  *
43  * @see IMemento
44  */

45 public final class XMLMemento implements IMemento {
46     private Document JavaDoc factory;
47
48     private Element JavaDoc element;
49
50     /**
51      * Creates a <code>Document</code> from the <code>Reader</code>
52      * and returns a memento on the first <code>Element</code> for reading
53      * the document.
54      * <p>
55      * Same as calling createReadRoot(reader, null)
56      * </p>
57      *
58      * @param reader the <code>Reader</code> used to create the memento's document
59      * @return a memento on the first <code>Element</code> for reading the document
60      * @throws WorkbenchException if IO problems, invalid format, or no element.
61      */

62     public static XMLMemento createReadRoot(Reader JavaDoc reader)
63             throws WorkbenchException {
64         return createReadRoot(reader, null);
65     }
66
67     /**
68      * Creates a <code>Document</code> from the <code>Reader</code>
69      * and returns a memento on the first <code>Element</code> for reading
70      * the document.
71      *
72      * @param reader the <code>Reader</code> used to create the memento's document
73      * @param baseDir the directory used to resolve relative file names
74      * in the XML document. This directory must exist and include the
75      * trailing separator. The directory format, including the separators,
76      * must be valid for the platform. Can be <code>null</code> if not
77      * needed.
78      * @return a memento on the first <code>Element</code> for reading the document
79      * @throws WorkbenchException if IO problems, invalid format, or no element.
80      */

81     public static XMLMemento createReadRoot(Reader JavaDoc reader, String JavaDoc baseDir)
82             throws WorkbenchException {
83         String JavaDoc errorMessage = null;
84         Exception JavaDoc exception = null;
85
86         try {
87             DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory
88                     .newInstance();
89             DocumentBuilder JavaDoc parser = factory.newDocumentBuilder();
90             InputSource JavaDoc source = new InputSource JavaDoc(reader);
91             if (baseDir != null) {
92                 source.setSystemId(baseDir);
93             }
94             Document JavaDoc document = parser.parse(source);
95             NodeList JavaDoc list = document.getChildNodes();
96             for (int i = 0; i < list.getLength(); i++) {
97                 Node JavaDoc node = list.item(i);
98                 if (node instanceof Element JavaDoc) {
99                     return new XMLMemento(document, (Element JavaDoc) node);
100                 }
101             }
102         } catch (ParserConfigurationException JavaDoc e) {
103             exception = e;
104             errorMessage = WorkbenchMessages.XMLMemento_parserConfigError;
105         } catch (IOException JavaDoc e) {
106             exception = e;
107             errorMessage = WorkbenchMessages.XMLMemento_ioError;
108         } catch (SAXException JavaDoc e) {
109             exception = e;
110             errorMessage = WorkbenchMessages.XMLMemento_formatError;
111         }
112
113         String JavaDoc problemText = null;
114         if (exception != null) {
115             problemText = exception.getMessage();
116         }
117         if (problemText == null || problemText.length() == 0) {
118             problemText = errorMessage != null ? errorMessage
119                     : WorkbenchMessages.XMLMemento_noElement;
120         }
121         throw new WorkbenchException(problemText, exception);
122     }
123
124     /**
125      * Returns a root memento for writing a document.
126      *
127      * @param type the element node type to create on the document
128      * @return the root memento for writing a document
129      */

130     public static XMLMemento createWriteRoot(String JavaDoc type) {
131         Document JavaDoc document;
132         try {
133             document = DocumentBuilderFactory.newInstance()
134                     .newDocumentBuilder().newDocument();
135             Element JavaDoc element = document.createElement(type);
136             document.appendChild(element);
137             return new XMLMemento(document, element);
138         } catch (ParserConfigurationException JavaDoc e) {
139 // throw new Error(e);
140
throw new Error JavaDoc(e.getMessage());
141         }
142     }
143
144     /**
145      * Creates a memento for the specified document and element.
146      * <p>
147      * Clients should use <code>createReadRoot</code> and
148      * <code>createWriteRoot</code> to create the initial
149      * memento on a document.
150      * </p>
151      *
152      * @param document the document for the memento
153      * @param element the element node for the memento
154      */

155     public XMLMemento(Document JavaDoc document, Element JavaDoc element) {
156         super();
157         this.factory = document;
158         this.element = element;
159     }
160
161     /* (non-Javadoc)
162      * Method declared in IMemento.
163      */

164     public IMemento createChild(String JavaDoc type) {
165         Element JavaDoc child = factory.createElement(type);
166         element.appendChild(child);
167         return new XMLMemento(factory, child);
168     }
169
170     /* (non-Javadoc)
171      * Method declared in IMemento.
172      */

173     public IMemento createChild(String JavaDoc type, String JavaDoc id) {
174         Element JavaDoc child = factory.createElement(type);
175         child.setAttribute(TAG_ID, id == null ? "" : id); //$NON-NLS-1$
176
element.appendChild(child);
177         return new XMLMemento(factory, child);
178     }
179
180     /* (non-Javadoc)
181      * Method declared in IMemento.
182      */

183     public IMemento copyChild(IMemento child) {
184         Element JavaDoc childElement = ((XMLMemento) child).element;
185         Element JavaDoc newElement = (Element JavaDoc) factory.importNode(childElement, true);
186         element.appendChild(newElement);
187         return new XMLMemento(factory, newElement);
188     }
189
190     /* (non-Javadoc)
191      * Method declared in IMemento.
192      */

193     public IMemento getChild(String JavaDoc type) {
194
195         // Get the nodes.
196
NodeList JavaDoc nodes = element.getChildNodes();
197         int size = nodes.getLength();
198         if (size == 0) {
199             return null;
200         }
201
202         // Find the first node which is a child of this node.
203
for (int nX = 0; nX < size; nX++) {
204             Node JavaDoc node = nodes.item(nX);
205             if (node instanceof Element JavaDoc) {
206                 Element JavaDoc element = (Element JavaDoc) node;
207                 if (element.getNodeName().equals(type)) {
208                     return new XMLMemento(factory, element);
209                 }
210             }
211         }
212
213         // A child was not found.
214
return null;
215     }
216
217     /* (non-Javadoc)
218      * Method declared in IMemento.
219      */

220     public IMemento[] getChildren(String JavaDoc type) {
221
222         // Get the nodes.
223
NodeList JavaDoc nodes = element.getChildNodes();
224         int size = nodes.getLength();
225         if (size == 0) {
226             return new IMemento[0];
227         }
228
229         // Extract each node with given type.
230
ArrayList JavaDoc list = new ArrayList JavaDoc(size);
231         for (int nX = 0; nX < size; nX++) {
232             Node JavaDoc node = nodes.item(nX);
233             if (node instanceof Element JavaDoc) {
234                 Element JavaDoc element = (Element JavaDoc) node;
235                 if (element.getNodeName().equals(type)) {
236                     list.add(element);
237                 }
238             }
239         }
240
241         // Create a memento for each node.
242
size = list.size();
243         IMemento[] results = new IMemento[size];
244         for (int x = 0; x < size; x++) {
245             results[x] = new XMLMemento(factory, (Element JavaDoc) list.get(x));
246         }
247         return results;
248     }
249
250     /* (non-Javadoc)
251      * Method declared in IMemento.
252      */

253     public Float JavaDoc getFloat(String JavaDoc key) {
254         Attr JavaDoc attr = element.getAttributeNode(key);
255         if (attr == null) {
256             return null;
257         }
258         String JavaDoc strValue = attr.getValue();
259         try {
260             return new Float JavaDoc(strValue);
261         } catch (NumberFormatException JavaDoc e) {
262             WorkbenchPlugin.log("Memento problem - Invalid float for key: " //$NON-NLS-1$
263
+ key + " value: " + strValue, e); //$NON-NLS-1$
264
return null;
265         }
266     }
267
268     /* (non-Javadoc)
269      * Method declared in IMemento.
270      */

271     public String JavaDoc getID() {
272         return element.getAttribute(TAG_ID);
273     }
274
275     /* (non-Javadoc)
276      * Method declared in IMemento.
277      */

278     public Integer JavaDoc getInteger(String JavaDoc key) {
279         Attr JavaDoc attr = element.getAttributeNode(key);
280         if (attr == null) {
281             return null;
282         }
283         String JavaDoc strValue = attr.getValue();
284         try {
285             return new Integer JavaDoc(strValue);
286         } catch (NumberFormatException JavaDoc e) {
287             WorkbenchPlugin
288                     .log("Memento problem - invalid integer for key: " + key //$NON-NLS-1$
289
+ " value: " + strValue, e); //$NON-NLS-1$
290
return null;
291         }
292     }
293
294     /* (non-Javadoc)
295      * Method declared in IMemento.
296      */

297     public String JavaDoc getString(String JavaDoc key) {
298         Attr JavaDoc attr = element.getAttributeNode(key);
299         if (attr == null) {
300             return null;
301         }
302         return attr.getValue();
303     }
304
305     /* (non-Javadoc)
306      * Method declared in IMemento.
307      */

308     public String JavaDoc getTextData() {
309         Text JavaDoc textNode = getTextNode();
310         if (textNode != null) {
311             return textNode.getData();
312         }
313         return null;
314     }
315
316     /**
317      * Returns the Text node of the memento. Each memento is allowed only
318      * one Text node.
319      *
320      * @return the Text node of the memento, or <code>null</code> if
321      * the memento has no Text node.
322      */

323     private Text JavaDoc getTextNode() {
324         // Get the nodes.
325
NodeList JavaDoc nodes = element.getChildNodes();
326         int size = nodes.getLength();
327         if (size == 0) {
328             return null;
329         }
330         for (int nX = 0; nX < size; nX++) {
331             Node JavaDoc node = nodes.item(nX);
332             if (node instanceof Text JavaDoc) {
333                 return (Text JavaDoc) node;
334             }
335         }
336         // a Text node was not found
337
return null;
338     }
339
340     /**
341      * Places the element's attributes into the document.
342      * @param copyText true if the first text node should be copied
343      */

344     private void putElement(Element JavaDoc element, boolean copyText) {
345         NamedNodeMap JavaDoc nodeMap = element.getAttributes();
346         int size = nodeMap.getLength();
347         for (int i = 0; i < size; i++) {
348             Attr JavaDoc attr = (Attr JavaDoc) nodeMap.item(i);
349             putString(attr.getName(), attr.getValue());
350         }
351
352         NodeList JavaDoc nodes = element.getChildNodes();
353         size = nodes.getLength();
354         // Copy first text node (fixes bug 113659).
355
// Note that text data will be added as the first child (see putTextData)
356
boolean needToCopyText = copyText;
357         for (int i = 0; i < size; i++) {
358             Node JavaDoc node = nodes.item(i);
359             if (node instanceof Element JavaDoc) {
360                 XMLMemento child = (XMLMemento) createChild(node.getNodeName());
361                 child.putElement((Element JavaDoc) node, true);
362             } else if (node instanceof Text JavaDoc && needToCopyText) {
363                 putTextData(((Text JavaDoc) node).getData());
364                 needToCopyText = false;
365             }
366         }
367     }
368
369     /* (non-Javadoc)
370      * Method declared in IMemento.
371      */

372     public void putFloat(String JavaDoc key, float f) {
373         element.setAttribute(key, String.valueOf(f));
374     }
375
376     /* (non-Javadoc)
377      * Method declared in IMemento.
378      */

379     public void putInteger(String JavaDoc key, int n) {
380         element.setAttribute(key, String.valueOf(n));
381     }
382
383     /* (non-Javadoc)
384      * Method declared in IMemento.
385      */

386     public void putMemento(IMemento memento) {
387         // Do not copy the element's top level text node (this would overwrite the existing text).
388
// Text nodes of children are copied.
389
putElement(((XMLMemento) memento).element, false);
390     }
391
392     /* (non-Javadoc)
393      * Method declared in IMemento.
394      */

395     public void putString(String JavaDoc key, String JavaDoc value) {
396         if (value == null) {
397             return;
398         }
399         element.setAttribute(key, value);
400     }
401
402     /* (non-Javadoc)
403      * Method declared in IMemento.
404      */

405     public void putTextData(String JavaDoc data) {
406         Text JavaDoc textNode = getTextNode();
407         if (textNode == null) {
408             textNode = factory.createTextNode(data);
409             // Always add the text node as the first child (fixes bug 93718)
410
element.insertBefore(textNode, element.getFirstChild());
411         } else {
412             textNode.setData(data);
413         }
414     }
415
416     /**
417      * Saves this memento's document current values to the
418      * specified writer.
419      *
420      * @param writer the writer used to save the memento's document
421      * @throws IOException if there is a problem serializing the document to the stream.
422      */

423     public void save(Writer JavaDoc writer) throws IOException JavaDoc {
424         DOMWriter out = new DOMWriter(writer);
425         try {
426             out.print(element);
427         } finally {
428             out.close();
429         }
430     }
431
432     /**
433      * A simple XML writer. Using this instead of the javax.xml.transform classes allows
434      * compilation against JCL Foundation (bug 80053).
435      */

436     private static final class DOMWriter extends PrintWriter JavaDoc {
437         
438         private int tab;
439
440         /* constants */
441         private static final String JavaDoc XML_VERSION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; //$NON-NLS-1$
442

443         /**
444          * Creates a new DOM writer on the given output writer.
445          *
446          * @param output the output writer
447          */

448         public DOMWriter(Writer JavaDoc output) {
449             super(output);
450             tab = 0;
451             println(XML_VERSION);
452         }
453
454         /**
455          * Prints the given element.
456          *
457          * @param element the element to print
458          */

459         public void print(Element JavaDoc element) {
460             // Ensure extra whitespace is not emitted next to a Text node,
461
// as that will result in a situation where the restored text data is not the
462
// same as the saved text data.
463
boolean hasChildren = element.hasChildNodes();
464             startTag(element, hasChildren);
465             if (hasChildren) {
466                 tab++;
467                 boolean prevWasText = false;
468                 NodeList JavaDoc children = element.getChildNodes();
469                 for (int i = 0; i < children.getLength(); i++) {
470                     Node JavaDoc node = children.item(i);
471                     if (node instanceof Element JavaDoc) {
472                         if (!prevWasText) {
473                             println();
474                             printTabulation();
475                         }
476                         print((Element JavaDoc) children.item(i));
477                         prevWasText = false;
478                     }
479                     else if (node instanceof Text JavaDoc) {
480                         print(getEscaped(node.getNodeValue()));
481                         prevWasText = true;
482                     }
483                 }
484                 tab--;
485                 if (!prevWasText) {
486                     println();
487                     printTabulation();
488                 }
489                 endTag(element);
490             }
491         }
492
493         private void printTabulation() {
494             // Indenting is disabled, as it can affect the result of getTextData().
495
// In 3.0, elements were separated by a newline but not indented.
496
// This causes getTextData() to return "\n" even if no text data had explicitly been set.
497
// The code here emulates that behaviour.
498

499 // for (int i = 0; i < tab; i++)
500
// super.print("\t"); //$NON-NLS-1$
501
}
502
503         private void startTag(Element JavaDoc element, boolean hasChildren) {
504             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
505             sb.append("<"); //$NON-NLS-1$
506
sb.append(element.getTagName());
507             NamedNodeMap JavaDoc attributes = element.getAttributes();
508             for (int i = 0; i < attributes.getLength(); i++) {
509                 Attr JavaDoc attribute = (Attr JavaDoc)attributes.item(i);
510                 sb.append(" "); //$NON-NLS-1$
511
sb.append(attribute.getName());
512                 sb.append("=\""); //$NON-NLS-1$
513
sb.append(getEscaped(String.valueOf(attribute.getValue())));
514                 sb.append("\""); //$NON-NLS-1$
515
}
516             sb.append(hasChildren ? ">" : "/>"); //$NON-NLS-1$ //$NON-NLS-2$
517
print(sb.toString());
518         }
519
520         private void endTag(Element JavaDoc element) {
521             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
522             sb.append("</"); //$NON-NLS-1$
523
sb.append(element.getNodeName());
524             sb.append(">"); //$NON-NLS-1$
525
print(sb.toString());
526         }
527         
528         private static void appendEscapedChar(StringBuffer JavaDoc buffer, char c) {
529             String JavaDoc replacement = getReplacement(c);
530             if (replacement != null) {
531                 buffer.append('&');
532                 buffer.append(replacement);
533                 buffer.append(';');
534             } else {
535                 buffer.append(c);
536             }
537         }
538
539         private static String JavaDoc getEscaped(String JavaDoc s) {
540             StringBuffer JavaDoc result = new StringBuffer JavaDoc(s.length() + 10);
541             for (int i = 0; i < s.length(); ++i) {
542                 appendEscapedChar(result, s.charAt(i));
543             }
544             return result.toString();
545         }
546
547         private static String JavaDoc getReplacement(char c) {
548             // Encode special XML characters into the equivalent character references.
549
// The first five are defined by default for all XML documents.
550
// The next three (#xD, #xA, #x9) are encoded to avoid them
551
// being converted to spaces on deserialization
552
// (fixes bug 93720)
553
switch (c) {
554                 case '<' :
555                     return "lt"; //$NON-NLS-1$
556
case '>' :
557                     return "gt"; //$NON-NLS-1$
558
case '"' :
559                     return "quot"; //$NON-NLS-1$
560
case '\'' :
561                     return "apos"; //$NON-NLS-1$
562
case '&' :
563                     return "amp"; //$NON-NLS-1$
564
case '\r':
565                     return "#x0D"; //$NON-NLS-1$
566
case '\n':
567                     return "#x0A"; //$NON-NLS-1$
568
case '\u0009':
569                     return "#x09"; //$NON-NLS-1$
570
}
571             return null;
572         }
573     }
574 }
575
Popular Tags