aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaewan Kim <jaewan@google.com>2014-08-02 09:50:04 +0900
committerJaewan Kim <jaewan@google.com>2014-08-05 16:42:13 +0900
commitc26ffd6d4c714fd7af5cf14a154d0ed52bc0089e (patch)
treebd1087497d73efa3700c2d24d4488cd0b13d842c
parentbd61574966d9bda08f8f82bfee24155fa43a82e2 (diff)
downloadTV-c26ffd6d4c714fd7af5cf14a154d0ed52bc0089e.tar.gz
Initial commit of content rating XML parser
Bug: 16113341 Change-Id: I6194a499eae626df77300370df47de6079f36d5e
-rw-r--r--src/com/android/tv/parental/ContentRatingSystem.java428
-rw-r--r--src/com/android/tv/parental/ContentRatingsManager.java52
-rw-r--r--src/com/android/tv/parental/ContentRatingsParser.java333
3 files changed, 813 insertions, 0 deletions
diff --git a/src/com/android/tv/parental/ContentRatingSystem.java b/src/com/android/tv/parental/ContentRatingSystem.java
new file mode 100644
index 00000000..7985adf5
--- /dev/null
+++ b/src/com/android/tv/parental/ContentRatingSystem.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2014 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 com.android.tv.parental;
+
+import android.net.Uri;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContentRatingSystem {
+ private final static String TAG = "ContentRatingSystem";
+
+ // Id of this content rating system. It should be unique in an XML file.
+ private String mId;
+
+ // Domain of this content rating system. It's package name now.
+ private String mDomain;
+
+ // Display name of this content rating system. (e.g. TV-PG)
+ private String mDisplayName;
+
+ // Description of this content rating system.
+ private String mDescription;
+
+ // Country code of this content rating system.
+ private String mCountry;
+
+ // Ordered list of main content ratings. UX should respect the order.
+ private List<Rating> mRatings;
+
+ // Ordered list of sub content ratings. UX should respect the order.
+ private List<SubRating> mSubRatings;
+
+ // List of orders. This describes the automatic lock/unlock relationship between ratings.
+ // For example, let say we have following order.
+ // <order>
+ // <rating id="US_TVPG_Y" />
+ // <rating id="US_TVPG_Y7" />
+ // </order>
+ // This means that locking US_TVPG_Y7 automatically locks US_TVPG_Y and
+ // unlocking US_TVPG_Y automatically unlocks US_TVPG_Y7 from the UX.
+ // An user can still unlock US_TVPG_Y while US_TVPG_Y7 is locked by manually.
+ private List<Order> mOrders;
+
+ public String getId(){
+ return mId;
+ }
+
+ public String getDomain() {
+ return mDomain;
+ }
+
+ public String getDisplayName(){
+ return mDisplayName;
+ }
+
+ public String getDescription(){
+ return mDescription;
+ }
+
+ public String getCountry(){
+ return mCountry;
+ }
+
+ public List<Rating> getRatings(){
+ return mRatings;
+ }
+
+ public List<SubRating> getSubRatings(){
+ return mSubRatings;
+ }
+
+ public List<Order> getOrders(){
+ return mOrders;
+ }
+
+ private ContentRatingSystem(
+ String id, String domain, String displayName, String description, String country,
+ List<Rating> ratings, List<SubRating> subRatings, List<Order> orders) {
+ mId = id;
+ mDomain = domain;
+ mDisplayName = displayName;
+ mDescription = description;
+ mCountry = country;
+ mRatings = ratings;
+ mSubRatings = subRatings;
+ mOrders = orders;
+ }
+
+ public static class Builder {
+ private String mId;
+ private String mDomain;
+ private String mDisplayName;
+ private String mDescription;
+ private String mCountry;
+ private List<Rating.Builder> mRatingBuilders = new ArrayList<Rating.Builder>();
+ private List<SubRating.Builder> mSubRatingBuilders = new ArrayList<SubRating.Builder>();
+ private List<Order.Builder> mOrderBuilders = new ArrayList<Order.Builder>();
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public void setDomain(String domain) {
+ mDomain = domain;
+ }
+
+ public void setDisplayName(String displayName) {
+ mDisplayName = displayName;
+ }
+
+ public void setDescription(String description) {
+ mDescription = description;
+ }
+
+ public void setCountry(String country) {
+ mCountry = country;
+ }
+
+ public void addRatingBuilder(Rating.Builder ratingBuilder) {
+ // To provide easy access to the SubRatings in it,
+ // Rating has reference to SubRating, not Id of it.
+ // (Note that Rating/SubRating is ordered list so we cannot use Map)
+ // To do so, we need to have list of all SubRatings which might not be available
+ // at this moment. Keep builders here and build it with SubRatings later.
+ mRatingBuilders.add(ratingBuilder);
+ }
+
+ public void addSubRatingBuilder(SubRating.Builder subRatingBuilder) {
+ // SubRatings would be built rather to keep consistency with other fields.
+ mSubRatingBuilders.add(subRatingBuilder);
+ }
+
+ public void addOrderBuilder(Order.Builder orderBuilder) {
+ // To provide easy access to the Ratings in it,
+ // Order has reference to Rating, not Id of it.
+ // (Note that Rating/SubRating is ordered list so we cannot use Map)
+ // To do so, we need to have list of all Rating which might not be available
+ // at this moment. Keep builders here and build it with Ratings later.
+ mOrderBuilders.add(orderBuilder);
+ }
+
+ public ContentRatingSystem build() {
+ if (TextUtils.isEmpty(mId)) {
+ throw new IllegalArgumentException("Id cannot be empty");
+ }
+ if (TextUtils.isEmpty(mDomain)) {
+ throw new IllegalArgumentException("Domain cannot be empty");
+ }
+
+ List<SubRating> subRatings = new ArrayList<SubRating>();
+ if (mSubRatingBuilders != null) {
+ for (SubRating.Builder builder : mSubRatingBuilders) {
+ subRatings.add(builder.build());
+ }
+ }
+
+ if (mRatingBuilders.size() <= 0) {
+ throw new IllegalArgumentException("Rating isn't available.");
+ }
+ List<Rating> ratings = new ArrayList<Rating>();
+ if (mRatingBuilders != null) {
+ // Map string ID to object.
+ for (Rating.Builder builder : mRatingBuilders) {
+ ratings.add(builder.build(subRatings));
+ }
+
+ // Sanity check.
+ for (SubRating subRating : subRatings) {
+ boolean used = false;
+ for (Rating rating : ratings) {
+ if (rating.getSubRatings().contains(subRating)) {
+ used = true;
+ break;
+ }
+ }
+ if (!used) {
+ throw new IllegalArgumentException("Subrating " + subRating.getId() +
+ " isn't used by any rating");
+ }
+ }
+ }
+
+ List<Order> orders = new ArrayList<Order>();
+ if (mOrderBuilders != null) {
+ for (Order.Builder builder : mOrderBuilders) {
+ orders.add(builder.build(ratings));
+ }
+ }
+
+ return new ContentRatingSystem(mId, mDomain, mDisplayName, mDescription, mCountry,
+ ratings, subRatings, orders);
+ }
+ }
+
+ public static class Rating {
+ private String mId;
+ private String mDisplayName;
+ private String mDescription;
+ private Uri mIconUri;
+ private int mAgeHint;
+ private List<SubRating> mSubRatings;
+
+ public String getId() {
+ return mId;
+ }
+
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public Uri getIconUri() {
+ return mIconUri;
+ }
+
+ public int getAgeHint() {
+ return mAgeHint;
+ }
+
+ public List<SubRating> getSubRatings() {
+ return mSubRatings;
+ }
+
+ private Rating(String id, String displayName, String description, Uri iconUri,
+ int ageHint, List<SubRating> subRatings) {
+ mId = id;
+ mDisplayName = displayName;
+ mDescription = description;
+ mIconUri = iconUri;
+ mAgeHint = ageHint;
+ mSubRatings = subRatings;
+ }
+
+ public static class Builder {
+ private String mId;
+ private String mDisplayName;
+ private String mDescription;
+ private Uri mIconUri;
+ private Integer mAgeHint;
+ private List<String> mSubRatingIds = new ArrayList<String>();
+
+ public Builder() {
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public void setDisplayName(String displayName) {
+ mDisplayName = displayName;
+ }
+
+ public void setDescription(String description) {
+ mDescription = description;
+ }
+
+ public void setIconUri(Uri iconUri) {
+ mIconUri = iconUri;
+ }
+
+ public void setAgeHint(int ageHint) {
+ mAgeHint = (mAgeHint == null) ? new Integer(ageHint) : (Integer) ageHint;
+ }
+
+ public void addSubRatingId(String subRatingId) {
+ mSubRatingIds.add(subRatingId);
+ }
+
+ private Rating build(List<SubRating> allDefinedSubRatings) {
+ if (TextUtils.isEmpty(mId)) {
+ throw new IllegalArgumentException("A rating should have non-empty id");
+ }
+ if (allDefinedSubRatings == null && mSubRatingIds.size() > 0) {
+ throw new IllegalArgumentException("Invalid subrating for rating " +
+ mId);
+ }
+ if (mAgeHint == null || mAgeHint < 0) {
+ throw new IllegalArgumentException("Rating " + mId + " should define " +
+ "non-negative ageHint");
+ }
+
+ List<SubRating> subRatings = new ArrayList<SubRating>();
+ for (String subRatingId : mSubRatingIds) {
+ boolean found = false;
+ for (SubRating subRating : allDefinedSubRatings) {
+ if (subRatingId.equals(subRating.getId())) {
+ found = true;
+ subRatings.add(subRating);
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException("Unknown subrating Id " + subRatingId +
+ " in rating " + mId);
+ }
+ }
+ return new Rating(
+ mId, mDisplayName, mDescription, mIconUri, (int) mAgeHint, subRatings);
+ }
+ }
+ }
+
+ public static class SubRating {
+ private String mId;
+ private String mDisplayName;
+ private String mDescription;
+ private Uri mIconUri;
+
+ public String getId() {
+ return mId;
+ }
+
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public Uri getIconUri() {
+ return mIconUri;
+ }
+
+ private SubRating(String id, String displayName, String description, Uri iconUri) {
+ mId = id;
+ mDisplayName = displayName;
+ mDescription = description;
+ mIconUri = iconUri;
+ }
+
+ public static class Builder {
+ private String mId;
+ private String mDisplayName;
+ private String mDescription;
+ private Uri mIconUri;
+
+ public Builder() {
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public void setDisplayName(String displayName) {
+ mDisplayName = displayName;
+ }
+
+ public void setDescription(String description) {
+ mDescription = description;
+ }
+
+ public void setIconUri(Uri iconUri) {
+ mIconUri = iconUri;
+ }
+
+ private SubRating build() {
+ if (TextUtils.isEmpty(mId)) {
+ throw new IllegalArgumentException("A subrating should have non-empty id");
+ }
+ return new SubRating(mId, mDisplayName, mDescription, mIconUri);
+ }
+ }
+ }
+
+ public static class Order {
+ private List<Rating> mRatingOrder;
+
+ public List<Rating> getRatingOrder() {
+ return mRatingOrder;
+ }
+
+ private Order(List<Rating> ratingOrder) {
+ mRatingOrder = ratingOrder;
+ }
+
+ public static class Builder {
+ private final List<String> mRatingIds = new ArrayList<String>();
+
+ public Builder() {
+ }
+
+ private Order build(List<Rating> ratings) {
+ List<Rating> ratingOrder = new ArrayList<Rating>();
+ for (String ratingId : mRatingIds) {
+ boolean found = false;
+ for (Rating rating : ratings) {
+ if (ratingId.equals(rating.getId())) {
+ found = true;
+ ratingOrder.add(rating);
+ break;
+ }
+ }
+
+ if (!found) {
+ throw new IllegalArgumentException("Unknown rating " + ratingId +
+ " in order tag");
+ }
+ }
+
+ return new Order(ratingOrder);
+ }
+
+ public void addRatingId(String ratingId) {
+ mRatingIds.add(ratingId);
+ }
+ }
+ }
+}
diff --git a/src/com/android/tv/parental/ContentRatingsManager.java b/src/com/android/tv/parental/ContentRatingsManager.java
new file mode 100644
index 00000000..acc33e51
--- /dev/null
+++ b/src/com/android/tv/parental/ContentRatingsManager.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 com.android.tv.parental;
+
+import android.content.Context;
+import android.media.tv.TvInputManager;
+import android.net.Uri;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContentRatingsManager {
+ private final List<ContentRatingSystem> mContenRatings = new ArrayList<ContentRatingSystem>();
+
+ private Context mContext;
+
+ public ContentRatingsManager(Context context) {
+ mContext = context;
+ }
+
+ public void update() {
+ mContenRatings.clear();
+
+ TvInputManager manager =
+ (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE);
+ List<Uri> uris = manager.getTvContentRatingSystemXmls();
+ for (Uri uri : uris) {
+ List<ContentRatingSystem> list = ContentRatingsParser.parse(mContext, uri);
+ if (list != null) {
+ mContenRatings.addAll(list);
+ }
+ }
+ }
+
+ public List<ContentRatingSystem> getContentRatingSystems() {
+ return mContenRatings;
+ }
+}
diff --git a/src/com/android/tv/parental/ContentRatingsParser.java b/src/com/android/tv/parental/ContentRatingsParser.java
new file mode 100644
index 00000000..b999f938
--- /dev/null
+++ b/src/com/android/tv/parental/ContentRatingsParser.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2014 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 com.android.tv.parental;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.android.tv.parental.ContentRatingSystem.Rating;
+import com.android.tv.parental.ContentRatingSystem.SubRating;
+import com.android.tv.parental.ContentRatingSystem.Order;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+public class ContentRatingsParser {
+ private final static String TAG = "ContentRatingsParser";
+
+ private final static String TAG_RATING_SYSTEM_DEFINITIONS = "rating-system-definitions";
+ private final static String TAG_RATING_SYSTEM_DEFINITION = "rating-system-definition";
+ private final static String TAG_SUB_RATING_DEFINITION = "sub-rating-definition";
+ private final static String TAG_RATING_DEFINITION = "rating-definition";
+ private final static String TAG_SUB_RATING = "sub-rating";
+ private final static String TAG_ORDER = "order";
+ private final static String TAG_RATING = "rating";
+
+ private final static String ATTR_ID = "id";
+ private final static String ATTR_DISPLAY_NAME = "displayName";
+ private final static String ATTR_COUNTRY = "country";
+ private final static String ATTR_ICON = "icon";
+ private final static String ATTR_DESCRIPTION = "description";
+ private final static String ATTR_AGE_HINT = "ageHint";
+
+ private ContentRatingsParser() {
+ // Prevent instantiation.
+ }
+
+ public static List<ContentRatingSystem> parse(Context context, Uri uri) {
+ List<ContentRatingSystem> ratingSystems = null;
+ XmlResourceParser parser = null;
+ try {
+ if (!uri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE)) {
+ throw new IllegalArgumentException("Invalid URI scheme " + uri);
+ }
+ String packageName = uri.getAuthority();
+ int resId = (int) ContentUris.parseId(uri);
+ parser = context.getPackageManager().getXml(packageName, resId, null);
+ if (parser == null) {
+ throw new IllegalArgumentException("Cannot get XML with URI " + uri);
+ }
+ ratingSystems = parse(parser, packageName);
+ } catch (Exception e) {
+ // Catching all exceptions and print which URI is malformed XML with description
+ // and stack trace here.
+ // TODO: We may want to print message to stdout. see b/16803331
+ Log.w(TAG, "Error parsing XML " + uri, e);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+
+ return ratingSystems;
+ }
+
+ private static void assertEquals(int a, int b, String msg) throws XmlPullParserException {
+ if (a != b) {
+ throw new XmlPullParserException(msg);
+ }
+ }
+
+ private static void assertEquals(String a, String b, String msg) throws XmlPullParserException {
+ if (!b.equals(a)) {
+ throw new XmlPullParserException(msg);
+ }
+ }
+
+ private static List<ContentRatingSystem> parse(XmlResourceParser parser, String domain)
+ throws XmlPullParserException, IOException {
+ // Consume all START_DOCUMENT which can appear more than once.
+ while (parser.next() == XmlPullParser.START_DOCUMENT);
+
+ int eventType = parser.getEventType();
+ assertEquals(eventType, XmlPullParser.START_TAG, "Malformed XML: Not a valid XML file");
+ assertEquals(parser.getName(), TAG_RATING_SYSTEM_DEFINITIONS,
+ "Malformed XML: Should start with tag " + TAG_RATING_SYSTEM_DEFINITIONS);
+
+ List<ContentRatingSystem> ratingSystems = new ArrayList<ContentRatingSystem>();
+ while (true) {
+ eventType = parser.nextTag();
+
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ assertEquals(parser.getName(), TAG_RATING_SYSTEM_DEFINITION,
+ "Malformed XML: Should contains " +
+ TAG_RATING_SYSTEM_DEFINITION);
+ ratingSystems.add(parseRatingSystem(parser, domain));
+ break;
+ case XmlPullParser.END_TAG:
+ assertEquals(parser.getName(), TAG_RATING_SYSTEM_DEFINITIONS,
+ "Malformed XML: Should end with tag " +
+ TAG_RATING_SYSTEM_DEFINITIONS);
+ eventType = parser.next();
+ assertEquals(eventType, XmlPullParser.END_DOCUMENT,
+ "Malformed XML: Should end with tag " +
+ TAG_RATING_SYSTEM_DEFINITIONS);
+ return ratingSystems;
+ default:
+ throw new XmlPullParserException("Malformed XML: Error in " +
+ TAG_RATING_SYSTEM_DEFINITIONS);
+ }
+ }
+ }
+
+ private static ContentRatingSystem parseRatingSystem(XmlResourceParser parser, String domain)
+ throws XmlPullParserException, IOException {
+ ContentRatingSystem.Builder builder = new ContentRatingSystem.Builder();
+
+ builder.setDomain(domain);
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ String attr = parser.getAttributeName(i);
+ switch (attr) {
+ case ATTR_ID:
+ builder.setId(parser.getAttributeValue(i));
+ break;
+ case ATTR_COUNTRY:
+ builder.setCountry(parser.getAttributeValue(i));
+ break;
+ case ATTR_DISPLAY_NAME:
+ builder.setDisplayName(parser.getAttributeValue(i));
+ break;
+ case ATTR_DESCRIPTION:
+ builder.setDisplayName(parser.getAttributeValue(i));
+ break;
+ default:
+ throw new XmlPullParserException("Malformed XML: Unknown attribute " + attr +
+ " in " + TAG_RATING_SYSTEM_DEFINITIONS);
+ }
+ }
+
+ while (true) {
+ int eventType = parser.nextTag();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String tag = parser.getName();
+ switch (tag) {
+ case TAG_RATING_DEFINITION:
+ builder.addRatingBuilder(parseRating(parser));
+ break;
+ case TAG_SUB_RATING_DEFINITION:
+ builder.addSubRatingBuilder(parseSubRating(parser));
+ break;
+ case TAG_ORDER:
+ builder.addOrderBuilder(parseOrder(parser));
+ break;
+ default:
+ throw new XmlPullParserException("Malformed XML: Unknown tag " + tag +
+ " in " + TAG_RATING_SYSTEM_DEFINITION);
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ assertEquals(parser.getName(), TAG_RATING_SYSTEM_DEFINITION,
+ "Malformed XML: Tag mismatch for " + TAG_RATING_SYSTEM_DEFINITION);
+ return builder.build();
+ default:
+ throw new XmlPullParserException("Malformed XML: Tag is expected in " +
+ TAG_RATING_SYSTEM_DEFINITION);
+ }
+ }
+ }
+
+ private static Rating.Builder parseRating(XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Rating.Builder builder = new Rating.Builder();
+
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ String attr = parser.getAttributeName(i);
+ switch (attr) {
+ case ATTR_ID:
+ builder.setId(parser.getAttributeValue(i));
+ break;
+ case ATTR_DISPLAY_NAME:
+ builder.setDisplayName(parser.getAttributeValue(i));
+ break;
+ case ATTR_DESCRIPTION:
+ builder.setDisplayName(parser.getAttributeValue(i));
+ break;
+ case ATTR_ICON:
+ builder.setIconUri(Uri.parse(parser.getAttributeValue(i)));
+ break;
+ case ATTR_AGE_HINT:
+ int ageHint = -1;
+ try {
+ ageHint = Integer.parseInt(parser.getAttributeValue(i));
+ } catch (NumberFormatException e) {
+ }
+
+ if (ageHint < 0) {
+ throw new XmlPullParserException("Malformed XML: " + ATTR_AGE_HINT +
+ " should be a non-negative number");
+ }
+ builder.setAgeHint(ageHint);
+ break;
+ default:
+ throw new XmlPullParserException("Malformed XML: Unknown attribute " + attr +
+ " in " + TAG_RATING_DEFINITION);
+ }
+ }
+
+ while (true) {
+ int eventType = parser.nextTag();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ assertEquals(parser.getName(), TAG_SUB_RATING,
+ "Malformed XML: Only " + TAG_SUB_RATING + " is allowed in " +
+ TAG_RATING_DEFINITION);
+ if (parser.getAttributeCount() != 1 ||
+ !ATTR_ID.equals(parser.getAttributeName(0))) {
+ throw new XmlPullParserException("Malformed XML: " + TAG_SUB_RATING +
+ " should only contain " + ATTR_ID);
+ }
+ builder.addSubRatingId(parser.getAttributeValue(0));
+ eventType = parser.nextTag();
+ if (eventType != XmlPullParser.END_TAG ||
+ !TAG_SUB_RATING.equals(parser.getName())) {
+ throw new XmlPullParserException("Malformed XML: " + TAG_SUB_RATING +
+ " has child");
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ assertEquals(parser.getName(), TAG_RATING_DEFINITION,
+ "Malformed XML: Tag mismatch for " + TAG_RATING_DEFINITION);
+ return builder;
+
+ default:
+ throw new XmlPullParserException("Malformed XML: Error in " +
+ TAG_RATING_DEFINITION);
+ }
+ }
+ }
+
+ private static SubRating.Builder parseSubRating(XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ SubRating.Builder builder = new SubRating.Builder();
+
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ String attr = parser.getAttributeName(i);
+ switch (attr) {
+ case ATTR_ID:
+ builder.setId(parser.getAttributeValue(i));
+ break;
+ case ATTR_DISPLAY_NAME:
+ builder.setDisplayName(parser.getAttributeValue(i));
+ break;
+ case ATTR_DESCRIPTION:
+ builder.setDisplayName(parser.getAttributeValue(i));
+ break;
+ case ATTR_ICON:
+ builder.setIconUri(Uri.parse(parser.getAttributeValue(i)));
+ break;
+ default:
+ throw new XmlPullParserException("Malformed XML: Unknown attribute " + attr +
+ " in " + TAG_SUB_RATING_DEFINITION);
+ }
+ }
+
+ assertEquals(parser.nextTag(), XmlPullParser.END_TAG,
+ "Malformed XML: " + TAG_SUB_RATING_DEFINITION + " has child");
+ assertEquals(parser.getName(), TAG_SUB_RATING_DEFINITION,
+ "Malformed XML: " + TAG_SUB_RATING_DEFINITION + " isn't closed");
+
+ return builder;
+ }
+
+ private static Order.Builder parseOrder(XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Order.Builder builder = new Order.Builder();
+
+ assertEquals(parser.getAttributeCount(), 0,
+ "Malformed XML: Attribute isn't allowed in " + TAG_ORDER);
+
+ while (true) {
+ int eventType = parser.nextTag();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ assertEquals(parser.getName(), TAG_RATING,
+ "Malformed XML: Only " + TAG_RATING + " is allowed in " +
+ TAG_ORDER);
+ if (parser.getAttributeCount() != 1 ||
+ !ATTR_ID.equals(parser.getAttributeName(0))) {
+ throw new XmlPullParserException("Malformed XML: " + TAG_ORDER +
+ " should only contain " + ATTR_ID);
+ }
+ builder.addRatingId(parser.getAttributeValue(0));
+ eventType = parser.nextTag();
+ if (eventType != XmlPullParser.END_TAG ||
+ !TAG_RATING.equals(parser.getName())) {
+ throw new XmlPullParserException("Malformed XML: " + TAG_RATING +
+ " has child");
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ assertEquals(parser.getName(), TAG_ORDER,
+ "Malformed XML: Tag mismatch for " + TAG_ORDER);
+ return builder;
+ default:
+ throw new XmlPullParserException("Malformed XML: Error in " + TAG_ORDER);
+ }
+ }
+ }
+}