content: /*
   * 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.Locator;
  import org.xml.sax.SAXParseException;
  
  import java.util.ArrayList;
  
  /**
   * An XML element. Provides access to child elements and hooks to listen
   * for events related to this element.
   *
   * @see RootElement
   */
  public class Element {
  
      final String uri;
      final String localName;
      final int depth;
      final Element parent;
      
      Children children;
      ArrayList<Element> requiredChilden;
  
      boolean visited;
  
      StartElementListener startElementListener;
      EndElementListener endElementListener;
      EndTextElementListener endTextElementListener;
  
      Element(Element parent, String uri, String localName, int depth) {
          this.parent = parent;
          this.uri = uri;
          this.localName = localName;
          this.depth = depth;
      }
  
      /**
       * Gets the child element with the given name. Uses an empty string as the
       * namespace.
       */
      public Element getChild(String localName) {
          return getChild("", localName);
      }
  
      /**
       * Gets the child element with the given name.
       */
      public Element getChild(String uri, String localName) {
          if (endTextElementListener != null) {
              throw new IllegalStateException("This element already has an end"
                      + " text element listener. It cannot have children.");
          }
  
          if (children == null) {
              children = new Children();
          }
  
          return children.getOrCreate(this, uri, localName);
      }
  
      /**
       * Gets the child element with the given name. Uses an empty string as the
       * namespace. We will throw a {@link org.xml.sax.SAXException} at parsing
       * time if the specified child is missing. This helps you ensure that your
       * listeners are called.
       */
      public Element requireChild(String localName) {
          return requireChild("", localName);
      }
  
      /**
       * Gets the child element with the given name. We will throw a
       * {@link org.xml.sax.SAXException} at parsing time if the specified child
       * is missing. This helps you ensure that your listeners are called.
       */
      public Element requireChild(String uri, String localName) {
          Element child = getChild(uri, localName);
  
          if (requiredChilden == null) {
              requiredChilden = new ArrayList<Element>();
              requiredChilden.add(child);
          } else {
              if (!requiredChilden.contains(child)) {
                  requiredChilden.add(child);
              }
          }
  
          return child;
      }
          
      /**
       * Sets start and end element listeners at the same time.
       */
      public void setElementListener(ElementListener elementListener) {
          setStartElementListener(elementListener);
          setEndElementListener(elementListener);
      }
  
      /**
       * Sets start and end text element listeners at the same time.
       */
      public void setTextElementListener(TextElementListener elementListener) {
          setStartElementListener(elementListener);
          setEndTextElementListener(elementListener);
      }
  
      /**
       * Sets a listener for the start of this element.
       */
      public void setStartElementListener(
              StartElementListener startElementListener) {
          if (this.startElementListener != null) {
              throw new IllegalStateException(
                      "Start element listener has already been set.");
          }
          this.startElementListener = startElementListener;
      }
  
      /**
       * Sets a listener for the end of this element.
       */
      public void setEndElementListener(EndElementListener endElementListener) {
          if (this.endElementListener != null) {
              throw new IllegalStateException(
                      "End element listener has already been set.");
          }
          this.endElementListener = endElementListener;
      }
  
      /**
       * Sets a listener for the end of this text element.
       */
      public void setEndTextElementListener(
              EndTextElementListener endTextElementListener) {
          if (this.endTextElementListener != null) {
              throw new IllegalStateException(
                      "End text element listener has already been set.");
          }
  
          if (children != null) {
              throw new IllegalStateException("This element already has children."
                      + " It cannot have an end text element listener.");
          }
  
          this.endTextElementListener = endTextElementListener;
      }
  
      @Override
      public String toString() {
          return toString(uri, localName);
      }
  
      static String toString(String uri, String localName) {
          return "'" + (uri.equals("") ? localName : uri + ":" + localName) + "'";
      }
  
      /**
       * Clears flags on required children.
       */
      void resetRequiredChildren() {
          ArrayList<Element> requiredChildren = this.requiredChilden;
          if (requiredChildren != null) {
              for (int i = requiredChildren.size() - 1; i >= 0; i--) {
                  requiredChildren.get(i).visited = false;
              }
          }
      }
  
      /**
       * Throws an exception if a required child was not present.
       */
      void checkRequiredChildren(Locator locator) throws SAXParseException {
          ArrayList<Element> requiredChildren = this.requiredChilden;
          if (requiredChildren != null) {
              for (int i = requiredChildren.size() - 1; i >= 0; i--) {
                  Element child = requiredChildren.get(i);
                  if (!child.visited) {
                      throw new BadXmlException(
                              "Element named " + this + " is missing required"
                                      + " child element named "
                                      + child + ".", locator);
                  }
              }
          }
      }
  }
  