208 lines
6.0 KiB
Java
208 lines
6.0 KiB
Java
/*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.sax;
|
|
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.SAXException;
|
|
import org.xml.sax.ContentHandler;
|
|
import org.xml.sax.Locator;
|
|
|
|
/**
|
|
* The root XML element. The entry point for this API. Not safe for concurrent
|
|
* use.
|
|
*
|
|
* <p>For example, passing this XML:
|
|
*
|
|
* <pre>
|
|
* <feed xmlns='http://www.w3.org/2005/Atom'>
|
|
* <entry>
|
|
* <id>bob</id>
|
|
* </entry>
|
|
* </feed>
|
|
* </pre>
|
|
*
|
|
* to this code:
|
|
*
|
|
* <pre>
|
|
* static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
|
|
*
|
|
* ...
|
|
*
|
|
* RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
|
|
* Element entry = root.getChild(ATOM_NAMESPACE, "entry");
|
|
* entry.getChild(ATOM_NAMESPACE, "id").setEndTextElementListener(
|
|
* new EndTextElementListener() {
|
|
* public void end(String body) {
|
|
* System.out.println("Entry ID: " + body);
|
|
* }
|
|
* });
|
|
*
|
|
* XMLReader reader = ...;
|
|
* reader.setContentHandler(root.getContentHandler());
|
|
* reader.parse(...);
|
|
* </pre>
|
|
*
|
|
* would output:
|
|
*
|
|
* <pre>
|
|
* Entry ID: bob
|
|
* </pre>
|
|
*/
|
|
public class RootElement extends Element {
|
|
|
|
final Handler handler = new Handler();
|
|
|
|
/**
|
|
* Constructs a new root element with the given name.
|
|
*
|
|
* @param uri the namespace
|
|
* @param localName the local name
|
|
*/
|
|
public RootElement(String uri, String localName) {
|
|
super(null, uri, localName, 0);
|
|
}
|
|
|
|
/**
|
|
* Constructs a new root element with the given name. Uses an empty string
|
|
* as the namespace.
|
|
*
|
|
* @param localName the local name
|
|
*/
|
|
public RootElement(String localName) {
|
|
this("", localName);
|
|
}
|
|
|
|
/**
|
|
* Gets the SAX {@code ContentHandler}. Pass this to your SAX parser.
|
|
*/
|
|
public ContentHandler getContentHandler() {
|
|
return this.handler;
|
|
}
|
|
|
|
class Handler extends DefaultHandler {
|
|
|
|
Locator locator;
|
|
int depth = -1;
|
|
Element current = null;
|
|
StringBuilder bodyBuilder = null;
|
|
|
|
@Override
|
|
public void setDocumentLocator(Locator locator) {
|
|
this.locator = locator;
|
|
}
|
|
|
|
@Override
|
|
public void startElement(String uri, String localName, String qName,
|
|
Attributes attributes) throws SAXException {
|
|
int depth = ++this.depth;
|
|
|
|
if (depth == 0) {
|
|
// This is the root element.
|
|
startRoot(uri, localName, attributes);
|
|
return;
|
|
}
|
|
|
|
// Prohibit mixed text and elements.
|
|
if (bodyBuilder != null) {
|
|
throw new BadXmlException("Encountered mixed content"
|
|
+ " within text element named " + current + ".",
|
|
locator);
|
|
}
|
|
|
|
// If we're one level below the current element.
|
|
if (depth == current.depth + 1) {
|
|
// Look for a child to push onto the stack.
|
|
Children children = current.children;
|
|
if (children != null) {
|
|
Element child = children.get(uri, localName);
|
|
if (child != null) {
|
|
start(child, attributes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void startRoot(String uri, String localName, Attributes attributes)
|
|
throws SAXException {
|
|
Element root = RootElement.this;
|
|
if (root.uri.compareTo(uri) != 0
|
|
|| root.localName.compareTo(localName) != 0) {
|
|
throw new BadXmlException("Root element name does"
|
|
+ " not match. Expected: " + root + ", Got: "
|
|
+ Element.toString(uri, localName), locator);
|
|
}
|
|
|
|
start(root, attributes);
|
|
}
|
|
|
|
void start(Element e, Attributes attributes) {
|
|
// Push element onto the stack.
|
|
this.current = e;
|
|
|
|
if (e.startElementListener != null) {
|
|
e.startElementListener.start(attributes);
|
|
}
|
|
|
|
if (e.endTextElementListener != null) {
|
|
this.bodyBuilder = new StringBuilder();
|
|
}
|
|
|
|
e.resetRequiredChildren();
|
|
e.visited = true;
|
|
}
|
|
|
|
@Override
|
|
public void characters(char[] buffer, int start, int length)
|
|
throws SAXException {
|
|
if (bodyBuilder != null) {
|
|
bodyBuilder.append(buffer, start, length);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void endElement(String uri, String localName, String qName)
|
|
throws SAXException {
|
|
Element current = this.current;
|
|
|
|
// If we've ended the current element...
|
|
if (depth == current.depth) {
|
|
current.checkRequiredChildren(locator);
|
|
|
|
// Invoke end element listener.
|
|
if (current.endElementListener != null) {
|
|
current.endElementListener.end();
|
|
}
|
|
|
|
// Invoke end text element listener.
|
|
if (bodyBuilder != null) {
|
|
String body = bodyBuilder.toString();
|
|
bodyBuilder = null;
|
|
|
|
// We can assume that this listener is present.
|
|
current.endTextElementListener.end(body);
|
|
}
|
|
|
|
// Pop element off the stack.
|
|
this.current = current.parent;
|
|
}
|
|
|
|
depth--;
|
|
}
|
|
}
|
|
}
|