1 /*
2  * Copyright (C) 2008 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.layoutlib.bridge.android;
18 
19 
20 import com.android.ide.common.rendering.api.ILayoutPullParser;
21 import com.android.layoutlib.bridge.impl.ParserFactory;
22 
23 import org.xmlpull.v1.XmlPullParser;
24 import org.xmlpull.v1.XmlPullParserException;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.content.res.XmlResourceParser;
29 import android.util.AttributeSet;
30 import android.util.BridgeXmlPullAttributes;
31 
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.Reader;
35 
36 /**
37  * {@link BridgeXmlBlockParser} reimplements most of android.xml.XmlBlock.Parser.
38  * It delegates to both an instance of {@link XmlPullParser} and an instance of
39  * XmlPullAttributes (for the {@link AttributeSet} part).
40  */
41 public class BridgeXmlBlockParser implements XmlResourceParser {
42 
43     private final XmlPullParser mParser;
44     private final AttributeSet mAttrib;
45     private final BridgeContext mContext;
46     private final boolean mPlatformFile;
47 
48     private boolean mStarted = false;
49     private int mEventType = START_DOCUMENT;
50 
51     private boolean mPopped = true; // default to true in case it's not pushed.
52 
53     /**
54      * Builds a {@link BridgeXmlBlockParser}.
55      * @param parser The XmlPullParser to get the content from.
56      * @param context the Context.
57      * @param platformFile Indicates whether the the file is a platform file or not.
58      */
BridgeXmlBlockParser(@onNull XmlPullParser parser, @Nullable BridgeContext context, boolean platformFile)59     public BridgeXmlBlockParser(@NonNull XmlPullParser parser, @Nullable BridgeContext context,
60             boolean platformFile) {
61         if (ParserFactory.LOG_PARSER) {
62             System.out.println("CRTE " + parser.toString());
63         }
64 
65         mParser = parser;
66         mContext = context;
67         mPlatformFile = platformFile;
68 
69         if (mContext != null) {
70             mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
71             mContext.pushParser(this);
72             mPopped = false;
73         }
74         else {
75             mAttrib = new NopAttributeSet();
76         }
77     }
78 
getParser()79     public XmlPullParser getParser() {
80         return mParser;
81     }
82 
isPlatformFile()83     public boolean isPlatformFile() {
84         return mPlatformFile;
85     }
86 
getViewCookie()87     public Object getViewCookie() {
88         if (mParser instanceof ILayoutPullParser) {
89             return ((ILayoutPullParser)mParser).getViewCookie();
90         }
91 
92         return null;
93     }
94 
ensurePopped()95     public void ensurePopped() {
96         if (mContext != null && !mPopped) {
97             mContext.popParser();
98             mPopped = true;
99         }
100     }
101 
102     // ------- XmlResourceParser implementation
103 
104     @Override
setFeature(String name, boolean state)105     public void setFeature(String name, boolean state)
106             throws XmlPullParserException {
107         if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
108             return;
109         }
110         if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
111             return;
112         }
113         throw new XmlPullParserException("Unsupported feature: " + name);
114     }
115 
116     @Override
getFeature(String name)117     public boolean getFeature(String name) {
118         return FEATURE_PROCESS_NAMESPACES.equals(name) ||
119                 FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name);
120     }
121 
122     @Override
setProperty(String name, Object value)123     public void setProperty(String name, Object value) throws XmlPullParserException {
124         throw new XmlPullParserException("setProperty() not supported");
125     }
126 
127     @Override
getProperty(String name)128     public Object getProperty(String name) {
129         return null;
130     }
131 
132     @Override
setInput(Reader in)133     public void setInput(Reader in) throws XmlPullParserException {
134         mParser.setInput(in);
135     }
136 
137     @Override
setInput(InputStream inputStream, String inputEncoding)138     public void setInput(InputStream inputStream, String inputEncoding)
139             throws XmlPullParserException {
140         mParser.setInput(inputStream, inputEncoding);
141     }
142 
143     @Override
defineEntityReplacementText(String entityName, String replacementText)144     public void defineEntityReplacementText(String entityName,
145             String replacementText) throws XmlPullParserException {
146         throw new XmlPullParserException(
147                 "defineEntityReplacementText() not supported");
148     }
149 
150     @Override
getNamespacePrefix(int pos)151     public String getNamespacePrefix(int pos) throws XmlPullParserException {
152         throw new XmlPullParserException("getNamespacePrefix() not supported");
153     }
154 
155     @Override
getInputEncoding()156     public String getInputEncoding() {
157         return null;
158     }
159 
160     @Override
getNamespace(String prefix)161     public String getNamespace(String prefix) {
162         return mParser.getNamespace(prefix);
163     }
164 
165     @Override
getNamespaceCount(int depth)166     public int getNamespaceCount(int depth) throws XmlPullParserException {
167         throw new XmlPullParserException("getNamespaceCount() not supported");
168     }
169 
170     @Override
getPositionDescription()171     public String getPositionDescription() {
172         return "Binary XML file line #" + getLineNumber();
173     }
174 
175     @Override
getNamespaceUri(int pos)176     public String getNamespaceUri(int pos) throws XmlPullParserException {
177         throw new XmlPullParserException("getNamespaceUri() not supported");
178     }
179 
180     @Override
getColumnNumber()181     public int getColumnNumber() {
182         return -1;
183     }
184 
185     @Override
getDepth()186     public int getDepth() {
187         return mParser.getDepth();
188     }
189 
190     @Override
getText()191     public String getText() {
192         return mParser.getText();
193     }
194 
195     @Override
getLineNumber()196     public int getLineNumber() {
197         return mParser.getLineNumber();
198     }
199 
200     @Override
getEventType()201     public int getEventType() {
202         return mEventType;
203     }
204 
205     @Override
isWhitespace()206     public boolean isWhitespace() throws XmlPullParserException {
207         // Original comment: whitespace was stripped by aapt.
208         return mParser.isWhitespace();
209     }
210 
211     @Override
getPrefix()212     public String getPrefix() {
213         throw new RuntimeException("getPrefix not supported");
214     }
215 
216     @Override
getTextCharacters(int[] holderForStartAndLength)217     public char[] getTextCharacters(int[] holderForStartAndLength) {
218         String txt = getText();
219         char[] chars = null;
220         if (txt != null) {
221             holderForStartAndLength[0] = 0;
222             holderForStartAndLength[1] = txt.length();
223             chars = new char[txt.length()];
224             txt.getChars(0, txt.length(), chars, 0);
225         }
226         return chars;
227     }
228 
229     @Override
getNamespace()230     public String getNamespace() {
231         return mParser.getNamespace();
232     }
233 
234     @Override
getName()235     public String getName() {
236         return mParser.getName();
237     }
238 
239     @Override
getAttributeNamespace(int index)240     public String getAttributeNamespace(int index) {
241         return mParser.getAttributeNamespace(index);
242     }
243 
244     @Override
getAttributeName(int index)245     public String getAttributeName(int index) {
246         return mParser.getAttributeName(index);
247     }
248 
249     @Override
getAttributePrefix(int index)250     public String getAttributePrefix(int index) {
251         throw new RuntimeException("getAttributePrefix not supported");
252     }
253 
254     @Override
isEmptyElementTag()255     public boolean isEmptyElementTag() {
256         // XXX Need to detect this.
257         return false;
258     }
259 
260     @Override
getAttributeCount()261     public int getAttributeCount() {
262         return mParser.getAttributeCount();
263     }
264 
265     @Override
getAttributeValue(int index)266     public String getAttributeValue(int index) {
267         return mParser.getAttributeValue(index);
268     }
269 
270     @Override
getAttributeType(int index)271     public String getAttributeType(int index) {
272         return "CDATA";
273     }
274 
275     @Override
isAttributeDefault(int index)276     public boolean isAttributeDefault(int index) {
277         return false;
278     }
279 
280     @Override
nextToken()281     public int nextToken() throws XmlPullParserException, IOException {
282         return next();
283     }
284 
285     @Override
getAttributeValue(String namespace, String name)286     public String getAttributeValue(String namespace, String name) {
287         return mParser.getAttributeValue(namespace, name);
288     }
289 
290     @Override
next()291     public int next() throws XmlPullParserException, IOException {
292         if (!mStarted) {
293             mStarted = true;
294 
295             if (ParserFactory.LOG_PARSER) {
296                 System.out.println("STRT " + mParser.toString());
297             }
298 
299             return START_DOCUMENT;
300         }
301 
302         int ev = mParser.next();
303 
304         if (ParserFactory.LOG_PARSER) {
305             System.out.println("NEXT " + mParser.toString() + " " +
306                     eventTypeToString(mEventType) + " -> " + eventTypeToString(ev));
307         }
308 
309         if (ev == END_TAG && mParser.getDepth() == 1) {
310             // done with parser remove it from the context stack.
311             ensurePopped();
312 
313             if (ParserFactory.LOG_PARSER) {
314                 System.out.println("");
315             }
316         }
317 
318         mEventType = ev;
319         return ev;
320     }
321 
eventTypeToString(int eventType)322     private static String eventTypeToString(int eventType) {
323         switch (eventType) {
324             case START_DOCUMENT:
325                 return "START_DOC";
326             case END_DOCUMENT:
327                 return "END_DOC";
328             case START_TAG:
329                 return "START_TAG";
330             case END_TAG:
331                 return "END_TAG";
332             case TEXT:
333                 return "TEXT";
334             case CDSECT:
335                 return "CDSECT";
336             case ENTITY_REF:
337                 return "ENTITY_REF";
338             case IGNORABLE_WHITESPACE:
339                 return "IGNORABLE_WHITESPACE";
340             case PROCESSING_INSTRUCTION:
341                 return "PROCESSING_INSTRUCTION";
342             case COMMENT:
343                 return "COMMENT";
344             case DOCDECL:
345                 return "DOCDECL";
346         }
347 
348         return "????";
349     }
350 
351     @Override
require(int type, String namespace, String name)352     public void require(int type, String namespace, String name)
353             throws XmlPullParserException {
354         if (type != getEventType()
355                 || (namespace != null && !namespace.equals(getNamespace()))
356                 || (name != null && !name.equals(getName())))
357             throw new XmlPullParserException("expected " + TYPES[type]
358                     + getPositionDescription());
359     }
360 
361     @Override
nextText()362     public String nextText() throws XmlPullParserException, IOException {
363         if (getEventType() != START_TAG) {
364             throw new XmlPullParserException(getPositionDescription()
365                     + ": parser must be on START_TAG to read next text", this,
366                     null);
367         }
368         int eventType = next();
369         if (eventType == TEXT) {
370             String result = getText();
371             eventType = next();
372             if (eventType != END_TAG) {
373                 throw new XmlPullParserException(
374                         getPositionDescription()
375                                 + ": event TEXT it must be immediately followed by END_TAG",
376                         this, null);
377             }
378             return result;
379         } else if (eventType == END_TAG) {
380             return "";
381         } else {
382             throw new XmlPullParserException(getPositionDescription()
383                     + ": parser must be on START_TAG or TEXT to read text",
384                     this, null);
385         }
386     }
387 
388     @Override
nextTag()389     public int nextTag() throws XmlPullParserException, IOException {
390         int eventType = next();
391         if (eventType == TEXT && isWhitespace()) { // skip whitespace
392             eventType = next();
393         }
394         if (eventType != START_TAG && eventType != END_TAG) {
395             throw new XmlPullParserException(getPositionDescription()
396                     + ": expected start or end tag", this, null);
397         }
398         return eventType;
399     }
400 
401     // AttributeSet implementation
402 
403 
404     @Override
close()405     public void close() {
406         // pass
407     }
408 
409     @Override
getAttributeBooleanValue(int index, boolean defaultValue)410     public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
411         return mAttrib.getAttributeBooleanValue(index, defaultValue);
412     }
413 
414     @Override
getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue)415     public boolean getAttributeBooleanValue(String namespace, String attribute,
416             boolean defaultValue) {
417         return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue);
418     }
419 
420     @Override
getAttributeFloatValue(int index, float defaultValue)421     public float getAttributeFloatValue(int index, float defaultValue) {
422         return mAttrib.getAttributeFloatValue(index, defaultValue);
423     }
424 
425     @Override
getAttributeFloatValue(String namespace, String attribute, float defaultValue)426     public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
427         return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue);
428     }
429 
430     @Override
getAttributeIntValue(int index, int defaultValue)431     public int getAttributeIntValue(int index, int defaultValue) {
432         return mAttrib.getAttributeIntValue(index, defaultValue);
433     }
434 
435     @Override
getAttributeIntValue(String namespace, String attribute, int defaultValue)436     public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
437         return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue);
438     }
439 
440     @Override
getAttributeListValue(int index, String[] options, int defaultValue)441     public int getAttributeListValue(int index, String[] options, int defaultValue) {
442         return mAttrib.getAttributeListValue(index, options, defaultValue);
443     }
444 
445     @Override
getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue)446     public int getAttributeListValue(String namespace, String attribute,
447             String[] options, int defaultValue) {
448         return mAttrib.getAttributeListValue(namespace, attribute, options, defaultValue);
449     }
450 
451     @Override
getAttributeNameResource(int index)452     public int getAttributeNameResource(int index) {
453         return mAttrib.getAttributeNameResource(index);
454     }
455 
456     @Override
getAttributeResourceValue(int index, int defaultValue)457     public int getAttributeResourceValue(int index, int defaultValue) {
458         return mAttrib.getAttributeResourceValue(index, defaultValue);
459     }
460 
461     @Override
getAttributeResourceValue(String namespace, String attribute, int defaultValue)462     public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
463         return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue);
464     }
465 
466     @Override
getAttributeUnsignedIntValue(int index, int defaultValue)467     public int getAttributeUnsignedIntValue(int index, int defaultValue) {
468         return mAttrib.getAttributeUnsignedIntValue(index, defaultValue);
469     }
470 
471     @Override
getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue)472     public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
473         return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
474     }
475 
476     @Override
getClassAttribute()477     public String getClassAttribute() {
478         return mAttrib.getClassAttribute();
479     }
480 
481     @Override
getIdAttribute()482     public String getIdAttribute() {
483         return mAttrib.getIdAttribute();
484     }
485 
486     @Override
getIdAttributeResourceValue(int defaultValue)487     public int getIdAttributeResourceValue(int defaultValue) {
488         return mAttrib.getIdAttributeResourceValue(defaultValue);
489     }
490 
491     @Override
getStyleAttribute()492     public int getStyleAttribute() {
493         return mAttrib.getStyleAttribute();
494     }
495 
496 }
497