• 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      * Read the next element, ignoring comments and ignorable whitespace, and returns only if it's a
134      * {@link XmlPullParser#TEXT}. Any other tag will fail this check.
135      *
136      * <p>The parser will be pointing to the first next element after skipping comments,
137      * instructions and ignorable whitespace.
138      */
readNextText(TypedXmlPullParser parser, String tagName)139     public static void readNextText(TypedXmlPullParser parser, String tagName)
140             throws XmlParserException, IOException {
141         try {
142             int type = parser.next(); // skips comments, instruction tokens and ignorable whitespace
143             XmlValidator.checkParserCondition(type == XmlPullParser.TEXT,
144                     "Unexpected event %s of type %d, expected text event inside tag %s",
145                     parser.getName(), type, tagName);
146         } catch (XmlPullParserException e) {
147             throw XmlParserException.createFromPullParserException("text event", e);
148         }
149     }
150 
151     /**
152      * Check parser has a {@link XmlPullParser#END_TAG} as the next tag, with no nested tags.
153      *
154      * <p>The parser will be pointing to the end tag after this method.
155      */
readEndTag(TypedXmlPullParser parser)156     public static void readEndTag(TypedXmlPullParser parser)
157             throws XmlParserException, IOException {
158         readEndTag(parser, parser.getName(), parser.getDepth());
159     }
160 
161     /**
162      * Check parser has a {@link XmlPullParser#END_TAG} with same {@code tagDepth} as the next tag,
163      * with no more nested start tags.
164      *
165      * <p>The parser will be pointing to the end tag after this method.
166      */
readEndTag(TypedXmlPullParser parser, String tagName, int tagDepth)167     public static void readEndTag(TypedXmlPullParser parser, String tagName, int tagDepth)
168             throws XmlParserException, IOException {
169         // Read nested tag first, so we can use the parser.getName() in the error message.
170         boolean hasNestedTag = readNextTagWithin(parser, tagDepth);
171         XmlValidator.checkParserCondition(!hasNestedTag,
172                 "Unexpected nested tag %s found in tag %s", parser.getName(), tagName);
173     }
174 
175     /**
176      * Read attribute from current tag as a non-negative integer, returning default value if
177      * attribute is missing.
178      */
readAttributeIntNonNegative( TypedXmlPullParser parser, String attrName, int defaultValue)179     public static int readAttributeIntNonNegative(
180             TypedXmlPullParser parser, String attrName, int defaultValue)
181             throws XmlParserException {
182         if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
183             return defaultValue;
184         }
185         return readAttributeIntNonNegative(parser, attrName);
186     }
187 
188     /** Read attribute from current tag as a non-negative integer. */
readAttributeIntNonNegative(TypedXmlPullParser parser, String attrName)189     public static int readAttributeIntNonNegative(TypedXmlPullParser parser, String attrName)
190             throws XmlParserException {
191         String tagName = parser.getName();
192         int value = readAttributeInt(parser, attrName);
193 
194         XmlValidator.checkParserCondition(value >= 0,
195                 "Unexpected %s = %d in tag %s, expected %s >= 0",
196                 attrName, value, tagName, attrName);
197         return value;
198     }
199 
200     /** Read attribute from current tag as an integer within given inclusive range. */
readAttributeIntInRange( TypedXmlPullParser parser, String attrName, int lowerInclusive, int upperInclusive)201     public static int readAttributeIntInRange(
202             TypedXmlPullParser parser, String attrName, int lowerInclusive, int upperInclusive)
203             throws XmlParserException {
204         String tagName = parser.getName();
205         int value = readAttributeInt(parser, attrName);
206 
207         XmlValidator.checkParserCondition(
208                 value >= lowerInclusive && value <= upperInclusive,
209                 "Unexpected %s = %d in tag %s, expected %s in [%d, %d]",
210                 attrName, value, tagName, attrName, lowerInclusive, upperInclusive);
211         return value;
212     }
213 
214     /**
215      * Read attribute from current tag as a float within given inclusive range, returning default
216      * value if attribute is missing.
217      */
readAttributeFloatInRange( TypedXmlPullParser parser, String attrName, float lowerInclusive, float upperInclusive, float defaultValue)218     public static float readAttributeFloatInRange(
219             TypedXmlPullParser parser, String attrName, float lowerInclusive,
220             float upperInclusive, float defaultValue) throws XmlParserException {
221         if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
222             return defaultValue;
223         }
224 
225         return readAttributeFloatInRange(parser, attrName, lowerInclusive, upperInclusive);
226     }
227 
228     /**
229      * Read attribute from current tag as a float within given inclusive range.
230      */
readAttributeFloatInRange( TypedXmlPullParser parser, String attrName, float lowerInclusive, float upperInclusive)231     public static float readAttributeFloatInRange(
232             TypedXmlPullParser parser, String attrName, float lowerInclusive,
233             float upperInclusive) throws XmlParserException {
234         String tagName = parser.getName();
235         float value = readAttributeFloat(parser, attrName);
236 
237         XmlValidator.checkParserCondition(value >= lowerInclusive && value <= upperInclusive,
238                 "Unexpected %s = %f in tag %s, expected %s in [%f, %f]", attrName, value, tagName,
239                 attrName, lowerInclusive, upperInclusive);
240         return value;
241     }
242 
243     /**
244      * Read attribute from current tag as a positive float, returning default value if attribute
245      * is missing.
246      */
readAttributePositiveFloat(TypedXmlPullParser parser, String attrName, float defaultValue)247     public static float readAttributePositiveFloat(TypedXmlPullParser parser, String attrName,
248             float defaultValue) throws XmlParserException {
249         if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
250             return defaultValue;
251         }
252 
253         return readAttributePositiveFloat(parser, attrName);
254     }
255 
256     /**
257      * Read attribute from current tag as a positive float.
258      */
readAttributePositiveFloat(TypedXmlPullParser parser, String attrName)259     public static float readAttributePositiveFloat(TypedXmlPullParser parser, String attrName)
260             throws XmlParserException {
261         String tagName = parser.getName();
262         float value = readAttributeFloat(parser, attrName);
263 
264         XmlValidator.checkParserCondition(value > 0,
265                 "Unexpected %s = %d in tag %s, expected %s > 0", attrName, value, tagName,
266                 attrName);
267         return value;
268     }
269 
270     /**
271      * Read attribute from current tag as a positive long.
272      */
readAttributePositiveLong(TypedXmlPullParser parser, String attrName)273     public static long readAttributePositiveLong(TypedXmlPullParser parser, String attrName)
274             throws XmlParserException {
275         String tagName = parser.getName();
276         long value = readAttributeLong(parser, attrName);
277 
278         XmlValidator.checkParserCondition(value > 0,
279                 "Unexpected %s = %d in tag %s, expected %s > 0", attrName, value, tagName,
280                 attrName);
281         return value;
282     }
283 
readAttributeInt(TypedXmlPullParser parser, String attrName)284     private static int readAttributeInt(TypedXmlPullParser parser, String attrName)
285             throws XmlParserException {
286         String tagName = parser.getName();
287         try {
288             return parser.getAttributeInt(NAMESPACE, attrName);
289         } catch (XmlPullParserException e) {
290             String rawValue = parser.getAttributeValue(NAMESPACE, attrName);
291             throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
292         }
293     }
294 
readAttributeFloat(TypedXmlPullParser parser, String attrName)295     private static float readAttributeFloat(TypedXmlPullParser parser, String attrName)
296             throws XmlParserException {
297         String tagName = parser.getName();
298         try {
299             return parser.getAttributeFloat(NAMESPACE, attrName);
300         } catch (XmlPullParserException e) {
301             String rawValue = parser.getAttributeValue(NAMESPACE, attrName);
302             throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
303         }
304     }
305 
readAttributeLong(TypedXmlPullParser parser, String attrName)306     private static long readAttributeLong(TypedXmlPullParser parser, String attrName)
307             throws XmlParserException {
308         String tagName = parser.getName();
309         try {
310             return parser.getAttributeLong(NAMESPACE, attrName);
311         } catch (XmlPullParserException e) {
312             String rawValue = parser.getAttributeValue(NAMESPACE, attrName);
313             throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
314         }
315     }
316 }
317