001: /* StdXMLBuilder.java NanoXML/Java
002: *
003: * $Revision: 2056 $
004: * $Date: 2008-02-25 00:29:28 -0800 (Mon, 25 Feb 2008) $
005: * $Name$
006: *
007: * This file is part of NanoXML 2 for Java.
008: * Copyright (C) 2001 Marc De Scheemaecker, All Rights Reserved.
009: *
010: * This software is provided 'as-is', without any express or implied warranty.
011: * In no event will the authors be held liable for any damages arising from the
012: * use of this software.
013: *
014: * Permission is granted to anyone to use this software for any purpose,
015: * including commercial applications, and to alter it and redistribute it
016: * freely, subject to the following restrictions:
017: *
018: * 1. The origin of this software must not be misrepresented; you must not
019: * claim that you wrote the original software. If you use this software in
020: * a product, an acknowledgment in the product documentation would be
021: * appreciated but is not required.
022: *
023: * 2. Altered source versions must be plainly marked as such, and must not be
024: * misrepresented as being the original software.
025: *
026: * 3. This notice may not be removed or altered from any source distribution.
027: */
028:
029: package net.n3.nanoxml;
030:
031: import java.io.Reader;
032: import java.util.Stack;
033:
034: /**
035: * StdXMLBuilder is a concrete implementation of IXMLBuilder which creates a tree of XMLElement from
036: * an XML data source.
037: *
038: * @see net.n3.nanoxml.XMLElement
039: *
040: * @author Marc De Scheemaecker
041: * @version $Name$, $Revision: 2056 $
042: */
043: public class StdXMLBuilder implements IXMLBuilder {
044:
045: /**
046: * This stack contains the current element and its parents.
047: */
048: private Stack<XMLElement> stack;
049:
050: /**
051: * The root element of the parsed XML tree.
052: */
053: private XMLElement root;
054:
055: /**
056: * Creates the builder.
057: */
058: public StdXMLBuilder() {
059: this .stack = null;
060: this .root = null;
061: }
062:
063: /**
064: * Return the element that is currently being processed
065: *
066: * @return the element that is currently being processed
067: */
068: protected XMLElement getCurrentElement() {
069: return stack.peek();
070: }
071:
072: /**
073: * Return the stack used for processing the elements.
074: *
075: * @return the stack used for processing the elements.
076: */
077: protected Stack<XMLElement> getStack() {
078: return stack;
079: }
080:
081: /**
082: * Set the root element to a new element. This causes the internal stack
083: * to be flushed and the supplied element to be pushed onto it
084: *
085: * @param element the new root element.
086: */
087: protected void setRootElement(XMLElement element) {
088: stack.clear();
089: stack.push(element);
090: this .root = element;
091: }
092:
093: /**
094: * Cleans up the object when it's destroyed.
095: */
096: protected void finalize() throws Throwable {
097: this .root = null;
098: this .stack.clear();
099: this .stack = null;
100: super .finalize();
101: }
102:
103: /**
104: * This method is called before the parser starts processing its input.
105: *
106: * @param systemID the system ID of the XML data source
107: * @param lineNr the line on which the parsing starts
108: */
109: public void startBuilding(String systemID, int lineNr) {
110: this .stack = new Stack<XMLElement>();
111: this .root = null;
112: }
113:
114: /**
115: * This method is called when a processing instruction is encountered. PIs with target "xml" are
116: * handled by the parser.
117: *
118: * @param target the PI target
119: * @param reader to read the data from the PI
120: */
121: public void newProcessingInstruction(String target, Reader reader) {
122: // nothing to do
123: }
124:
125: /**
126: * This method is called when a new XML element is encountered.
127: *
128: * @see #endElement
129: *
130: * @param name the name of the element
131: * @param nsPrefix the prefix used to identify the namespace
132: * @param nsSystemID the system ID associated with the namespace
133: * @param systemID the system ID of the XML data source
134: * @param lineNr the line in the source where the element starts
135: */
136: public void startElement(String name, String nsPrefix,
137: String nsSystemID, String systemID, int lineNr) {
138: XMLElement elt = new XMLElement(name, systemID, lineNr);
139:
140: if (this .stack.empty()) {
141: this .root = elt;
142: } else {
143: XMLElement top = this .stack.peek();
144: top.addChild(elt);
145: }
146:
147: this .stack.push(elt);
148: }
149:
150: /**
151: * This method is called when the attributes of an XML element have been processed.
152: *
153: * @see #startElement
154: * @see #addAttribute
155: *
156: * @param name the name of the element
157: * @param nsPrefix the prefix used to identify the namespace
158: * @param nsSystemID the system ID associated with the namespace
159: */
160: public void elementAttributesProcessed(String name,
161: String nsPrefix, String nsSystemID) {
162: // nothing to do
163: }
164:
165: /**
166: * This method is called when the end of an XML elemnt is encountered.
167: *
168: * @see #startElement
169: *
170: * @param name the name of the element
171: * @param nsPrefix the prefix used to identify the namespace
172: * @param nsSystemID the system ID associated with the namespace
173: */
174: public void endElement(String name, String nsPrefix,
175: String nsSystemID) {
176: XMLElement elt = this .stack.pop();
177:
178: if (elt.getChildrenCount() == 1) {
179: XMLElement child = elt.getChildAtIndex(0);
180:
181: if (child.getName() == null) {
182: elt.setContent(child.getContent());
183: elt.removeChildAtIndex(0);
184: }
185: }
186: }
187:
188: /**
189: * This method is called when a new attribute of an XML element is encountered.
190: *
191: * @param key the key (name) of the attribute
192: * @param nsPrefix the prefix used to identify the namespace
193: * @param nsSystemID the system ID associated with the namespace
194: * @param value the value of the attribute
195: * @param type the type of the attribute ("CDATA" if unknown)
196: *
197: * @throws java.lang.Exception If an exception occurred while processing the event.
198: */
199: public void addAttribute(String key, String nsPrefix,
200: String nsSystemID, String value, String type)
201: throws Exception {
202: XMLElement top = this .stack.peek();
203:
204: if (top.hasAttribute(key)) {
205: throw new XMLParseException(top.getSystemID(), top
206: .getLineNr(), "Duplicate attribute: " + key);
207: }
208:
209: top.setAttribute(key, value);
210: }
211:
212: /**
213: * This method is called when a PCDATA element is encountered. A Java reader is supplied from
214: * which you can read the data. The reader will only read the data of the element. You don't
215: * need to check for boundaries. If you don't read the full element, the rest of the data is
216: * skipped. You also don't have to care about entities; they are resolved by the parser.
217: *
218: * @param reader the Java reader from which you can retrieve the data
219: * @param systemID the system ID of the XML data source
220: * @param lineNr the line in the source where the element starts
221: *
222: * @throws java.lang.Exception If an exception occurred while processing the event.
223: */
224: public void addPCData(Reader reader, String systemID, int lineNr)
225: throws Exception {
226: int bufSize = 2048;
227: int sizeRead = 0;
228: StringBuffer str = new StringBuffer(bufSize);
229: char[] buf = new char[bufSize];
230:
231: for (;;) {
232: if (sizeRead >= bufSize) {
233: bufSize *= 2;
234: str.ensureCapacity(bufSize);
235: }
236:
237: int size = reader.read(buf);
238:
239: if (size < 0) {
240: break;
241: }
242:
243: str.append(buf, 0, size);
244: sizeRead += size;
245: }
246:
247: XMLElement elt = new XMLElement(null, systemID, lineNr);
248: elt.setContent(str.toString());
249:
250: if (!this .stack.empty()) {
251: XMLElement top = this .stack.peek();
252: top.addChild(elt);
253: }
254: }
255:
256: /**
257: * Returns the result of the building process. This method is called just before the parse()
258: * method of IXMLParser returns.
259: *
260: * @see net.n3.nanoxml.IXMLParser#parse
261: *
262: * @return the result of the building process.
263: */
264: public Object getResult() {
265: return this.root;
266: }
267:
268: }
|