• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
17 package com.android.internal.vibrator.persistence;
18 
19 import static com.android.internal.util.Preconditions.checkArgument;
20 import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE;
21 
22 import com.android.modules.utils.TypedXmlPullParser;
23 
24 import org.xmlpull.v1.XmlPullParser;
25 import org.xmlpull.v1.XmlPullParserException;
26 
27 import java.io.IOException;
28 
29 /**
30  * Helper methods for reading elements from a {@link XmlPullParser}.
31  *
32  * @hide
33  */
34 public final class XmlReader {
35 
36     /**
37      * Check parser is currently at {@link XmlPullParser#START_DOCUMENT} and that it has a start tag
38      * with expected root tag name.
39      *
40      * <p>The parser will be pointing to the root start tag found after this method.
41      */
readDocumentStartTag(TypedXmlPullParser parser, String expectedRootTag)42     public static void readDocumentStartTag(TypedXmlPullParser parser, String expectedRootTag)
43             throws XmlParserException, IOException {
44         readDocumentStart(parser);
45 
46         String tagName = parser.getName();
47         XmlValidator.checkParserCondition(expectedRootTag.equals(tagName),
48                 "Unexpected root tag found %s, expected %s", tagName, expectedRootTag);
49     }
50 
51     /**
52      * Check parser is currently at {@link XmlPullParser#START_DOCUMENT}.
53      *
54      * <p>The parser will be pointing to the first tag in the document.
55      */
readDocumentStart(TypedXmlPullParser parser)56     public static void readDocumentStart(TypedXmlPullParser parser)
57             throws XmlParserException, IOException {
58         try {
59             int type = parser.getEventType();
60             checkArgument(
61                     type == XmlPullParser.START_DOCUMENT,
62                     "Unexpected type, expected %d", type);
63             parser.nextTag(); // skips comments, instruction tokens and whitespace only
64         } catch (XmlPullParserException e) {
65             throw XmlParserException.createFromPullParserException("document start tag", e);
66         }
67     }
68 
69     /**
70      * Check parser is currently at {@link XmlPullParser#END_TAG} and that has the expected root tag
71      * name, and that the next tag is the {@link XmlPullParser#END_DOCUMENT} tag.
72      *
73      * <p>The parser will be pointing to the end document tag after this method.
74      */
readDocumentEndTag(TypedXmlPullParser parser)75     public static void readDocumentEndTag(TypedXmlPullParser parser)
76             throws XmlParserException, IOException {
77         try {
78             int type = parser.getEventType();
79             XmlValidator.checkParserCondition(type == XmlPullParser.END_TAG,
80                     "Unexpected element at document end, expected end of root tag");
81 
82             type = parser.next(); // skips comments and instruction tokens
83             if (type == XmlPullParser.TEXT && parser.isWhitespace()) { // skip whitespace only
84                 type = parser.next();
85             }
86 
87             XmlValidator.checkParserCondition(type == XmlPullParser.END_DOCUMENT,
88                     "Unexpected tag found %s, expected document end", parser.getName());
89         } catch (XmlPullParserException e) {
90             throw XmlParserException.createFromPullParserException("document end tag", e);
91         }
92     }
93 
94     /**
95      * Read the next tag and returns true if it's a {@link XmlPullParser#START_TAG} at depth
96      * {@code outerDepth + 1} or false if it's a {@link XmlPullParser#END_TAG} at
97      * {@code outerDepth}. Any other tag will fail this check.
98      *
99      * <p>The parser will be pointing to the next nested start tag when this method returns true,
100      * or to the end tag for given depth if it returns false.
101      *
102      * @return true if start tag found within given depth, false otherwise
103      */
readNextTagWithin(TypedXmlPullParser parser, int outerDepth)104     public static boolean readNextTagWithin(TypedXmlPullParser parser, int outerDepth)
105             throws XmlParserException, IOException {
106         int type;
107         try {
108             type = parser.getEventType();
109             if (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth) {
110                 // Already pointing to the end tag at outerDepth, just return before calling next.
111                 return false;
112             }
113 
114             type = parser.nextTag(); // skips comments, instruction tokens and whitespace only
115         } catch (XmlPullParserException e) {
116             throw XmlParserException.createFromPullParserException(parser.getName(), e);
117         }
118 
119         if (type == XmlPullParser.START_TAG && parser.getDepth() == outerDepth + 1) {
120             return true;
121         }
122 
123         // Next tag is not a start tag at outerDepth+1, expect it to be the end tag for outerDepth.
124         XmlValidator.checkParserCondition(
125                 type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth,
126                 "Unexpected tag found %s, expected end tag at depth %d",
127                 parser.getName(), outerDepth);
128 
129         return false;
130     }
131 
132     /**
133      * Check parser has a {@link XmlPullParser#END_TAG} as the next tag, with no nested tags.
134      *
135      * <p>The parser will be pointing to the end tag after this method.
136      */
readEndTag(TypedXmlPullParser parser)137     public static void readEndTag(TypedXmlPullParser parser)
138             throws XmlParserException, IOException {
139         readEndTag(parser, parser.getName(), parser.getDepth());
140     }
141 
142     /**
143      * Check parser has a {@link XmlPullParser#END_TAG} with same {@code tagDepth} as the next tag,
144      * with no more nested start tags.
145      *
146      * <p>The parser will be pointing to the end tag after this method.
147      */
readEndTag(TypedXmlPullParser parser, String tagName, int tagDepth)148     public static void readEndTag(TypedXmlPullParser parser, String tagName, int tagDepth)
149             throws XmlParserException, IOException {
150         // Read nested tag first, so we can use the parser.getName() in the error message.
151         boolean hasNestedTag = readNextTagWithin(parser, tagDepth);
152         XmlValidator.checkParserCondition(!hasNestedTag,
153                 "Unexpected nested tag %s found in tag %s", parser.getName(), tagName);
154     }
155 
156     /**
157      * Read attribute from current tag as a non-negative integer, returning default value if
158      * attribute is missing.
159      */
readAttributeIntNonNegative( TypedXmlPullParser parser, String attrName, int defaultValue)160     public static int readAttributeIntNonNegative(
161             TypedXmlPullParser parser, String attrName, int defaultValue)
162             throws XmlParserException {
163         if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
164             return defaultValue;
165         }
166         return readAttributeIntNonNegative(parser, attrName);
167     }
168 
169     /** Read attribute from current tag as a non-negative integer. */
readAttributeIntNonNegative(TypedXmlPullParser parser, String attrName)170     public static int readAttributeIntNonNegative(TypedXmlPullParser parser, String attrName)
171             throws XmlParserException {
172         String tagName = parser.getName();
173         int value = readAttributeInt(parser, attrName);
174 
175         XmlValidator.checkParserCondition(value >= 0,
176                 "Unexpected %s = %d in tag %s, expected %s >= 0",
177                 attrName, value, tagName, attrName);
178         return value;
179     }
180 
181     /** Read attribute from current tag as an integer within given inclusive range. */
readAttributeIntInRange( TypedXmlPullParser parser, String attrName, int lowerInclusive, int upperInclusive)182     public static int readAttributeIntInRange(
183             TypedXmlPullParser parser, String attrName, int lowerInclusive, int upperInclusive)
184             throws XmlParserException {
185         String tagName = parser.getName();
186         int value = readAttributeInt(parser, attrName);
187 
188         XmlValidator.checkParserCondition(
189                 value >= lowerInclusive && value <= upperInclusive,
190                 "Unexpected %s = %d in tag %s, expected %s in [%d, %d]",
191                 attrName, value, tagName, attrName, lowerInclusive, upperInclusive);
192         return value;
193     }
194 
195     /**
196      * Read attribute from current tag as a float within given inclusive range, returning default
197      * value if attribute is missing.
198      */
readAttributeFloatInRange( TypedXmlPullParser parser, String attrName, float lowerInclusive, float upperInclusive, float defaultValue)199     public static float readAttributeFloatInRange(
200             TypedXmlPullParser parser, String attrName, float lowerInclusive,
201             float upperInclusive, float defaultValue) throws XmlParserException {
202         if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
203             return defaultValue;
204         }
205         String tagName = parser.getName();
206         float value = readAttributeFloat(parser, attrName);
207 
208         XmlValidator.checkParserCondition(value >= lowerInclusive && value <= upperInclusive,
209                 "Unexpected %s = %f in tag %s, expected %s in [%f, %f]",
210                 attrName, value, tagName, attrName, lowerInclusive, upperInclusive);
211         return value;
212     }
213 
readAttributeInt(TypedXmlPullParser parser, String attrName)214     private static int readAttributeInt(TypedXmlPullParser parser, String attrName)
215             throws XmlParserException {
216         String tagName = parser.getName();
217         try {
218             return parser.getAttributeInt(NAMESPACE, attrName);
219         } catch (XmlPullParserException e) {
220             String rawValue = parser.getAttributeValue(NAMESPACE, attrName);
221             throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
222         }
223     }
224 
readAttributeFloat(TypedXmlPullParser parser, String attrName)225     private static float readAttributeFloat(TypedXmlPullParser parser, String attrName)
226             throws XmlParserException {
227         String tagName = parser.getName();
228         try {
229             return parser.getAttributeFloat(NAMESPACE, attrName);
230         } catch (XmlPullParserException e) {
231             String rawValue = parser.getAttributeValue(NAMESPACE, attrName);
232             throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
233         }
234     }
235 }
236