/sax/tests/saxtests/src/android/sax/SafeSaxTest.java

https://github.com/aizuzi/platform_frameworks_base · Java · 541 lines · 432 code · 89 blank · 20 comment · 55 complexity · 8f04bd0e2cc1a1c734d202a9a0f6b991 MD5 · raw file

  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. package android.sax;
  17. import android.graphics.Bitmap;
  18. import android.sax.Element;
  19. import android.sax.ElementListener;
  20. import android.sax.EndTextElementListener;
  21. import android.sax.RootElement;
  22. import android.sax.StartElementListener;
  23. import android.sax.TextElementListener;
  24. import android.test.AndroidTestCase;
  25. import android.test.suitebuilder.annotation.LargeTest;
  26. import android.test.suitebuilder.annotation.SmallTest;
  27. import android.text.format.Time;
  28. import android.util.Log;
  29. import android.util.Xml;
  30. import com.android.internal.util.XmlUtils;
  31. import org.xml.sax.Attributes;
  32. import org.xml.sax.ContentHandler;
  33. import org.xml.sax.SAXException;
  34. import org.xml.sax.helpers.DefaultHandler;
  35. import java.io.ByteArrayInputStream;
  36. import java.io.ByteArrayOutputStream;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39. import com.android.frameworks.saxtests.R;
  40. public class SafeSaxTest extends AndroidTestCase {
  41. private static final String TAG = SafeSaxTest.class.getName();
  42. private static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
  43. private static final String MEDIA_NAMESPACE = "http://search.yahoo.com/mrss/";
  44. private static final String YOUTUBE_NAMESPACE = "http://gdata.youtube.com/schemas/2007";
  45. private static final String GDATA_NAMESPACE = "http://schemas.google.com/g/2005";
  46. private static class ElementCounter implements ElementListener {
  47. int starts = 0;
  48. int ends = 0;
  49. public void start(Attributes attributes) {
  50. starts++;
  51. }
  52. public void end() {
  53. ends++;
  54. }
  55. }
  56. private static class TextElementCounter implements TextElementListener {
  57. int starts = 0;
  58. String bodies = "";
  59. public void start(Attributes attributes) {
  60. starts++;
  61. }
  62. public void end(String body) {
  63. this.bodies += body;
  64. }
  65. }
  66. @SmallTest
  67. public void testListener() throws Exception {
  68. String xml = "<feed xmlns='http://www.w3.org/2005/Atom'>\n"
  69. + "<entry>\n"
  70. + "<id>a</id>\n"
  71. + "</entry>\n"
  72. + "<entry>\n"
  73. + "<id>b</id>\n"
  74. + "</entry>\n"
  75. + "</feed>\n";
  76. RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
  77. Element entry = root.requireChild(ATOM_NAMESPACE, "entry");
  78. Element id = entry.requireChild(ATOM_NAMESPACE, "id");
  79. ElementCounter rootCounter = new ElementCounter();
  80. ElementCounter entryCounter = new ElementCounter();
  81. TextElementCounter idCounter = new TextElementCounter();
  82. root.setElementListener(rootCounter);
  83. entry.setElementListener(entryCounter);
  84. id.setTextElementListener(idCounter);
  85. Xml.parse(xml, root.getContentHandler());
  86. assertEquals(1, rootCounter.starts);
  87. assertEquals(1, rootCounter.ends);
  88. assertEquals(2, entryCounter.starts);
  89. assertEquals(2, entryCounter.ends);
  90. assertEquals(2, idCounter.starts);
  91. assertEquals("ab", idCounter.bodies);
  92. }
  93. @SmallTest
  94. public void testMissingRequiredChild() throws Exception {
  95. String xml = "<feed></feed>";
  96. RootElement root = new RootElement("feed");
  97. root.requireChild("entry");
  98. try {
  99. Xml.parse(xml, root.getContentHandler());
  100. fail("expected exception not thrown");
  101. } catch (SAXException e) {
  102. // Expected.
  103. }
  104. }
  105. @SmallTest
  106. public void testMixedContent() throws Exception {
  107. String xml = "<feed><entry></entry></feed>";
  108. RootElement root = new RootElement("feed");
  109. root.setEndTextElementListener(new EndTextElementListener() {
  110. public void end(String body) {
  111. }
  112. });
  113. try {
  114. Xml.parse(xml, root.getContentHandler());
  115. fail("expected exception not thrown");
  116. } catch (SAXException e) {
  117. // Expected.
  118. }
  119. }
  120. @LargeTest
  121. public void testPerformance() throws Exception {
  122. InputStream in = mContext.getResources().openRawResource(R.raw.youtube);
  123. byte[] xmlBytes;
  124. try {
  125. ByteArrayOutputStream out = new ByteArrayOutputStream();
  126. byte[] buffer = new byte[1024];
  127. int length;
  128. while ((length = in.read(buffer)) != -1) {
  129. out.write(buffer, 0, length);
  130. }
  131. xmlBytes = out.toByteArray();
  132. } finally {
  133. in.close();
  134. }
  135. Log.i("***", "File size: " + (xmlBytes.length / 1024) + "k");
  136. VideoAdapter videoAdapter = new VideoAdapter();
  137. ContentHandler handler = newContentHandler(videoAdapter);
  138. for (int i = 0; i < 2; i++) {
  139. pureSaxTest(new ByteArrayInputStream(xmlBytes));
  140. saxyModelTest(new ByteArrayInputStream(xmlBytes));
  141. saxyModelTest(new ByteArrayInputStream(xmlBytes), handler);
  142. }
  143. }
  144. private static void pureSaxTest(InputStream inputStream) throws IOException, SAXException {
  145. long start = System.currentTimeMillis();
  146. VideoAdapter videoAdapter = new VideoAdapter();
  147. Xml.parse(inputStream, Xml.Encoding.UTF_8, new YouTubeContentHandler(videoAdapter));
  148. long elapsed = System.currentTimeMillis() - start;
  149. Log.i(TAG, "pure SAX: " + elapsed + "ms");
  150. }
  151. private static void saxyModelTest(InputStream inputStream) throws IOException, SAXException {
  152. long start = System.currentTimeMillis();
  153. VideoAdapter videoAdapter = new VideoAdapter();
  154. Xml.parse(inputStream, Xml.Encoding.UTF_8, newContentHandler(videoAdapter));
  155. long elapsed = System.currentTimeMillis() - start;
  156. Log.i(TAG, "Saxy Model: " + elapsed + "ms");
  157. }
  158. private static void saxyModelTest(InputStream inputStream, ContentHandler contentHandler)
  159. throws IOException, SAXException {
  160. long start = System.currentTimeMillis();
  161. Xml.parse(inputStream, Xml.Encoding.UTF_8, contentHandler);
  162. long elapsed = System.currentTimeMillis() - start;
  163. Log.i(TAG, "Saxy Model (preloaded): " + elapsed + "ms");
  164. }
  165. private static class VideoAdapter {
  166. public void addVideo(YouTubeVideo video) {
  167. }
  168. }
  169. private static ContentHandler newContentHandler(VideoAdapter videoAdapter) {
  170. return new HandlerFactory().newContentHandler(videoAdapter);
  171. }
  172. private static class HandlerFactory {
  173. YouTubeVideo video;
  174. public ContentHandler newContentHandler(VideoAdapter videoAdapter) {
  175. RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
  176. final VideoListener videoListener = new VideoListener(videoAdapter);
  177. Element entry = root.getChild(ATOM_NAMESPACE, "entry");
  178. entry.setElementListener(videoListener);
  179. entry.getChild(ATOM_NAMESPACE, "id")
  180. .setEndTextElementListener(new EndTextElementListener() {
  181. public void end(String body) {
  182. video.videoId = body;
  183. }
  184. });
  185. entry.getChild(ATOM_NAMESPACE, "published")
  186. .setEndTextElementListener(new EndTextElementListener() {
  187. public void end(String body) {
  188. // TODO(tomtaylor): programmatically get the timezone
  189. video.dateAdded = new Time(Time.TIMEZONE_UTC);
  190. video.dateAdded.parse3339(body);
  191. }
  192. });
  193. Element author = entry.getChild(ATOM_NAMESPACE, "author");
  194. author.getChild(ATOM_NAMESPACE, "name")
  195. .setEndTextElementListener(new EndTextElementListener() {
  196. public void end(String body) {
  197. video.authorName = body;
  198. }
  199. });
  200. Element mediaGroup = entry.getChild(MEDIA_NAMESPACE, "group");
  201. mediaGroup.getChild(MEDIA_NAMESPACE, "thumbnail")
  202. .setStartElementListener(new StartElementListener() {
  203. public void start(Attributes attributes) {
  204. String url = attributes.getValue("", "url");
  205. if (video.thumbnailUrl == null && url.length() > 0) {
  206. video.thumbnailUrl = url;
  207. }
  208. }
  209. });
  210. mediaGroup.getChild(MEDIA_NAMESPACE, "content")
  211. .setStartElementListener(new StartElementListener() {
  212. public void start(Attributes attributes) {
  213. String url = attributes.getValue("", "url");
  214. if (url != null) {
  215. video.videoUrl = url;
  216. }
  217. }
  218. });
  219. mediaGroup.getChild(MEDIA_NAMESPACE, "player")
  220. .setStartElementListener(new StartElementListener() {
  221. public void start(Attributes attributes) {
  222. String url = attributes.getValue("", "url");
  223. if (url != null) {
  224. video.playbackUrl = url;
  225. }
  226. }
  227. });
  228. mediaGroup.getChild(MEDIA_NAMESPACE, "title")
  229. .setEndTextElementListener(new EndTextElementListener() {
  230. public void end(String body) {
  231. video.title = body;
  232. }
  233. });
  234. mediaGroup.getChild(MEDIA_NAMESPACE, "category")
  235. .setEndTextElementListener(new EndTextElementListener() {
  236. public void end(String body) {
  237. video.category = body;
  238. }
  239. });
  240. mediaGroup.getChild(MEDIA_NAMESPACE, "description")
  241. .setEndTextElementListener(new EndTextElementListener() {
  242. public void end(String body) {
  243. video.description = body;
  244. }
  245. });
  246. mediaGroup.getChild(MEDIA_NAMESPACE, "keywords")
  247. .setEndTextElementListener(new EndTextElementListener() {
  248. public void end(String body) {
  249. video.tags = body;
  250. }
  251. });
  252. mediaGroup.getChild(YOUTUBE_NAMESPACE, "duration")
  253. .setStartElementListener(new StartElementListener() {
  254. public void start(Attributes attributes) {
  255. String seconds = attributes.getValue("", "seconds");
  256. video.lengthInSeconds
  257. = XmlUtils.convertValueToInt(seconds, 0);
  258. }
  259. });
  260. mediaGroup.getChild(YOUTUBE_NAMESPACE, "statistics")
  261. .setStartElementListener(new StartElementListener() {
  262. public void start(Attributes attributes) {
  263. String viewCount = attributes.getValue("", "viewCount");
  264. video.viewCount
  265. = XmlUtils.convertValueToInt(viewCount, 0);
  266. }
  267. });
  268. entry.getChild(GDATA_NAMESPACE, "rating")
  269. .setStartElementListener(new StartElementListener() {
  270. public void start(Attributes attributes) {
  271. String average = attributes.getValue("", "average");
  272. video.rating = average == null
  273. ? 0.0f : Float.parseFloat(average);
  274. }
  275. });
  276. return root.getContentHandler();
  277. }
  278. class VideoListener implements ElementListener {
  279. final VideoAdapter videoAdapter;
  280. public VideoListener(VideoAdapter videoAdapter) {
  281. this.videoAdapter = videoAdapter;
  282. }
  283. public void start(Attributes attributes) {
  284. video = new YouTubeVideo();
  285. }
  286. public void end() {
  287. videoAdapter.addVideo(video);
  288. video = null;
  289. }
  290. }
  291. }
  292. private static class YouTubeContentHandler extends DefaultHandler {
  293. final VideoAdapter videoAdapter;
  294. YouTubeVideo video = null;
  295. StringBuilder builder = null;
  296. public YouTubeContentHandler(VideoAdapter videoAdapter) {
  297. this.videoAdapter = videoAdapter;
  298. }
  299. @Override
  300. public void startElement(String uri, String localName, String qName,
  301. Attributes attributes) throws SAXException {
  302. if (uri.equals(ATOM_NAMESPACE)) {
  303. if (localName.equals("entry")) {
  304. video = new YouTubeVideo();
  305. return;
  306. }
  307. if (video == null) {
  308. return;
  309. }
  310. if (!localName.equals("id")
  311. && !localName.equals("published")
  312. && !localName.equals("name")) {
  313. return;
  314. }
  315. this.builder = new StringBuilder();
  316. return;
  317. }
  318. if (video == null) {
  319. return;
  320. }
  321. if (uri.equals(MEDIA_NAMESPACE)) {
  322. if (localName.equals("thumbnail")) {
  323. String url = attributes.getValue("", "url");
  324. if (video.thumbnailUrl == null && url.length() > 0) {
  325. video.thumbnailUrl = url;
  326. }
  327. return;
  328. }
  329. if (localName.equals("content")) {
  330. String url = attributes.getValue("", "url");
  331. if (url != null) {
  332. video.videoUrl = url;
  333. }
  334. return;
  335. }
  336. if (localName.equals("player")) {
  337. String url = attributes.getValue("", "url");
  338. if (url != null) {
  339. video.playbackUrl = url;
  340. }
  341. return;
  342. }
  343. if (localName.equals("title")
  344. || localName.equals("category")
  345. || localName.equals("description")
  346. || localName.equals("keywords")) {
  347. this.builder = new StringBuilder();
  348. return;
  349. }
  350. return;
  351. }
  352. if (uri.equals(YOUTUBE_NAMESPACE)) {
  353. if (localName.equals("duration")) {
  354. video.lengthInSeconds = XmlUtils.convertValueToInt(
  355. attributes.getValue("", "seconds"), 0);
  356. return;
  357. }
  358. if (localName.equals("statistics")) {
  359. video.viewCount = XmlUtils.convertValueToInt(
  360. attributes.getValue("", "viewCount"), 0);
  361. return;
  362. }
  363. return;
  364. }
  365. if (uri.equals(GDATA_NAMESPACE)) {
  366. if (localName.equals("rating")) {
  367. String average = attributes.getValue("", "average");
  368. video.rating = average == null
  369. ? 0.0f : Float.parseFloat(average);
  370. }
  371. }
  372. }
  373. @Override
  374. public void characters(char text[], int start, int length)
  375. throws SAXException {
  376. if (builder != null) {
  377. builder.append(text, start, length);
  378. }
  379. }
  380. String takeText() {
  381. try {
  382. return builder.toString();
  383. } finally {
  384. builder = null;
  385. }
  386. }
  387. @Override
  388. public void endElement(String uri, String localName, String qName)
  389. throws SAXException {
  390. if (video == null) {
  391. return;
  392. }
  393. if (uri.equals(ATOM_NAMESPACE)) {
  394. if (localName.equals("published")) {
  395. // TODO(tomtaylor): programmatically get the timezone
  396. video.dateAdded = new Time(Time.TIMEZONE_UTC);
  397. video.dateAdded.parse3339(takeText());
  398. return;
  399. }
  400. if (localName.equals("name")) {
  401. video.authorName = takeText();
  402. return;
  403. }
  404. if (localName.equals("id")) {
  405. video.videoId = takeText();
  406. return;
  407. }
  408. if (localName.equals("entry")) {
  409. // Add the video!
  410. videoAdapter.addVideo(video);
  411. video = null;
  412. return;
  413. }
  414. return;
  415. }
  416. if (uri.equals(MEDIA_NAMESPACE)) {
  417. if (localName.equals("description")) {
  418. video.description = takeText();
  419. return;
  420. }
  421. if (localName.equals("keywords")) {
  422. video.tags = takeText();
  423. return;
  424. }
  425. if (localName.equals("category")) {
  426. video.category = takeText();
  427. return;
  428. }
  429. if (localName.equals("title")) {
  430. video.title = takeText();
  431. }
  432. }
  433. }
  434. }
  435. private static class YouTubeVideo {
  436. public String videoId; // the id used to lookup on YouTube
  437. public String videoUrl; // the url to play the video
  438. public String playbackUrl; // the url to share for users to play video
  439. public String thumbnailUrl; // the url of the thumbnail image
  440. public String title;
  441. public Bitmap bitmap; // cached bitmap of the thumbnail
  442. public int lengthInSeconds;
  443. public int viewCount; // number of times the video has been viewed
  444. public float rating; // ranges from 0.0 to 5.0
  445. public Boolean triedToLoadThumbnail;
  446. public String authorName;
  447. public Time dateAdded;
  448. public String category;
  449. public String tags;
  450. public String description;
  451. }
  452. }