summaryrefslogtreecommitdiffstats
path: root/sax/tests/saxtests/src/android/sax/SafeSaxTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'sax/tests/saxtests/src/android/sax/SafeSaxTest.java')
-rw-r--r--sax/tests/saxtests/src/android/sax/SafeSaxTest.java541
1 files changed, 541 insertions, 0 deletions
diff --git a/sax/tests/saxtests/src/android/sax/SafeSaxTest.java b/sax/tests/saxtests/src/android/sax/SafeSaxTest.java
new file mode 100644
index 0000000..bee3938
--- /dev/null
+++ b/sax/tests/saxtests/src/android/sax/SafeSaxTest.java
@@ -0,0 +1,541 @@
+/*
+ * 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 android.graphics.Bitmap;
+import android.sax.Element;
+import android.sax.ElementListener;
+import android.sax.EndTextElementListener;
+import android.sax.RootElement;
+import android.sax.StartElementListener;
+import android.sax.TextElementListener;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.Xml;
+import com.android.common.XmlUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.android.frameworks.saxtests.R;
+
+public class SafeSaxTest extends AndroidTestCase {
+
+ private static final String TAG = SafeSaxTest.class.getName();
+
+ private static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
+ private static final String MEDIA_NAMESPACE = "http://search.yahoo.com/mrss/";
+ private static final String YOUTUBE_NAMESPACE = "http://gdata.youtube.com/schemas/2007";
+ private static final String GDATA_NAMESPACE = "http://schemas.google.com/g/2005";
+
+ private static class ElementCounter implements ElementListener {
+ int starts = 0;
+ int ends = 0;
+
+ public void start(Attributes attributes) {
+ starts++;
+ }
+
+ public void end() {
+ ends++;
+ }
+ }
+
+ private static class TextElementCounter implements TextElementListener {
+ int starts = 0;
+ String bodies = "";
+
+ public void start(Attributes attributes) {
+ starts++;
+ }
+
+ public void end(String body) {
+ this.bodies += body;
+ }
+ }
+
+ @SmallTest
+ public void testListener() throws Exception {
+ String xml = "<feed xmlns='http://www.w3.org/2005/Atom'>\n"
+ + "<entry>\n"
+ + "<id>a</id>\n"
+ + "</entry>\n"
+ + "<entry>\n"
+ + "<id>b</id>\n"
+ + "</entry>\n"
+ + "</feed>\n";
+
+ RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
+ Element entry = root.requireChild(ATOM_NAMESPACE, "entry");
+ Element id = entry.requireChild(ATOM_NAMESPACE, "id");
+
+ ElementCounter rootCounter = new ElementCounter();
+ ElementCounter entryCounter = new ElementCounter();
+ TextElementCounter idCounter = new TextElementCounter();
+
+ root.setElementListener(rootCounter);
+ entry.setElementListener(entryCounter);
+ id.setTextElementListener(idCounter);
+
+ Xml.parse(xml, root.getContentHandler());
+
+ assertEquals(1, rootCounter.starts);
+ assertEquals(1, rootCounter.ends);
+ assertEquals(2, entryCounter.starts);
+ assertEquals(2, entryCounter.ends);
+ assertEquals(2, idCounter.starts);
+ assertEquals("ab", idCounter.bodies);
+ }
+
+ @SmallTest
+ public void testMissingRequiredChild() throws Exception {
+ String xml = "<feed></feed>";
+ RootElement root = new RootElement("feed");
+ root.requireChild("entry");
+
+ try {
+ Xml.parse(xml, root.getContentHandler());
+ fail("expected exception not thrown");
+ } catch (SAXException e) {
+ // Expected.
+ }
+ }
+
+ @SmallTest
+ public void testMixedContent() throws Exception {
+ String xml = "<feed><entry></entry></feed>";
+
+ RootElement root = new RootElement("feed");
+ root.setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ }
+ });
+
+ try {
+ Xml.parse(xml, root.getContentHandler());
+ fail("expected exception not thrown");
+ } catch (SAXException e) {
+ // Expected.
+ }
+ }
+
+ @LargeTest
+ public void testPerformance() throws Exception {
+ InputStream in = mContext.getResources().openRawResource(R.raw.youtube);
+ byte[] xmlBytes;
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = in.read(buffer)) != -1) {
+ out.write(buffer, 0, length);
+ }
+ xmlBytes = out.toByteArray();
+ } finally {
+ in.close();
+ }
+
+ Log.i("***", "File size: " + (xmlBytes.length / 1024) + "k");
+
+ VideoAdapter videoAdapter = new VideoAdapter();
+ ContentHandler handler = newContentHandler(videoAdapter);
+ for (int i = 0; i < 2; i++) {
+ pureSaxTest(new ByteArrayInputStream(xmlBytes));
+ saxyModelTest(new ByteArrayInputStream(xmlBytes));
+ saxyModelTest(new ByteArrayInputStream(xmlBytes), handler);
+ }
+ }
+
+ private static void pureSaxTest(InputStream inputStream) throws IOException, SAXException {
+ long start = System.currentTimeMillis();
+ VideoAdapter videoAdapter = new VideoAdapter();
+ Xml.parse(inputStream, Xml.Encoding.UTF_8, new YouTubeContentHandler(videoAdapter));
+ long elapsed = System.currentTimeMillis() - start;
+ Log.i(TAG, "pure SAX: " + elapsed + "ms");
+ }
+
+ private static void saxyModelTest(InputStream inputStream) throws IOException, SAXException {
+ long start = System.currentTimeMillis();
+ VideoAdapter videoAdapter = new VideoAdapter();
+ Xml.parse(inputStream, Xml.Encoding.UTF_8, newContentHandler(videoAdapter));
+ long elapsed = System.currentTimeMillis() - start;
+ Log.i(TAG, "Saxy Model: " + elapsed + "ms");
+ }
+
+ private static void saxyModelTest(InputStream inputStream, ContentHandler contentHandler)
+ throws IOException, SAXException {
+ long start = System.currentTimeMillis();
+ Xml.parse(inputStream, Xml.Encoding.UTF_8, contentHandler);
+ long elapsed = System.currentTimeMillis() - start;
+ Log.i(TAG, "Saxy Model (preloaded): " + elapsed + "ms");
+ }
+
+ private static class VideoAdapter {
+ public void addVideo(YouTubeVideo video) {
+ }
+ }
+
+ private static ContentHandler newContentHandler(VideoAdapter videoAdapter) {
+ return new HandlerFactory().newContentHandler(videoAdapter);
+ }
+
+ private static class HandlerFactory {
+ YouTubeVideo video;
+
+ public ContentHandler newContentHandler(VideoAdapter videoAdapter) {
+ RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
+
+ final VideoListener videoListener = new VideoListener(videoAdapter);
+
+ Element entry = root.getChild(ATOM_NAMESPACE, "entry");
+
+ entry.setElementListener(videoListener);
+
+ entry.getChild(ATOM_NAMESPACE, "id")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ video.videoId = body;
+ }
+ });
+
+ entry.getChild(ATOM_NAMESPACE, "published")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ // TODO(tomtaylor): programmatically get the timezone
+ video.dateAdded = new Time(Time.TIMEZONE_UTC);
+ video.dateAdded.parse3339(body);
+ }
+ });
+
+ Element author = entry.getChild(ATOM_NAMESPACE, "author");
+ author.getChild(ATOM_NAMESPACE, "name")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ video.authorName = body;
+ }
+ });
+
+ Element mediaGroup = entry.getChild(MEDIA_NAMESPACE, "group");
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "thumbnail")
+ .setStartElementListener(new StartElementListener() {
+ public void start(Attributes attributes) {
+ String url = attributes.getValue("", "url");
+ if (video.thumbnailUrl == null && url.length() > 0) {
+ video.thumbnailUrl = url;
+ }
+ }
+ });
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "content")
+ .setStartElementListener(new StartElementListener() {
+ public void start(Attributes attributes) {
+ String url = attributes.getValue("", "url");
+ if (url != null) {
+ video.videoUrl = url;
+ }
+ }
+ });
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "player")
+ .setStartElementListener(new StartElementListener() {
+ public void start(Attributes attributes) {
+ String url = attributes.getValue("", "url");
+ if (url != null) {
+ video.playbackUrl = url;
+ }
+ }
+ });
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "title")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ video.title = body;
+ }
+ });
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "category")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ video.category = body;
+ }
+ });
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "description")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ video.description = body;
+ }
+ });
+
+ mediaGroup.getChild(MEDIA_NAMESPACE, "keywords")
+ .setEndTextElementListener(new EndTextElementListener() {
+ public void end(String body) {
+ video.tags = body;
+ }
+ });
+
+ mediaGroup.getChild(YOUTUBE_NAMESPACE, "duration")
+ .setStartElementListener(new StartElementListener() {
+ public void start(Attributes attributes) {
+ String seconds = attributes.getValue("", "seconds");
+ video.lengthInSeconds
+ = XmlUtils.convertValueToInt(seconds, 0);
+ }
+ });
+
+ mediaGroup.getChild(YOUTUBE_NAMESPACE, "statistics")
+ .setStartElementListener(new StartElementListener() {
+ public void start(Attributes attributes) {
+ String viewCount = attributes.getValue("", "viewCount");
+ video.viewCount
+ = XmlUtils.convertValueToInt(viewCount, 0);
+ }
+ });
+
+ entry.getChild(GDATA_NAMESPACE, "rating")
+ .setStartElementListener(new StartElementListener() {
+ public void start(Attributes attributes) {
+ String average = attributes.getValue("", "average");
+ video.rating = average == null
+ ? 0.0f : Float.parseFloat(average);
+ }
+ });
+
+ return root.getContentHandler();
+ }
+
+ class VideoListener implements ElementListener {
+
+ final VideoAdapter videoAdapter;
+
+ public VideoListener(VideoAdapter videoAdapter) {
+ this.videoAdapter = videoAdapter;
+ }
+
+ public void start(Attributes attributes) {
+ video = new YouTubeVideo();
+ }
+
+ public void end() {
+ videoAdapter.addVideo(video);
+ video = null;
+ }
+ }
+ }
+
+ private static class YouTubeContentHandler extends DefaultHandler {
+
+ final VideoAdapter videoAdapter;
+
+ YouTubeVideo video = null;
+ StringBuilder builder = null;
+
+ public YouTubeContentHandler(VideoAdapter videoAdapter) {
+ this.videoAdapter = videoAdapter;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) throws SAXException {
+ if (uri.equals(ATOM_NAMESPACE)) {
+ if (localName.equals("entry")) {
+ video = new YouTubeVideo();
+ return;
+ }
+
+ if (video == null) {
+ return;
+ }
+
+ if (!localName.equals("id")
+ && !localName.equals("published")
+ && !localName.equals("name")) {
+ return;
+ }
+ this.builder = new StringBuilder();
+ return;
+
+ }
+
+ if (video == null) {
+ return;
+ }
+
+ if (uri.equals(MEDIA_NAMESPACE)) {
+ if (localName.equals("thumbnail")) {
+ String url = attributes.getValue("", "url");
+ if (video.thumbnailUrl == null && url.length() > 0) {
+ video.thumbnailUrl = url;
+ }
+ return;
+ }
+
+ if (localName.equals("content")) {
+ String url = attributes.getValue("", "url");
+ if (url != null) {
+ video.videoUrl = url;
+ }
+ return;
+ }
+
+ if (localName.equals("player")) {
+ String url = attributes.getValue("", "url");
+ if (url != null) {
+ video.playbackUrl = url;
+ }
+ return;
+ }
+
+ if (localName.equals("title")
+ || localName.equals("category")
+ || localName.equals("description")
+ || localName.equals("keywords")) {
+ this.builder = new StringBuilder();
+ return;
+ }
+
+ return;
+ }
+
+ if (uri.equals(YOUTUBE_NAMESPACE)) {
+ if (localName.equals("duration")) {
+ video.lengthInSeconds = XmlUtils.convertValueToInt(
+ attributes.getValue("", "seconds"), 0);
+ return;
+ }
+
+ if (localName.equals("statistics")) {
+ video.viewCount = XmlUtils.convertValueToInt(
+ attributes.getValue("", "viewCount"), 0);
+ return;
+ }
+
+ return;
+ }
+
+ if (uri.equals(GDATA_NAMESPACE)) {
+ if (localName.equals("rating")) {
+ String average = attributes.getValue("", "average");
+ video.rating = average == null
+ ? 0.0f : Float.parseFloat(average);
+ }
+ }
+ }
+
+ @Override
+ public void characters(char text[], int start, int length)
+ throws SAXException {
+ if (builder != null) {
+ builder.append(text, start, length);
+ }
+ }
+
+ String takeText() {
+ try {
+ return builder.toString();
+ } finally {
+ builder = null;
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+ if (video == null) {
+ return;
+ }
+
+ if (uri.equals(ATOM_NAMESPACE)) {
+ if (localName.equals("published")) {
+ // TODO(tomtaylor): programmatically get the timezone
+ video.dateAdded = new Time(Time.TIMEZONE_UTC);
+ video.dateAdded.parse3339(takeText());
+ return;
+ }
+
+ if (localName.equals("name")) {
+ video.authorName = takeText();
+ return;
+ }
+
+ if (localName.equals("id")) {
+ video.videoId = takeText();
+ return;
+ }
+
+ if (localName.equals("entry")) {
+ // Add the video!
+ videoAdapter.addVideo(video);
+ video = null;
+ return;
+ }
+
+ return;
+ }
+
+ if (uri.equals(MEDIA_NAMESPACE)) {
+ if (localName.equals("description")) {
+ video.description = takeText();
+ return;
+ }
+
+ if (localName.equals("keywords")) {
+ video.tags = takeText();
+ return;
+ }
+
+ if (localName.equals("category")) {
+ video.category = takeText();
+ return;
+ }
+
+ if (localName.equals("title")) {
+ video.title = takeText();
+ }
+ }
+ }
+ }
+
+ private static class YouTubeVideo {
+ public String videoId; // the id used to lookup on YouTube
+ public String videoUrl; // the url to play the video
+ public String playbackUrl; // the url to share for users to play video
+ public String thumbnailUrl; // the url of the thumbnail image
+ public String title;
+ public Bitmap bitmap; // cached bitmap of the thumbnail
+ public int lengthInSeconds;
+ public int viewCount; // number of times the video has been viewed
+ public float rating; // ranges from 0.0 to 5.0
+ public Boolean triedToLoadThumbnail;
+ public String authorName;
+ public Time dateAdded;
+ public String category;
+ public String tags;
+ public String description;
+ }
+}
+