1 /* 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 17 package android.sax; 18 19 import org.xml.sax.Locator; 20 import org.xml.sax.SAXParseException; 21 22 import java.util.ArrayList; 23 24 /** 25 * An XML element. Provides access to child elements and hooks to listen 26 * for events related to this element. 27 * 28 * @see RootElement 29 */ 30 public class Element { 31 32 final String uri; 33 final String localName; 34 final int depth; 35 final Element parent; 36 37 Children children; 38 ArrayList<Element> requiredChilden; 39 40 boolean visited; 41 42 StartElementListener startElementListener; 43 EndElementListener endElementListener; 44 EndTextElementListener endTextElementListener; 45 Element(Element parent, String uri, String localName, int depth)46 Element(Element parent, String uri, String localName, int depth) { 47 this.parent = parent; 48 this.uri = uri; 49 this.localName = localName; 50 this.depth = depth; 51 } 52 53 /** 54 * Gets the child element with the given name. Uses an empty string as the 55 * namespace. 56 */ getChild(String localName)57 public Element getChild(String localName) { 58 return getChild("", localName); 59 } 60 61 /** 62 * Gets the child element with the given name. 63 */ getChild(String uri, String localName)64 public Element getChild(String uri, String localName) { 65 if (endTextElementListener != null) { 66 throw new IllegalStateException("This element already has an end" 67 + " text element listener. It cannot have children."); 68 } 69 70 if (children == null) { 71 children = new Children(); 72 } 73 74 return children.getOrCreate(this, uri, localName); 75 } 76 77 /** 78 * Gets the child element with the given name. Uses an empty string as the 79 * namespace. We will throw a {@link org.xml.sax.SAXException} at parsing 80 * time if the specified child is missing. This helps you ensure that your 81 * listeners are called. 82 */ requireChild(String localName)83 public Element requireChild(String localName) { 84 return requireChild("", localName); 85 } 86 87 /** 88 * Gets the child element with the given name. We will throw a 89 * {@link org.xml.sax.SAXException} at parsing time if the specified child 90 * is missing. This helps you ensure that your listeners are called. 91 */ requireChild(String uri, String localName)92 public Element requireChild(String uri, String localName) { 93 Element child = getChild(uri, localName); 94 95 if (requiredChilden == null) { 96 requiredChilden = new ArrayList<Element>(); 97 requiredChilden.add(child); 98 } else { 99 if (!requiredChilden.contains(child)) { 100 requiredChilden.add(child); 101 } 102 } 103 104 return child; 105 } 106 107 /** 108 * Sets start and end element listeners at the same time. 109 */ setElementListener(ElementListener elementListener)110 public void setElementListener(ElementListener elementListener) { 111 setStartElementListener(elementListener); 112 setEndElementListener(elementListener); 113 } 114 115 /** 116 * Sets start and end text element listeners at the same time. 117 */ setTextElementListener(TextElementListener elementListener)118 public void setTextElementListener(TextElementListener elementListener) { 119 setStartElementListener(elementListener); 120 setEndTextElementListener(elementListener); 121 } 122 123 /** 124 * Sets a listener for the start of this element. 125 */ setStartElementListener( StartElementListener startElementListener)126 public void setStartElementListener( 127 StartElementListener startElementListener) { 128 if (this.startElementListener != null) { 129 throw new IllegalStateException( 130 "Start element listener has already been set."); 131 } 132 this.startElementListener = startElementListener; 133 } 134 135 /** 136 * Sets a listener for the end of this element. 137 */ setEndElementListener(EndElementListener endElementListener)138 public void setEndElementListener(EndElementListener endElementListener) { 139 if (this.endElementListener != null) { 140 throw new IllegalStateException( 141 "End element listener has already been set."); 142 } 143 this.endElementListener = endElementListener; 144 } 145 146 /** 147 * Sets a listener for the end of this text element. 148 */ setEndTextElementListener( EndTextElementListener endTextElementListener)149 public void setEndTextElementListener( 150 EndTextElementListener endTextElementListener) { 151 if (this.endTextElementListener != null) { 152 throw new IllegalStateException( 153 "End text element listener has already been set."); 154 } 155 156 if (children != null) { 157 throw new IllegalStateException("This element already has children." 158 + " It cannot have an end text element listener."); 159 } 160 161 this.endTextElementListener = endTextElementListener; 162 } 163 164 @Override toString()165 public String toString() { 166 return toString(uri, localName); 167 } 168 toString(String uri, String localName)169 static String toString(String uri, String localName) { 170 return "'" + (uri.equals("") ? localName : uri + ":" + localName) + "'"; 171 } 172 173 /** 174 * Clears flags on required children. 175 */ resetRequiredChildren()176 void resetRequiredChildren() { 177 ArrayList<Element> requiredChildren = this.requiredChilden; 178 if (requiredChildren != null) { 179 for (int i = requiredChildren.size() - 1; i >= 0; i--) { 180 requiredChildren.get(i).visited = false; 181 } 182 } 183 } 184 185 /** 186 * Throws an exception if a required child was not present. 187 */ checkRequiredChildren(Locator locator)188 void checkRequiredChildren(Locator locator) throws SAXParseException { 189 ArrayList<Element> requiredChildren = this.requiredChilden; 190 if (requiredChildren != null) { 191 for (int i = requiredChildren.size() - 1; i >= 0; i--) { 192 Element child = requiredChildren.get(i); 193 if (!child.visited) { 194 throw new BadXmlException( 195 "Element named " + this + " is missing required" 196 + " child element named " 197 + child + ".", locator); 198 } 199 } 200 } 201 } 202 } 203