KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sapia > util > xml > idefix > XmlBuffer


1 package org.sapia.util.xml.idefix;
2
3
4 // Import of Sapia's utility classes
5
// ---------------------------------
6
import org.sapia.util.xml.Attribute;
7 import org.sapia.util.xml.CData;
8 import org.sapia.util.xml.Namespace;
9
10 // Import of Sun's JDK classes
11
// ---------------------------
12
import java.util.HashMap JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.LinkedList JavaDoc;
15 import java.util.Map JavaDoc;
16
17
18 /**
19  *
20  *
21  * @author Jean-Cedric Desrochers
22  * <dl>
23  * <dt><b>Copyright:</b><dd>Copyright &#169; 2002-2003 <a HREF="http://www.sapia-oss.org">Sapia Open Source Software</a>. All Rights Reserved.</dd></dt>
24  * <dt><b>License:</b><dd>Read the license.txt file of the jar or visit the
25  * <a HREF="http://www.sapia-oss.org/license.html">license page</a> at the Sapia OSS web site</dd></dt>
26  * </dl>
27  */

28 public class XmlBuffer {
29   /////////////////////////////////////////////////////////////////////////////////////////
30
////////////////////////////////// CLASS ATTRIBUTES ///////////////////////////////////
31
/////////////////////////////////////////////////////////////////////////////////////////
32

33   /** Defines the default character encoding. */
34   public static final String JavaDoc DEFAULT_CHARACTER_ENCODING = "UTF-8";
35
36   /////////////////////////////////////////////////////////////////////////////////////////
37
///////////////////////////////// INSTANCE ATTRIBUTES /////////////////////////////////
38
/////////////////////////////////////////////////////////////////////////////////////////
39

40   /** The XML scribe instance to generate the XML strings. */
41   private XmlScribe _theScribe;
42
43   /** The internal string buffer of this xml buffer. */
44   private StringBuffer JavaDoc _theBuffer;
45
46   /** Indicates if the buffer contains an xml declaration or not. */
47   private boolean _useXmlDeclaration;
48
49   /** The encoding of this xml buffer. */
50   private String JavaDoc _theCharacterEncoding;
51
52   /** The map of namespace definition by URI .*/
53   private Map JavaDoc _theNamespaceByURI;
54
55   /** The list of buffer states. */
56   private LinkedList JavaDoc _theStates;
57
58   /////////////////////////////////////////////////////////////////////////////////////////
59
//////////////////////////////////// CONSTRUCTORS /////////////////////////////////////
60
/////////////////////////////////////////////////////////////////////////////////////////
61

62   /**
63    * Creates a new XmlBuffer
64    */

65   public XmlBuffer() {
66     this(false);
67   }
68
69   /**
70    * Creates a new XmlBuffer
71    */

72   public XmlBuffer(boolean insertXmlDeclaration) {
73     this(DEFAULT_CHARACTER_ENCODING);
74     _useXmlDeclaration = insertXmlDeclaration;
75   }
76
77   /**
78    * Creates a new XmlBuffer.
79    */

80   public XmlBuffer(String JavaDoc aCharacterEncoding) {
81     _theScribe = new XmlScribe(aCharacterEncoding);
82     _theBuffer = new StringBuffer JavaDoc();
83     _useXmlDeclaration = true;
84     _theCharacterEncoding = aCharacterEncoding;
85     _theNamespaceByURI = new HashMap JavaDoc();
86     _theStates = new LinkedList JavaDoc();
87   }
88
89   /////////////////////////////////////////////////////////////////////////////////////////
90
////////////////////////////////// ACCESSOR METHODS ///////////////////////////////////
91
/////////////////////////////////////////////////////////////////////////////////////////
92

93   /**
94    * Returns the namespace prefix of the URI passed in.
95    *
96    * @param aNamespaceURI The URI of the namespace to look for.
97    * @return The associated prefix or null if the namespace URI is not found.
98    */

99   public String JavaDoc getNamespacePrefix(String JavaDoc aNamespaceURI) {
100     LinkedList JavaDoc someNamespaces = (LinkedList JavaDoc) _theNamespaceByURI.get(aNamespaceURI);
101
102     if (someNamespaces == null) {
103       return null;
104     } else {
105       NamespaceReference aNamespaceRef = (NamespaceReference) someNamespaces.getFirst();
106
107       return aNamespaceRef.getNamespace().getPrefix();
108     }
109   }
110
111   /**
112    * Returns true if this xml buffer is empty (no element has been started
113    * on this xml buffer).
114    *
115    * @return True if this xml buffer is empty.
116    */

117   public boolean isEmpty() {
118     return ((_theBuffer.length() == 0) && _theStates.isEmpty());
119   }
120
121   /////////////////////////////////////////////////////////////////////////////////////////
122
/////////////////////////////////// MUTATOR METHODS ///////////////////////////////////
123
/////////////////////////////////////////////////////////////////////////////////////////
124

125   /**
126    * Adds the namespace definition passed in to this xml buffer. The namespace
127    * will be use for all the future elements created by this xml buffer. To define the
128    * default namespace this method accepts either <CODE>null</CODE> or an empty
129    * string as the namespace prefix.
130    *
131    * @param aNamespaceURI The URI of the namespace to add.
132    * @param aNamespacePrefix The prefix associated to the namespace.
133    * @return This xml buffer instance.
134    * @exception IllegalArgumentException If the namespace URI passed in is null.
135    */

136   public XmlBuffer addNamespace(String JavaDoc aNamespaceURI, String JavaDoc aNamespacePrefix) {
137     // Validate arguments
138
if (aNamespaceURI == null) {
139       throw new IllegalArgumentException JavaDoc("The namespace URI passed in null");
140     }
141
142     // Get the list of namespaces for the URI
143
LinkedList JavaDoc someNamespaces = (LinkedList JavaDoc) _theNamespaceByURI.get(aNamespaceURI);
144
145     if (someNamespaces == null) {
146       someNamespaces = new LinkedList JavaDoc();
147       _theNamespaceByURI.put(aNamespaceURI, someNamespaces);
148     }
149
150     // Convert for default prefix if necessary
151
if (aNamespacePrefix == null) {
152       aNamespacePrefix = "";
153     }
154
155     Namespace aNamespace = new Namespace(aNamespaceURI, aNamespacePrefix);
156
157     // If the list of namespaces is empty add the current namespace
158
if (someNamespaces.isEmpty()) {
159       someNamespaces.addFirst(new NamespaceReference(aNamespace));
160     } else {
161       NamespaceReference aNamespaceRef = (NamespaceReference) someNamespaces.getFirst();
162
163       // If the current state is the same namespace, increment the reference counter
164
if (aNamespace.equals(aNamespaceRef.getNamespace())) {
165         aNamespaceRef.addReference();
166       } else {
167         someNamespaces.addFirst(new NamespaceReference(aNamespace));
168       }
169     }
170
171     return this;
172   }
173
174   /**
175    * Removes the namespace definition from this xml buffer.
176    *
177    * @param aNamespaceURI The URI of the namespace to remove.
178    * @return This xml buffer instance.
179    * @exception IllegalArgumentException If the namespace URI passed in is null or
180    * if the namespace URI is not found.
181    */

182   public XmlBuffer removeNamespace(String JavaDoc aNamespaceURI) {
183     // Validate the argument
184
if (aNamespaceURI == null) {
185       throw new IllegalArgumentException JavaDoc("The namespace URI passed in null");
186     }
187
188     // Retrieve the list of namespaces for the URI
189
LinkedList JavaDoc someNamespaces = (LinkedList JavaDoc) _theNamespaceByURI.get(aNamespaceURI);
190
191     if (someNamespaces == null) {
192       throw new IllegalStateException JavaDoc("No namespace found for the URI " +
193         aNamespaceURI);
194     }
195
196     // If there is one reference left remove the namespace, otherwise decrement the ref count
197
NamespaceReference aNamespaceRef = (NamespaceReference) someNamespaces.getFirst();
198
199     if (aNamespaceRef.getReferenceCount() == 1) {
200       someNamespaces.removeFirst();
201     } else {
202       aNamespaceRef.removeReference();
203     }
204
205     return this;
206   }
207
208   /**
209    * Start an element in the XML buffer. The element will be create for the default namespace.
210    *
211    * @anElementName The name of the element to start.
212    * @return This xml buffer instance.
213    * @exception IllegalArgumentException If the element name passed in is null.
214    */

215   public XmlBuffer startElement(String JavaDoc anElementName) {
216     return startElement(null, anElementName);
217   }
218
219   /**
220    * Start an element in the XML buffer using the namespace URI passed in.
221    *
222    * @param aNamespaceURI The namespace URI in which belong the element.
223    * @param anElementName The name of the element to create.
224    * @return This xml buffer instance.
225    * @exception IllegalArgumentException If the element name passed in is null.
226    */

227   public XmlBuffer startElement(String JavaDoc aNamespaceURI, String JavaDoc anElementName) {
228     if (anElementName == null) {
229       throw new IllegalArgumentException JavaDoc("The element name passed in is null");
230     }
231
232     BufferState aParentState = null;
233
234     if (!_theStates.isEmpty()) {
235       aParentState = (BufferState) _theStates.getFirst();
236     }
237
238     validateStartingXmlGeneration(aParentState);
239
240     String JavaDoc aPrefix = getNamespacePrefix(aNamespaceURI);
241     Namespace aNamespace = new Namespace(aNamespaceURI, aPrefix);
242     BufferState aState = new BufferState(aParentState, aNamespace,
243         anElementName);
244     aState.addDeclaredNamespace(aNamespace);
245     _theStates.addFirst(aState);
246
247     return this;
248   }
249
250   /**
251    * Adds the string passed in as content of the current XML element.
252    *
253    * @param aContent The content to add.
254    * @return This xml buffer instance.
255    * @exception IllegalStateException If there is no element started.
256    */

257   public XmlBuffer addContent(String JavaDoc aContent) {
258     if (_theStates.isEmpty()) {
259       throw new IllegalStateException JavaDoc("Could not add content [" + aContent +
260         "] there is no element started");
261     }
262
263     BufferState aState = (BufferState) _theStates.getFirst();
264     validateStartingXmlGeneration(aState);
265     _theScribe.xmlEncode(aContent, aState.getContent());
266
267     return this;
268   }
269
270   /**
271    * Adds the string passed in as CData of the current XML element.
272    *
273    * @param aContent The content to add as CData.
274    * @return This xml buffer instance.
275    * @exception IllegalStateException If there is no element started.
276    */

277   public XmlBuffer addContent(CData aCData) {
278     if (_theStates.isEmpty()) {
279       throw new IllegalStateException JavaDoc("Could not add CData [" + aCData +
280         "] there is no element started");
281     }
282
283     BufferState aState = (BufferState) _theStates.getFirst();
284     validateStartingXmlGeneration(aState);
285     _theScribe.composeCData(aCData.toString(), aState.getContent());
286
287     return this;
288   }
289
290   /**
291    * Ends the element name passed in. The element to close will be in the default namespace.
292    *
293    * @param anElementName The name of the element to end.
294    * @return This xml buffer instance.
295    * @exception IllegalArgumentException If the element name passed in is null or if it does not
296    * match the current element name and namespace.
297    * @exception IllegalStateException If there is no element started.
298    */

299   public XmlBuffer endElement(String JavaDoc anElementName) {
300     return endElement(null, anElementName);
301   }
302
303   /**
304    * Ends the element name passed in the provided namespace.
305    *
306    * @param aNamespaceURI The namespace of the element to close.
307    * @param anElementName The name of the element to end.
308    * @return This xml buffer instance.
309    * @exception IllegalArgumentException If the element name passed in is null or if it does not
310    * match the current element name and namespace.
311    * @exception IllegalStateException If there is no element started.
312    */

313   public XmlBuffer endElement(String JavaDoc aNamespaceURI, String JavaDoc anElementName) {
314     if (anElementName == null) {
315       throw new IllegalArgumentException JavaDoc("The element name passed in is null");
316     } else if (_theStates.isEmpty()) {
317       throw new IllegalStateException JavaDoc("Could not end the element [" +
318         anElementName + "] on an empty xml buffer");
319     }
320
321     BufferState aState = (BufferState) _theStates.removeFirst();
322
323     if (!anElementName.equals(aState.getElementName())) {
324       throw new IllegalArgumentException JavaDoc("The element name to end [" +
325         anElementName + "] does not match the starting tag [" +
326         aState.getElementName() + "]");
327     } else if (((aNamespaceURI != null) &&
328           !aNamespaceURI.equals(aState.getElementNamespace().getURI())) ||
329           ((aNamespaceURI == null) &&
330           (aState.getElementNamespace().getURI() != null))) {
331       throw new IllegalArgumentException JavaDoc("The namespace URI to end [" +
332         aNamespaceURI + "] does not match the starting namespace URI [" +
333         aState.getElementNamespace().getURI() + "]");
334     }
335
336     if (_theStates.isEmpty()) {
337       generateCompleteXmlFor(aState, _theBuffer);
338     } else {
339       BufferState aParentState = (BufferState) _theStates.getFirst();
340       generateCompleteXmlFor(aState, aParentState.getNestedXmlString());
341     }
342
343     return this;
344   }
345
346   /**
347    * Adds the attribute passed in to the current XML element.
348    *
349    * @param aName The name of the attribute to add.
350    * @param aValue The value of the attribute to add.
351    * @return This xml buffer instance.
352    * @exception IllegalStateException If there is no current element or if the method
353    * <CODE>endAttribute()</CODE> was previously called for the current element.
354    */

355   public XmlBuffer addAttribute(String JavaDoc aName, String JavaDoc aValue) {
356     return addAttribute(null, aName, aValue);
357   }
358
359   /**
360    * Adds the attribute passed in to the current XML element for the passed in namespace.
361    *
362    * @param aNamespaceURI The namespace of the attribute to add.
363    * @param aName The name of the attribute to add.
364    * @param aValue The value of the attribute to add.
365    * @return This xml buffer instance.
366    * @exception IllegalStateException If there is no current element or if the method
367    * <CODE>endAttribute()</CODE> was previously called for the current element.
368    */

369   public XmlBuffer addAttribute(String JavaDoc aNamespaceURI, String JavaDoc aName,
370     String JavaDoc aValue) {
371     if (_theStates.isEmpty()) {
372       throw new IllegalStateException JavaDoc("Could not add the attribute [" + aName +
373         "] on an empty xml buffer");
374     }
375
376     BufferState aState = (BufferState) _theStates.getFirst();
377
378     if (!aState.isGettingMoreAttribute()) {
379       throw new IllegalStateException JavaDoc("Could not add the attribute [" + aName +
380         "] on element for which the endAttribute() methos was previously called");
381     }
382
383     String JavaDoc aPrefix = getNamespacePrefix(aNamespaceURI);
384     Attribute anAttribute = new Attribute(aPrefix, aName, aValue);
385     aState.addAttribute(anAttribute);
386
387     Namespace aNamespace = new Namespace(aNamespaceURI, aPrefix);
388     aState.addDeclaredNamespace(aNamespace);
389
390     return this;
391   }
392
393   /**
394    * This method tells the xml buffer that the current element will not get any further
395    * attribute. It is a hint to let the xml buffer reduce it's footprint.
396    *
397    * @return This xml buffer instance.
398    * @exception IllegalStateException If there is no current element.
399    */

400   public XmlBuffer endAttribute() {
401     // hint that would tell the xml buffer that no more attributes will be added.
402
// upon this call it should generate the starting element of the current buffer state
403
if (_theStates.isEmpty()) {
404       throw new IllegalStateException JavaDoc("There is no current element");
405     }
406
407     BufferState aState = (BufferState) _theStates.getFirst();
408     aState.setIsGettingMoreAttribute(false);
409
410     return this;
411   }
412
413   /**
414    * Generates an XML string using the buffer state passed in.
415    *
416    * @param aState The buffer state that contains the info about the XML to generate.
417    * @param aBuffer The buffer into which to add the generated XML string.
418    */

419   private void generateCompleteXmlFor(BufferState aState, StringBuffer JavaDoc aBuffer) {
420     if (!aState.isStartElementGenerated()) {
421       generateStartingXmlFor(aState, aBuffer, true);
422
423       if (!aState.isElementEmpty()) {
424         if (!aState.isNestedXMLEmpty()) {
425           aBuffer.append(aState.getNestedXmlString().toString());
426         }
427
428         if (!aState.isContentEmpty()) {
429           aBuffer.append(aState.getContent().toString());
430         }
431
432         _theScribe.composeEndingElement(aState.getElementNamespace().getPrefix(),
433           aState.getElementName(), aBuffer);
434       }
435     } else if (!aState.isElementEmpty()) {
436       if (!aState.isContentEmpty()) {
437         aBuffer.append(aState.getContent().toString());
438       }
439
440       _theScribe.composeEndingElement(aState.getElementNamespace().getPrefix(),
441         aState.getElementName(), aBuffer);
442     }
443   }
444
445   /**
446    * Validates if it is necessary to generate a start element for the state passed in. If the
447    * conditions are meet, this method will call the <CODE>generateStartingXmlFor()</CODE> method
448    * to generate the starting element.
449    *
450    * @param aState The buffer state to validate.
451    */

452   private void validateStartingXmlGeneration(BufferState aState) {
453     if ((aState != null) && !aState.isGettingMoreAttribute() &&
454           !aState.isStartElementGenerated()) {
455       if (aState.getParent() == null) {
456         generateStartingXmlFor(aState, _theBuffer, false);
457         aState.setNestedXmlString(_theBuffer);
458       } else {
459         generateStartingXmlFor(aState, aState.getParent().getNestedXmlString(),
460           false);
461         aState.setNestedXmlString(aState.getParent().getNestedXmlString());
462       }
463     }
464   }
465
466   /**
467    * Generates an XML string using the buffer state passed in.
468    *
469    * @param aState The buffer state that contains the info about the XML to generate.
470    * @param aBuffer The buffer into which to add the generated XML string.
471    */

472   private void generateStartingXmlFor(BufferState aState, StringBuffer JavaDoc aBuffer,
473     boolean isClosingElement) {
474     int anIndex = 0;
475
476     for (Iterator JavaDoc it = aState.getDeclaredNamespaces().iterator(); it.hasNext();
477           anIndex++) {
478       Namespace aNamespace = (Namespace) it.next();
479
480       if ((aNamespace.getPrefix() == null) ||
481             (aNamespace.getPrefix().length() == 0)) {
482         Attribute anAttribute = new Attribute("xmlns", aNamespace.getURI());
483         aState.addAttribute(anIndex, anAttribute);
484       } else {
485         Attribute anAttribute = new Attribute("xmlns", aNamespace.getPrefix(),
486             aNamespace.getURI());
487         aState.addAttribute(anIndex, anAttribute);
488       }
489     }
490
491     if (isClosingElement && aState.isElementEmpty()) {
492       _theScribe.composeStartingElement(aState.getElementNamespace().getPrefix(),
493         aState.getElementName(), aState.getAttributes(), true, aBuffer);
494     } else {
495       _theScribe.composeStartingElement(aState.getElementNamespace().getPrefix(),
496         aState.getElementName(), aState.getAttributes(), false, aBuffer);
497     }
498
499     aState.setIsStartElementGenerated(true);
500   }
501
502   /////////////////////////////////////////////////////////////////////////////////////////
503
////////////////////////////////// OVERRIDEN METHODS //////////////////////////////////
504
/////////////////////////////////////////////////////////////////////////////////////////
505

506   /**
507    * Returns the string of this xml buffer.
508    *
509    * @return The string of this xml buffer.
510    */

511   public String JavaDoc toString() {
512     if (_useXmlDeclaration) {
513       StringBuffer JavaDoc aBuffer = new StringBuffer JavaDoc();
514       _theScribe.composeXmlDeclaration(_theCharacterEncoding, aBuffer);
515       aBuffer.append(_theBuffer.toString());
516
517       return aBuffer.toString();
518     } else {
519       return _theBuffer.toString();
520     }
521   }
522
523   public static class NamespaceReference {
524     private Namespace _theNamespace;
525     private int _theReferenceCount;
526
527     public NamespaceReference(Namespace aNamespace) {
528       _theNamespace = aNamespace;
529       _theReferenceCount = 1;
530     }
531
532     public int getReferenceCount() {
533       return _theReferenceCount;
534     }
535
536     public Namespace getNamespace() {
537       return _theNamespace;
538     }
539
540     public void addReference() {
541       _theReferenceCount++;
542     }
543
544     public void removeReference() {
545       _theReferenceCount--;
546     }
547   }
548 }
549
Popular Tags