Automatic sources dropoff on 2020-06-10 18:32:38.095721

The change is generated with prebuilt drop tool.

Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/android/sax/BadXmlException.java b/android/sax/BadXmlException.java
new file mode 100644
index 0000000..dd324fd
--- /dev/null
+++ b/android/sax/BadXmlException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.SAXParseException;
+import org.xml.sax.Locator;
+
+/**
+ * An XML parse exception which includes the line number in the message.
+ */
+class BadXmlException extends SAXParseException {
+
+    public BadXmlException(String message, Locator locator) {
+        super(message, locator);
+    }
+
+    public String getMessage() {
+        return "Line " + getLineNumber() + ": " + super.getMessage();
+    }
+}
diff --git a/android/sax/Children.java b/android/sax/Children.java
new file mode 100644
index 0000000..7aa8dcf
--- /dev/null
+++ b/android/sax/Children.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+/**
+ * Contains element children. Using this class instead of HashMap results in
+ * measurably better performance.
+ */
+class Children {
+
+    Child[] children = new Child[16];
+
+    /**
+     * Looks up a child by name and creates a new one if necessary.
+     */
+    Element getOrCreate(Element parent, String uri, String localName) {
+        int hash = uri.hashCode() * 31 + localName.hashCode();
+        int index = hash & 15;
+
+        Child current = children[index];
+        if (current == null) {
+            // We have no children in this bucket yet.
+            current = new Child(parent, uri, localName, parent.depth + 1, hash);
+            children[index] = current;
+            return current;
+        } else {
+            // Search this bucket.
+            Child previous;
+            do {
+                if (current.hash == hash
+                        && current.uri.compareTo(uri) == 0
+                        && current.localName.compareTo(localName) == 0) {
+                    // We already have a child with that name.
+                    return current;
+                }
+
+                previous = current;
+                current = current.next;
+            } while (current != null);
+
+            // Add a new child to the bucket.
+            current = new Child(parent, uri, localName, parent.depth + 1, hash);
+            previous.next = current;
+            return current;         
+        }
+    }
+
+    /**
+     * Looks up a child by name.
+     */
+    Element get(String uri, String localName) {
+        int hash = uri.hashCode() * 31 + localName.hashCode();
+        int index = hash & 15;
+
+        Child current = children[index];
+        if (current == null) {
+            return null;
+        } else {
+            do {
+                if (current.hash == hash
+                        && current.uri.compareTo(uri) == 0
+                        && current.localName.compareTo(localName) == 0) {
+                    return current;
+                }
+                current = current.next;
+            } while (current != null);
+
+            return null;
+        }
+    }
+
+    static class Child extends Element {
+
+        final int hash;
+        Child next;
+
+        Child(Element parent, String uri, String localName, int depth,
+                int hash) {
+            super(parent, uri, localName, depth);
+            this.hash = hash;
+        }
+    }
+}
diff --git a/android/sax/Element.java b/android/sax/Element.java
new file mode 100644
index 0000000..c3e275f
--- /dev/null
+++ b/android/sax/Element.java
@@ -0,0 +1,202 @@
+/*
+ * 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);
+                }
+            }
+        }
+    }
+}
diff --git a/android/sax/ElementListener.java b/android/sax/ElementListener.java
new file mode 100644
index 0000000..5b3f5be
--- /dev/null
+++ b/android/sax/ElementListener.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+/**
+ * Listens for the beginning and ending of elements.
+ */
+public interface ElementListener extends StartElementListener,
+        EndElementListener {}
diff --git a/android/sax/EndElementListener.java b/android/sax/EndElementListener.java
new file mode 100644
index 0000000..b00a006
--- /dev/null
+++ b/android/sax/EndElementListener.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Listens for the end of elements.
+ */
+public interface EndElementListener {
+
+    /**
+     * Invoked at the end of an element.
+     */
+    void end();
+}
diff --git a/android/sax/EndTextElementListener.java b/android/sax/EndTextElementListener.java
new file mode 100644
index 0000000..6d87d0e
--- /dev/null
+++ b/android/sax/EndTextElementListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * Listens for the end of text elements.
+ */
+public interface EndTextElementListener {
+
+    /**
+     * Invoked at the end of a text element with the body of the element.
+     *
+     * @param body of the element
+     */
+    void end(String body);
+}
diff --git a/android/sax/RootElement.java b/android/sax/RootElement.java
new file mode 100644
index 0000000..4fd6f6b
--- /dev/null
+++ b/android/sax/RootElement.java
@@ -0,0 +1,207 @@
+/*
+ * 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.helpers.DefaultHandler;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+
+/**
+ * The root XML element. The entry point for this API. Not safe for concurrent
+ * use.
+ *
+ * <p>For example, passing this XML:
+ *
+ * <pre>
+ * &lt;feed xmlns='http://www.w3.org/2005/Atom'>
+ *   &lt;entry>
+ *     &lt;id>bob&lt;/id>
+ *   &lt;/entry>
+ * &lt;/feed>
+ * </pre>
+ *
+ * to this code:
+ *
+ * <pre>
+ * static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
+ *
+ * ...
+ * 
+ * RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
+ * Element entry = root.getChild(ATOM_NAMESPACE, "entry");
+ * entry.getChild(ATOM_NAMESPACE, "id").setEndTextElementListener(
+ *   new EndTextElementListener() {
+ *     public void end(String body) {
+ *       System.out.println("Entry ID: " + body);
+ *     }
+ *   });
+ *
+ * XMLReader reader = ...;
+ * reader.setContentHandler(root.getContentHandler());
+ * reader.parse(...);
+ * </pre>
+ *
+ * would output:
+ *
+ * <pre>
+ * Entry ID: bob
+ * </pre>
+ */
+public class RootElement extends Element {
+
+    final Handler handler = new Handler();
+
+    /**
+     * Constructs a new root element with the given name.
+     *
+     * @param uri the namespace
+     * @param localName the local name
+     */
+    public RootElement(String uri, String localName) {
+        super(null, uri, localName, 0);
+    }
+
+    /**
+     * Constructs a new root element with the given name. Uses an empty string
+     * as the namespace.
+     *
+     * @param localName the local name
+     */
+    public RootElement(String localName) {
+        this("", localName);
+    }
+
+    /**
+     * Gets the SAX {@code ContentHandler}. Pass this to your SAX parser.
+     */
+    public ContentHandler getContentHandler() {
+        return this.handler;
+    }
+
+    class Handler extends DefaultHandler {
+
+        Locator locator;
+        int depth = -1;
+        Element current = null;
+        StringBuilder bodyBuilder = null;
+
+        @Override
+        public void setDocumentLocator(Locator locator) {
+            this.locator = locator;
+        }
+
+        @Override
+        public void startElement(String uri, String localName, String qName,
+                Attributes attributes) throws SAXException {
+            int depth = ++this.depth;
+
+            if (depth == 0) {
+                // This is the root element.
+                startRoot(uri, localName, attributes);
+                return;
+            }
+
+            // Prohibit mixed text and elements.
+            if (bodyBuilder != null) {
+                throw new BadXmlException("Encountered mixed content"
+                        + " within text element named " + current + ".",
+                        locator);
+            }
+
+            // If we're one level below the current element.
+            if (depth == current.depth + 1) {
+                // Look for a child to push onto the stack.
+                Children children = current.children;
+                if (children != null) {
+                    Element child = children.get(uri, localName);
+                    if (child != null) {
+                        start(child, attributes);
+                    }
+                }
+            }
+        }
+
+        void startRoot(String uri, String localName, Attributes attributes)
+                throws SAXException {
+            Element root = RootElement.this;
+            if (root.uri.compareTo(uri) != 0
+                    || root.localName.compareTo(localName) != 0) {
+                throw new BadXmlException("Root element name does"
+                        + " not match. Expected: " + root + ", Got: "
+                        + Element.toString(uri, localName), locator);
+            }
+
+            start(root, attributes);
+        }
+
+        void start(Element e, Attributes attributes) {
+            // Push element onto the stack.
+            this.current = e;
+
+            if (e.startElementListener != null) {
+                e.startElementListener.start(attributes);
+            }
+
+            if (e.endTextElementListener != null) {
+                this.bodyBuilder = new StringBuilder();
+            }
+            
+            e.resetRequiredChildren();
+            e.visited = true;
+        }
+
+        @Override
+        public void characters(char[] buffer, int start, int length)
+                throws SAXException {
+            if (bodyBuilder != null) {
+                bodyBuilder.append(buffer, start, length);
+            }
+        }
+
+        @Override
+        public void endElement(String uri, String localName, String qName)
+                throws SAXException {
+            Element current = this.current;
+
+            // If we've ended the current element...
+            if (depth == current.depth) {
+                current.checkRequiredChildren(locator);
+
+                // Invoke end element listener.
+                if (current.endElementListener != null) {
+                    current.endElementListener.end();
+                }
+
+                // Invoke end text element listener.
+                if (bodyBuilder != null) {
+                    String body = bodyBuilder.toString();
+                    bodyBuilder = null;
+
+                    // We can assume that this listener is present.
+                    current.endTextElementListener.end(body);
+                }
+
+                // Pop element off the stack.
+                this.current = current.parent;
+            }
+
+            depth--;
+        }
+    }
+}
diff --git a/android/sax/StartElementListener.java b/android/sax/StartElementListener.java
new file mode 100644
index 0000000..d935fed
--- /dev/null
+++ b/android/sax/StartElementListener.java
@@ -0,0 +1,32 @@
+/*
+ * 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.Attributes;
+
+/**
+ * Listens for the beginning of elements.
+ */
+public interface StartElementListener {
+
+    /**
+     * Invoked at the beginning of an element.
+     *
+     * @param attributes from the element
+     */
+    void start(Attributes attributes);
+}
diff --git a/android/sax/TextElementListener.java b/android/sax/TextElementListener.java
new file mode 100644
index 0000000..6f3c31d
--- /dev/null
+++ b/android/sax/TextElementListener.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+/**
+ * Listens for the beginning and ending of text elements.
+ */
+public interface TextElementListener extends StartElementListener,
+        EndTextElementListener {}