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