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 */ 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 73 public XmlPullParser getParser() { 74 return mParser; 75 } 76 77 public boolean isPlatformFile() { 78 return mPlatformFile; 79 } 80 81 public Object getViewCookie() { 82 if (mParser instanceof ILayoutPullParser) { 83 return ((ILayoutPullParser)mParser).getViewCookie(); 84 } 85 86 return null; 87 } 88 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 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 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 122 public void setProperty(String name, Object value) throws XmlPullParserException { 123 throw new XmlPullParserException("setProperty() not supported"); 124 } 125 126 @Override 127 public Object getProperty(String name) { 128 return null; 129 } 130 131 @Override 132 public void setInput(Reader in) throws XmlPullParserException { 133 mParser.setInput(in); 134 } 135 136 @Override 137 public void setInput(InputStream inputStream, String inputEncoding) 138 throws XmlPullParserException { 139 mParser.setInput(inputStream, inputEncoding); 140 } 141 142 @Override 143 public void defineEntityReplacementText(String entityName, 144 String replacementText) throws XmlPullParserException { 145 throw new XmlPullParserException( 146 "defineEntityReplacementText() not supported"); 147 } 148 149 @Override 150 public String getNamespacePrefix(int pos) throws XmlPullParserException { 151 throw new XmlPullParserException("getNamespacePrefix() not supported"); 152 } 153 154 @Override 155 public String getInputEncoding() { 156 return null; 157 } 158 159 @Override 160 public String getNamespace(String prefix) { 161 throw new RuntimeException("getNamespace() not supported"); 162 } 163 164 @Override 165 public int getNamespaceCount(int depth) throws XmlPullParserException { 166 throw new XmlPullParserException("getNamespaceCount() not supported"); 167 } 168 169 @Override 170 public String getPositionDescription() { 171 return "Binary XML file line #" + getLineNumber(); 172 } 173 174 @Override 175 public String getNamespaceUri(int pos) throws XmlPullParserException { 176 throw new XmlPullParserException("getNamespaceUri() not supported"); 177 } 178 179 @Override 180 public int getColumnNumber() { 181 return -1; 182 } 183 184 @Override 185 public int getDepth() { 186 return mParser.getDepth(); 187 } 188 189 @Override 190 public String getText() { 191 return mParser.getText(); 192 } 193 194 @Override 195 public int getLineNumber() { 196 return mParser.getLineNumber(); 197 } 198 199 @Override 200 public int getEventType() { 201 return mEventType; 202 } 203 204 @Override 205 public boolean isWhitespace() throws XmlPullParserException { 206 // Original comment: whitespace was stripped by aapt. 207 return mParser.isWhitespace(); 208 } 209 210 @Override 211 public String getPrefix() { 212 throw new RuntimeException("getPrefix not supported"); 213 } 214 215 @Override 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 229 public String getNamespace() { 230 return mParser.getNamespace(); 231 } 232 233 @Override 234 public String getName() { 235 return mParser.getName(); 236 } 237 238 @Override 239 public String getAttributeNamespace(int index) { 240 return mParser.getAttributeNamespace(index); 241 } 242 243 @Override 244 public String getAttributeName(int index) { 245 return mParser.getAttributeName(index); 246 } 247 248 @Override 249 public String getAttributePrefix(int index) { 250 throw new RuntimeException("getAttributePrefix not supported"); 251 } 252 253 @Override 254 public boolean isEmptyElementTag() { 255 // XXX Need to detect this. 256 return false; 257 } 258 259 @Override 260 public int getAttributeCount() { 261 return mParser.getAttributeCount(); 262 } 263 264 @Override 265 public String getAttributeValue(int index) { 266 return mParser.getAttributeValue(index); 267 } 268 269 @Override 270 public String getAttributeType(int index) { 271 return "CDATA"; 272 } 273 274 @Override 275 public boolean isAttributeDefault(int index) { 276 return false; 277 } 278 279 @Override 280 public int nextToken() throws XmlPullParserException, IOException { 281 return next(); 282 } 283 284 @Override 285 public String getAttributeValue(String namespace, String name) { 286 return mParser.getAttributeValue(namespace, name); 287 } 288 289 @Override 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 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 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 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 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 404 public void close() { 405 // pass 406 } 407 408 @Override 409 public boolean getAttributeBooleanValue(int index, boolean defaultValue) { 410 return mAttrib.getAttributeBooleanValue(index, defaultValue); 411 } 412 413 @Override 414 public boolean getAttributeBooleanValue(String namespace, String attribute, 415 boolean defaultValue) { 416 return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue); 417 } 418 419 @Override 420 public float getAttributeFloatValue(int index, float defaultValue) { 421 return mAttrib.getAttributeFloatValue(index, defaultValue); 422 } 423 424 @Override 425 public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { 426 return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue); 427 } 428 429 @Override 430 public int getAttributeIntValue(int index, int defaultValue) { 431 return mAttrib.getAttributeIntValue(index, defaultValue); 432 } 433 434 @Override 435 public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { 436 return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue); 437 } 438 439 @Override 440 public int getAttributeListValue(int index, String[] options, int defaultValue) { 441 return mAttrib.getAttributeListValue(index, options, defaultValue); 442 } 443 444 @Override 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 451 public int getAttributeNameResource(int index) { 452 return mAttrib.getAttributeNameResource(index); 453 } 454 455 @Override 456 public int getAttributeResourceValue(int index, int defaultValue) { 457 return mAttrib.getAttributeResourceValue(index, defaultValue); 458 } 459 460 @Override 461 public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { 462 return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue); 463 } 464 465 @Override 466 public int getAttributeUnsignedIntValue(int index, int defaultValue) { 467 return mAttrib.getAttributeUnsignedIntValue(index, defaultValue); 468 } 469 470 @Override 471 public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) { 472 return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue); 473 } 474 475 @Override 476 public String getClassAttribute() { 477 return mAttrib.getClassAttribute(); 478 } 479 480 @Override 481 public String getIdAttribute() { 482 return mAttrib.getIdAttribute(); 483 } 484 485 @Override 486 public int getIdAttributeResourceValue(int defaultValue) { 487 return mAttrib.getIdAttributeResourceValue(defaultValue); 488 } 489 490 @Override 491 public int getStyleAttribute() { 492 return mAttrib.getStyleAttribute(); 493 } 494 } 495