blob: 4fd6f6b1196b8bd03914fc683dd33b0c78730de3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.sax;
18
19import org.xml.sax.helpers.DefaultHandler;
20import org.xml.sax.Attributes;
21import org.xml.sax.SAXException;
22import org.xml.sax.ContentHandler;
23import org.xml.sax.Locator;
24
25/**
26 * The root XML element. The entry point for this API. Not safe for concurrent
27 * use.
28 *
29 * <p>For example, passing this XML:
30 *
31 * <pre>
32 * &lt;feed xmlns='http://www.w3.org/2005/Atom'>
33 * &lt;entry>
34 * &lt;id>bob&lt;/id>
35 * &lt;/entry>
36 * &lt;/feed>
37 * </pre>
38 *
39 * to this code:
40 *
41 * <pre>
42 * static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
43 *
44 * ...
45 *
46 * RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
47 * Element entry = root.getChild(ATOM_NAMESPACE, "entry");
48 * entry.getChild(ATOM_NAMESPACE, "id").setEndTextElementListener(
49 * new EndTextElementListener() {
50 * public void end(String body) {
51 * System.out.println("Entry ID: " + body);
52 * }
53 * });
54 *
55 * XMLReader reader = ...;
56 * reader.setContentHandler(root.getContentHandler());
57 * reader.parse(...);
58 * </pre>
59 *
60 * would output:
61 *
62 * <pre>
63 * Entry ID: bob
64 * </pre>
65 */
66public class RootElement extends Element {
67
68 final Handler handler = new Handler();
69
70 /**
71 * Constructs a new root element with the given name.
72 *
73 * @param uri the namespace
74 * @param localName the local name
75 */
76 public RootElement(String uri, String localName) {
77 super(null, uri, localName, 0);
78 }
79
80 /**
81 * Constructs a new root element with the given name. Uses an empty string
82 * as the namespace.
83 *
84 * @param localName the local name
85 */
86 public RootElement(String localName) {
87 this("", localName);
88 }
89
90 /**
91 * Gets the SAX {@code ContentHandler}. Pass this to your SAX parser.
92 */
93 public ContentHandler getContentHandler() {
94 return this.handler;
95 }
96
97 class Handler extends DefaultHandler {
98
99 Locator locator;
100 int depth = -1;
101 Element current = null;
102 StringBuilder bodyBuilder = null;
103
104 @Override
105 public void setDocumentLocator(Locator locator) {
106 this.locator = locator;
107 }
108
109 @Override
110 public void startElement(String uri, String localName, String qName,
111 Attributes attributes) throws SAXException {
112 int depth = ++this.depth;
113
114 if (depth == 0) {
115 // This is the root element.
116 startRoot(uri, localName, attributes);
117 return;
118 }
119
120 // Prohibit mixed text and elements.
121 if (bodyBuilder != null) {
122 throw new BadXmlException("Encountered mixed content"
123 + " within text element named " + current + ".",
124 locator);
125 }
126
127 // If we're one level below the current element.
128 if (depth == current.depth + 1) {
129 // Look for a child to push onto the stack.
130 Children children = current.children;
131 if (children != null) {
132 Element child = children.get(uri, localName);
133 if (child != null) {
134 start(child, attributes);
135 }
136 }
137 }
138 }
139
140 void startRoot(String uri, String localName, Attributes attributes)
141 throws SAXException {
142 Element root = RootElement.this;
143 if (root.uri.compareTo(uri) != 0
144 || root.localName.compareTo(localName) != 0) {
145 throw new BadXmlException("Root element name does"
146 + " not match. Expected: " + root + ", Got: "
147 + Element.toString(uri, localName), locator);
148 }
149
150 start(root, attributes);
151 }
152
153 void start(Element e, Attributes attributes) {
154 // Push element onto the stack.
155 this.current = e;
156
157 if (e.startElementListener != null) {
158 e.startElementListener.start(attributes);
159 }
160
161 if (e.endTextElementListener != null) {
162 this.bodyBuilder = new StringBuilder();
163 }
164
165 e.resetRequiredChildren();
166 e.visited = true;
167 }
168
169 @Override
170 public void characters(char[] buffer, int start, int length)
171 throws SAXException {
172 if (bodyBuilder != null) {
173 bodyBuilder.append(buffer, start, length);
174 }
175 }
176
177 @Override
178 public void endElement(String uri, String localName, String qName)
179 throws SAXException {
180 Element current = this.current;
181
182 // If we've ended the current element...
183 if (depth == current.depth) {
184 current.checkRequiredChildren(locator);
185
186 // Invoke end element listener.
187 if (current.endElementListener != null) {
188 current.endElementListener.end();
189 }
190
191 // Invoke end text element listener.
192 if (bodyBuilder != null) {
193 String body = bodyBuilder.toString();
194 bodyBuilder = null;
195
196 // We can assume that this listener is present.
197 current.endTextElementListener.end(body);
198 }
199
200 // Pop element off the stack.
201 this.current = current.parent;
202 }
203
204 depth--;
205 }
206 }
207}