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