diff options
Diffstat (limited to 'sax/tests/saxtests/src/android/sax/SafeSaxTest.java')
-rw-r--r-- | sax/tests/saxtests/src/android/sax/SafeSaxTest.java | 541 |
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; + } +} + |