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