001: /*
002: * Copyright (C) The DNA Group. All rights reserved.
003: *
004: * This software is published under the terms of the DNA
005: * Software License version 1.1, a copy of which has been included
006: * with this distribution in the LICENSE.txt file.
007: */
008: package org.codehaus.dna.impl;
009:
010: import java.util.ArrayList;
011: import java.util.List;
012:
013: import org.codehaus.dna.Configuration;
014: import org.xml.sax.Attributes;
015: import org.xml.sax.Locator;
016: import org.xml.sax.SAXException;
017: import org.xml.sax.SAXParseException;
018: import org.xml.sax.helpers.DefaultHandler;
019:
020: /**
021: * The SAXConfigurationHandler builds a Configuration tree
022: * from SAX events.
023: *
024: * @author Peter Donald
025: * @version $Revision: 1.2 $ $Date: 2004/05/01 09:51:48 $
026: */
027: public class SAXConfigurationHandler extends DefaultHandler {
028: /**
029: * Empty string used for padding out contents array.
030: */
031: private static final String EMPTY_STRING = "";
032:
033: /**
034: * Constant to indicate location of
035: * element when parser does not support Locator
036: * interface.
037: */
038: private static final String UNKNOWN = "";
039:
040: /**
041: * Stack of configuration elements currently being
042: * constructed.
043: */
044: private final List m_elements = new ArrayList();
045:
046: /**
047: * Stakc of content text for elements currently being
048: * constructed.
049: */
050: private final ArrayList m_values = new ArrayList();
051:
052: /**
053: * The configuration element created.
054: */
055: private Configuration m_configuration;
056:
057: /**
058: * The Locator specified by XML parser.
059: */
060: private Locator m_locator;
061:
062: /**
063: * Let the XML parser specify locator for when
064: * events arrive at handler.
065: *
066: * @param locator the locator
067: */
068: public void setDocumentLocator(final Locator locator) {
069: m_locator = locator;
070: }
071:
072: /**
073: * Reset internal state of handler in preapration for reuse.
074: */
075: public void clear() {
076: m_elements.clear();
077: m_values.clear();
078: m_locator = null;
079: }
080:
081: /**
082: * Return the configuration created by handler.
083: *
084: * @return the configuration created by handler.
085: */
086: public Configuration getConfiguration() {
087: return m_configuration;
088: }
089:
090: /**
091: * Start an element and thus a Configuration object.
092: *
093: * @param uri the uri (ignored)
094: * @param localName the localName (ignored)
095: * @param qName the qualified name (used for name of configuration)
096: * @param attributes the attributes of XML element
097: * @throws SAXException if unable to parse element
098: */
099: public void startElement(final String uri, final String localName,
100: final String qName, final Attributes attributes)
101: throws SAXException {
102: DefaultConfiguration parent = null;
103: String path = ConfigurationUtil.ROOT_PATH;
104: if (m_elements.size() > 0) {
105: final int index = m_elements.size() - 1;
106: parent = (DefaultConfiguration) m_elements.get(index);
107: path = ConfigurationUtil.generatePathName(parent.getPath(),
108: parent.getName());
109: }
110: final DefaultConfiguration configuration = new DefaultConfiguration(
111: qName, getLocationDescription(), path);
112: if (null != parent) {
113: parent.addChild(configuration);
114: }
115: final int length = attributes.getLength();
116: for (int i = 0; i < length; i++) {
117: final String key = attributes.getQName(i);
118: final String value = attributes.getValue(i);
119: final String newValue = processAttributeText(configuration,
120: key, value);
121: configuration.setAttribute(key, newValue);
122: }
123:
124: m_elements.add(configuration);
125: }
126:
127: /**
128: * End an element and thus a Configuration object.
129: * Will pop of configuration and value of object from
130: * stack. If the handler detects that element has both
131: * child elements and a text value then it will throw
132: * a SAXException.
133: *
134: * @param uri the uri (ignored)
135: * @param localName the localName (ignored)
136: * @param qName the qualified name (used for name of configuration)
137: * @throws SAXException if element had mixed content
138: */
139: public void endElement(final String uri, final String localName,
140: final String qName) throws SAXException {
141: final int index = m_elements.size() - 1;
142: final DefaultConfiguration configuration = (DefaultConfiguration) m_elements
143: .remove(index);
144: if (index < m_values.size()) {
145: final String value = m_values.remove(index).toString();
146: if (0 != value.trim().length()) {
147: if (0 == configuration.getChildren().length) {
148: final String newValue = processValueText(
149: configuration, value);
150: configuration.setValue(newValue);
151: } else {
152: final String message = "Mixed content ("
153: + value.trim() + ") " + "not supported @ "
154: + getLocationDescription();
155: throw new SAXException(message);
156: }
157: }
158: }
159: m_configuration = configuration;
160: }
161:
162: /**
163: * Receive text data for current element.
164: *
165: * @param ch the char array
166: * @param start the start index
167: * @param length the length of data
168: * @throws SAXException if unable ot parse data
169: */
170: public void characters(final char[] ch, final int start,
171: final int length) throws SAXException {
172: final int index = m_elements.size() - 1;
173: StringBuffer sb = null;
174: if (index < m_values.size()) {
175: sb = (StringBuffer) m_values.get(index);
176: }
177: if (null == sb) {
178: sb = new StringBuffer();
179: final int minCapacity = index + 1;
180: m_values.ensureCapacity(minCapacity);
181: final int size = m_values.size();
182: for (int i = size; i < minCapacity; i++) {
183: m_values.add(EMPTY_STRING);
184: }
185: m_values.set(index, sb);
186: }
187: sb.append(ch, start, length);
188: }
189:
190: /**
191: * Rethrow exception and dont attempt to do
192: * any error handling.
193: *
194: * @param spe the input exception
195: * @throws SAXException always thrown
196: */
197: public void warning(final SAXParseException spe)
198: throws SAXException {
199: throw spe;
200: }
201:
202: /**
203: * Rethrow exception and dont attempt to do
204: * any error handling.
205: *
206: * @param spe the input exception
207: * @throws SAXException always thrown
208: */
209: public void error(final SAXParseException spe) throws SAXException {
210: throw spe;
211: }
212:
213: /**
214: * Rethrow exception and dont attempt to do
215: * any error handling.
216: *
217: * @param spe the input exception
218: * @throws SAXException always thrown
219: */
220: public void fatalError(final SAXParseException spe)
221: throws SAXException {
222: throw spe;
223: }
224:
225: /**
226: * Utility method to derive current location of
227: * XML parser. Attempts to build up a string containing
228: * systemID:lineNumber:columnNumber such as
229: * "file.xml:20:3" if parser supports all fields.
230: *
231: * @return the location description
232: */
233: protected final String getLocationDescription() {
234: if (null == m_locator || null == m_locator.getSystemId()) {
235: return UNKNOWN;
236: } else if (-1 == m_locator.getLineNumber()) {
237: return m_locator.getSystemId();
238: } else if (-1 == m_locator.getColumnNumber()) {
239: return m_locator.getSystemId() + ":"
240: + m_locator.getLineNumber();
241: } else {
242: return m_locator.getSystemId() + ':'
243: + m_locator.getLineNumber() + ':'
244: + m_locator.getColumnNumber();
245: }
246: }
247:
248: /**
249: * Users may subclass this method to process attribute
250: * prior to it being set.
251: *
252: * @param configuration the associated configuration
253: * @param name the attribute name
254: * @param value the attribute value
255: * @return the attribute value
256: */
257: protected String processAttributeText(
258: final Configuration configuration, final String name,
259: final String value) {
260: return value;
261: }
262:
263: /**
264: * Users may subclass this method to process content
265: * prior to it being set.
266: *
267: * @param configuration the associated configuration
268: * @param value the value
269: * @return the value
270: */
271: protected String processValueText(
272: final Configuration configuration, final String value) {
273: return value;
274: }
275: }
|