Home | History | Annotate | Download | only in sax
      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 
     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      */
     57     public Element getChild(String localName) {
     58         return getChild("", localName);
     59     }
     60 
     61     /**
     62      * Gets the child element with the given name.
     63      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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
    165     public String toString() {
    166         return toString(uri, localName);
    167     }
    168 
    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      */
    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      */
    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