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