1 package org.robolectric.res.android; 2 3 import static org.robolectric.res.android.Errors.BAD_TYPE; 4 import static org.robolectric.res.android.Errors.NAME_NOT_FOUND; 5 import static org.robolectric.res.android.Errors.NO_ERROR; 6 import static org.robolectric.res.android.ResTable.kDebugStringPoolNoisy; 7 import static org.robolectric.res.android.ResTable.kDebugXMLNoisy; 8 import static org.robolectric.res.android.ResXMLParser.event_code_t.BAD_DOCUMENT; 9 import static org.robolectric.res.android.ResXMLParser.event_code_t.END_DOCUMENT; 10 import static org.robolectric.res.android.ResXMLParser.event_code_t.END_NAMESPACE; 11 import static org.robolectric.res.android.ResXMLParser.event_code_t.END_TAG; 12 import static org.robolectric.res.android.ResXMLParser.event_code_t.FIRST_CHUNK_CODE; 13 import static org.robolectric.res.android.ResXMLParser.event_code_t.START_DOCUMENT; 14 import static org.robolectric.res.android.ResXMLParser.event_code_t.START_NAMESPACE; 15 import static org.robolectric.res.android.ResXMLParser.event_code_t.START_TAG; 16 import static org.robolectric.res.android.ResXMLParser.event_code_t.TEXT; 17 import static org.robolectric.res.android.ResourceTypes.RES_XML_CDATA_TYPE; 18 import static org.robolectric.res.android.ResourceTypes.RES_XML_END_ELEMENT_TYPE; 19 import static org.robolectric.res.android.ResourceTypes.RES_XML_END_NAMESPACE_TYPE; 20 import static org.robolectric.res.android.ResourceTypes.RES_XML_FIRST_CHUNK_TYPE; 21 import static org.robolectric.res.android.ResourceTypes.RES_XML_START_ELEMENT_TYPE; 22 import static org.robolectric.res.android.ResourceTypes.RES_XML_START_NAMESPACE_TYPE; 23 import static org.robolectric.res.android.Util.ALOGI; 24 import static org.robolectric.res.android.Util.ALOGW; 25 import static org.robolectric.res.android.Util.dtohl; 26 import static org.robolectric.res.android.Util.dtohs; 27 import static org.robolectric.res.android.Util.isTruthy; 28 29 import org.robolectric.res.android.ResourceTypes.ResChunk_header; 30 import org.robolectric.res.android.ResourceTypes.ResXMLTree_attrExt; 31 import org.robolectric.res.android.ResourceTypes.ResXMLTree_attribute; 32 import org.robolectric.res.android.ResourceTypes.ResXMLTree_endElementExt; 33 import org.robolectric.res.android.ResourceTypes.ResXMLTree_node; 34 import org.robolectric.res.android.ResourceTypes.Res_value; 35 36 public class ResXMLParser { 37 38 static final int SIZEOF_RESXMLTREE_NAMESPACE_EXT = 4; 39 static final int SIZEOF_RESXMLTREE_NODE = ResChunk_header.SIZEOF + 8; 40 static final int SIZEOF_RESXMLTREE_ATTR_EXT = 20; 41 static final int SIZEOF_RESXMLTREE_CDATA_EXT = 4 + ResourceTypes.Res_value.SIZEOF; 42 static final int SIZEOF_CHAR = 2; 43 44 public static class event_code_t { 45 public static final int BAD_DOCUMENT = -1; 46 public static final int START_DOCUMENT = 0; 47 public static final int END_DOCUMENT = 1; 48 49 public static final int FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE; 50 51 public static final int START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE; 52 public static final int END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE; 53 public static final int START_TAG = RES_XML_START_ELEMENT_TYPE; 54 public static final int END_TAG = RES_XML_END_ELEMENT_TYPE; 55 public static final int TEXT = RES_XML_CDATA_TYPE; 56 } 57 58 ResXMLTree mTree; 59 int mEventCode; 60 ResXMLTree_node mCurNode; 61 int mCurExt; 62 63 public ResXMLParser(ResXMLTree tree) { 64 this.mTree = tree; 65 this.mEventCode = BAD_DOCUMENT; 66 } 67 68 public void restart() { 69 mCurNode = null; 70 mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; 71 } 72 73 public ResStringPool getStrings() { 74 return mTree.mStrings; 75 } 76 77 int getEventType() 78 { 79 return mEventCode; 80 } 81 82 public int next() 83 { 84 if (mEventCode == START_DOCUMENT) { 85 mCurNode = mTree.mRootNode; 86 mCurExt = mTree.mRootExt; 87 return (mEventCode=mTree.mRootCode); 88 } else if (mEventCode >= FIRST_CHUNK_CODE) { 89 return nextNode(); 90 } 91 return mEventCode; 92 } 93 94 int getCommentID() 95 { 96 return mCurNode != null ? dtohl(mCurNode.comment.index) : -1; 97 } 98 99 final String getComment(Ref<Integer> outLen) 100 { 101 int id = getCommentID(); 102 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 103 } 104 105 public int getLineNumber() 106 { 107 return mCurNode != null ? dtohl(mCurNode.lineNumber) : -1; 108 } 109 110 public int getTextID() 111 { 112 if (mEventCode == TEXT) { 113 return dtohl(new ResourceTypes.ResXMLTree_cdataExt(mTree.mBuffer.buf, mCurExt).data.index); 114 } 115 return -1; 116 } 117 118 final String getText(Ref<Integer> outLen) 119 { 120 int id = getTextID(); 121 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 122 } 123 124 int getTextValue(Res_value outValue) 125 { 126 if (mEventCode == TEXT) { 127 //outValue.copyFrom_dtoh(new ResourceTypes.ResXMLTree_cdataExt(mTree.mBuffer.buf, mCurExt).typedData); 128 return ResourceTypes.Res_value.SIZEOF /* sizeof(Res_value) */; 129 } 130 return BAD_TYPE; 131 } 132 133 int getNamespacePrefixID() 134 { 135 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { 136 return dtohl(new ResourceTypes.ResXMLTree_namespaceExt(mTree.mBuffer.buf, mCurExt).prefix.index); 137 } 138 return -1; 139 } 140 141 final String getNamespacePrefix(Ref<Integer> outLen) 142 { 143 int id = getNamespacePrefixID(); 144 //printf("prefix=%d event=%s\n", id, mEventCode); 145 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 146 } 147 148 int getNamespaceUriID() 149 { 150 if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { 151 return dtohl(new ResourceTypes.ResXMLTree_namespaceExt(mTree.mBuffer.buf, mCurExt).uri.index); 152 } 153 return -1; 154 } 155 156 final String getNamespaceUri(Ref<Integer> outLen) 157 { 158 int id = getNamespaceUriID(); 159 //printf("uri=%d event=%s\n", id, mEventCode); 160 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 161 } 162 163 public int getElementNamespaceID() 164 { 165 if (mEventCode == START_TAG) { 166 return dtohl(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).ns.index); 167 } 168 if (mEventCode == END_TAG) { 169 return dtohl(new ResXMLTree_endElementExt(mTree.mBuffer.buf, mCurExt).ns.index); 170 } 171 return -1; 172 } 173 174 final String getElementNamespace(Ref<Integer> outLen) 175 { 176 int id = getElementNamespaceID(); 177 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 178 } 179 180 public int getElementNameID() 181 { 182 if (mEventCode == START_TAG) { 183 return dtohl(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).name.index); 184 } 185 if (mEventCode == END_TAG) { 186 return dtohl(new ResXMLTree_endElementExt(mTree.mBuffer.buf, mCurExt).name.index); 187 } 188 return -1; 189 } 190 191 final String getElementName(Ref<Integer> outLen) 192 { 193 int id = getElementNameID(); 194 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 195 } 196 197 public int getAttributeCount() 198 { 199 if (mEventCode == START_TAG) { 200 return dtohs((new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt)).attributeCount); 201 } 202 return 0; 203 } 204 205 public int getAttributeNamespaceID(int idx) 206 { 207 if (mEventCode == START_TAG) { 208 ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt); 209 if (idx < dtohs(tag.attributeCount)) { 210 // final ResXMLTree_attribute attr = (ResXMLTree_attribute) 211 // (((final int8_t*)tag) 212 // + dtohs(tag.attributeStart()) 213 // + (dtohs(tag.attributeSize())*idx)); 214 ResXMLTree_attribute attr = tag.attributeAt(idx); 215 return dtohl(attr.ns.index); 216 } 217 } 218 return -2; 219 } 220 221 final String getAttributeNamespace(int idx, Ref<Integer> outLen) 222 { 223 int id = getAttributeNamespaceID(idx); 224 //printf("attribute namespace=%d idx=%d event=%s\n", id, idx, mEventCode); 225 if (kDebugXMLNoisy) { 226 System.out.println(String.format("getAttributeNamespace 0x%x=0x%x\n", idx, id)); 227 } 228 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 229 } 230 231 final String getAttributeNamespace8(int idx, Ref<Integer> outLen) 232 { 233 int id = getAttributeNamespaceID(idx); 234 //printf("attribute namespace=%d idx=%d event=%s\n", id, idx, mEventCode); 235 if (kDebugXMLNoisy) { 236 System.out.println(String.format("getAttributeNamespace 0x%x=0x%x\n", idx, id)); 237 } 238 return id >= 0 ? mTree.mStrings.string8At(id, outLen) : null; 239 } 240 241 public int getAttributeNameID(int idx) 242 { 243 if (mEventCode == START_TAG) { 244 ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt); 245 if (idx < dtohs(tag.attributeCount)) { 246 // final ResXMLTree_attribute attr = (ResXMLTree_attribute) 247 // (((final int8_t*)tag) 248 // + dtohs(tag.attributeStart()) 249 // + (dtohs(tag.attributeSize())*idx)); 250 ResXMLTree_attribute attr = tag.attributeAt(idx); 251 return dtohl(attr.name.index); 252 } 253 } 254 return -1; 255 } 256 257 final String getAttributeName(int idx, Ref<Integer> outLen) 258 { 259 int id = getAttributeNameID(idx); 260 //printf("attribute name=%d idx=%d event=%s\n", id, idx, mEventCode); 261 if (kDebugXMLNoisy) { 262 System.out.println(String.format("getAttributeName 0x%x=0x%x\n", idx, id)); 263 } 264 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 265 } 266 267 final String getAttributeName8(int idx, Ref<Integer> outLen) 268 { 269 int id = getAttributeNameID(idx); 270 //printf("attribute name=%d idx=%d event=%s\n", id, idx, mEventCode); 271 if (kDebugXMLNoisy) { 272 System.out.println(String.format("getAttributeName 0x%x=0x%x\n", idx, id)); 273 } 274 return id >= 0 ? mTree.mStrings.string8At(id, outLen) : null; 275 } 276 277 public int getAttributeNameResID(int idx) 278 { 279 int id = getAttributeNameID(idx); 280 if (id >= 0 && (int)id < mTree.mNumResIds) { 281 int resId = dtohl(mTree.mResIds[id]); 282 if (mTree.mDynamicRefTable != null) { 283 final Ref<Integer> resIdRef = new Ref<>(resId); 284 mTree.mDynamicRefTable.lookupResourceId(resIdRef); 285 resId = resIdRef.get(); 286 } 287 return resId; 288 } 289 return 0; 290 } 291 292 public int getAttributeValueStringID(int idx) 293 { 294 if (mEventCode == START_TAG) { 295 ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt); 296 if (idx < dtohs(tag.attributeCount)) { 297 // final ResXMLTree_attribute attr = (ResXMLTree_attribute) 298 // (((final int8_t*)tag) 299 // + dtohs(tag.attributeStart()) 300 // + (dtohs(tag.attributeSize())*idx)); 301 ResXMLTree_attribute attr = tag.attributeAt(idx); 302 return dtohl(attr.rawValue.index); 303 } 304 } 305 return -1; 306 } 307 308 final String getAttributeStringValue(int idx, Ref<Integer> outLen) 309 { 310 int id = getAttributeValueStringID(idx); 311 if (kDebugXMLNoisy) { 312 System.out.println(String.format("getAttributeValue 0x%x=0x%x\n", idx, id)); 313 } 314 return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null; 315 } 316 317 public int getAttributeDataType(int idx) 318 { 319 if (mEventCode == START_TAG) { 320 final ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt); 321 if (idx < dtohs(tag.attributeCount)) { 322 // final ResXMLTree_attribute attr = (ResXMLTree_attribute) 323 // (((final int8_t*)tag) 324 // + dtohs(tag.attributeStart()) 325 // + (dtohs(tag.attributeSize())*idx)); 326 ResXMLTree_attribute attr = tag.attributeAt(idx); 327 int type = attr.typedValue.dataType; 328 if (type != DataType.DYNAMIC_REFERENCE.code()) { 329 return type; 330 } 331 332 // This is a dynamic reference. We adjust those references 333 // to regular references at this level, so lie to the caller. 334 return DataType.REFERENCE.code(); 335 } 336 } 337 return DataType.NULL.code(); 338 } 339 340 public int getAttributeData(int idx) 341 { 342 if (mEventCode == START_TAG) { 343 ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt); 344 if (idx < dtohs(tag.attributeCount)) { 345 // final ResXMLTree_attribute attr = (ResXMLTree_attribute) 346 // (((final int8_t*)tag) 347 // + dtohs(tag.attributeStart) 348 // + (dtohs(tag.attributeSize)*idx)); 349 ResXMLTree_attribute attr = tag.attributeAt(idx); 350 if (attr.typedValue.dataType != DataType.DYNAMIC_REFERENCE.code() || 351 mTree.mDynamicRefTable == null) { 352 return dtohl(attr.typedValue.data); 353 } 354 355 final Ref<Integer> data = new Ref<>(dtohl(attr.typedValue.data)); 356 if (mTree.mDynamicRefTable.lookupResourceId(data) == NO_ERROR) { 357 return data.get(); 358 } 359 } 360 } 361 return 0; 362 } 363 364 public int getAttributeValue(int idx, Ref<Res_value> outValue) 365 { 366 if (mEventCode == START_TAG) { 367 ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt); 368 if (idx < dtohs(tag.attributeCount)) { 369 // final ResXMLTree_attribute attr = (ResXMLTree_attribute) 370 // (((final int8_t*)tag) 371 // + dtohs(tag.attributeStart()) 372 // + (dtohs(tag.attributeSize())*idx)); 373 ResXMLTree_attribute attr = tag.attributeAt(idx); 374 outValue.set(attr.typedValue); 375 if (mTree.mDynamicRefTable != null && 376 mTree.mDynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) { 377 return BAD_TYPE; 378 } 379 return ResourceTypes.Res_value.SIZEOF /* sizeof(Res_value) */; 380 } 381 } 382 return BAD_TYPE; 383 } 384 385 int indexOfAttribute(final String ns, final String attr) 386 { 387 String nsStr = ns != null ? ns : ""; 388 String attrStr = attr; 389 return indexOfAttribute(isTruthy(ns) ? nsStr : null, isTruthy(ns) ? nsStr.length() : 0, 390 attrStr, attrStr.length()); 391 } 392 393 public int indexOfAttribute(final String ns, int nsLen, 394 final String attr, int attrLen) 395 { 396 if (mEventCode == START_TAG) { 397 if (attr == null) { 398 return NAME_NOT_FOUND; 399 } 400 final int N = getAttributeCount(); 401 if (mTree.mStrings.isUTF8()) { 402 String8 ns8 = null, attr8; 403 if (ns != null) { 404 ns8 = new String8(ns, nsLen); 405 } 406 attr8 = new String8(attr, attrLen); 407 if (kDebugStringPoolNoisy) { 408 ALOGI("indexOfAttribute UTF8 %s (0x%x) / %s (0x%x)", ns8.string(), nsLen, 409 attr8.string(), attrLen); 410 } 411 for (int i=0; i<N; i++) { 412 final Ref<Integer> curNsLen = new Ref<>(0), curAttrLen = new Ref<>(0); 413 final String curNs = getAttributeNamespace8(i, curNsLen); 414 final String curAttr = getAttributeName8(i, curAttrLen); 415 if (kDebugStringPoolNoisy) { 416 ALOGI(" curNs=%s (0x%x), curAttr=%s (0x%x)", curNs, curNsLen, curAttr, curAttrLen); 417 } 418 if (curAttr != null && curNsLen.get() == nsLen && curAttrLen.get() == attrLen 419 && memcmp(attr8.string(), curAttr, attrLen) == 0) { 420 if (ns == null) { 421 if (curNs == null) { 422 if (kDebugStringPoolNoisy) { 423 ALOGI(" FOUND!"); 424 } 425 return i; 426 } 427 } else if (curNs != null) { 428 //printf(" -. ns=%s, curNs=%s\n", 429 // String8(ns).string(), String8(curNs).string()); 430 if (memcmp(ns8.string(), curNs, nsLen) == 0) { 431 if (kDebugStringPoolNoisy) { 432 ALOGI(" FOUND!"); 433 } 434 return i; 435 } 436 } 437 } 438 } 439 } else { 440 if (kDebugStringPoolNoisy) { 441 ALOGI("indexOfAttribute UTF16 %s (0x%x) / %s (0x%x)", 442 ns /*String8(ns, nsLen).string()*/, nsLen, 443 attr /*String8(attr, attrLen).string()*/, attrLen); 444 } 445 for (int i=0; i<N; i++) { 446 final Ref<Integer> curNsLen = new Ref<>(0), curAttrLen = new Ref<>(0); 447 final String curNs = getAttributeNamespace(i, curNsLen); 448 final String curAttr = getAttributeName(i, curAttrLen); 449 if (kDebugStringPoolNoisy) { 450 ALOGI(" curNs=%s (0x%x), curAttr=%s (0x%x)", 451 curNs /*String8(curNs, curNsLen).string()*/, curNsLen, 452 curAttr /*String8(curAttr, curAttrLen).string()*/, curAttrLen); 453 } 454 if (curAttr != null && curNsLen.get() == nsLen && curAttrLen.get() == attrLen 455 && (memcmp(attr, curAttr, attrLen*SIZEOF_CHAR/*sizeof(char16_t)*/) == 0)) { 456 if (ns == null) { 457 if (curNs == null) { 458 if (kDebugStringPoolNoisy) { 459 ALOGI(" FOUND!"); 460 } 461 return i; 462 } 463 } else if (curNs != null) { 464 //printf(" -. ns=%s, curNs=%s\n", 465 // String8(ns).string(), String8(curNs).string()); 466 if (memcmp(ns, curNs, nsLen*SIZEOF_CHAR/*sizeof(char16_t)*/) == 0) { 467 if (kDebugStringPoolNoisy) { 468 ALOGI(" FOUND!"); 469 } 470 return i; 471 } 472 } 473 } 474 } 475 } 476 } 477 478 return NAME_NOT_FOUND; 479 } 480 481 private int memcmp(String s1, String s2, int len) { 482 for (int i = 0; i < len; i++) { 483 int d = s1.charAt(i) - s2.charAt(i); 484 if (d != 0) { 485 return d; 486 } 487 } 488 return 0; 489 } 490 491 public int indexOfID() 492 { 493 if (mEventCode == START_TAG) { 494 final int idx = dtohs((new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt)).idIndex); 495 if (idx > 0) return (idx-1); 496 } 497 return NAME_NOT_FOUND; 498 } 499 500 public int indexOfClass() 501 { 502 if (mEventCode == START_TAG) { 503 final int idx = dtohs((new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt)).classIndex); 504 if (idx > 0) return (idx-1); 505 } 506 return NAME_NOT_FOUND; 507 } 508 509 public int indexOfStyle() 510 { 511 if (mEventCode == START_TAG) { 512 final int idx = dtohs((new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt)).styleIndex); 513 if (idx > 0) return (idx-1); 514 } 515 return NAME_NOT_FOUND; 516 } 517 518 int nextNode() { 519 if (mEventCode < 0) { 520 return mEventCode; 521 } 522 523 do { 524 int nextOffset = mCurNode.myOffset() + dtohl(mCurNode.header.size); 525 if (nextOffset >= mTree.mDataLen) { 526 mCurNode = null; 527 return (mEventCode=END_DOCUMENT); 528 } 529 530 // final ResXMLTree_node next = (ResXMLTree_node) 531 // (((final int8_t*)mCurNode) + dtohl(mCurNode.header.size)); 532 ResXMLTree_node next = new ResXMLTree_node(mTree.mBuffer.buf, nextOffset); 533 if (kDebugXMLNoisy) { 534 ALOGI("Next node: prev=%s, next=%s\n", mCurNode, next); 535 } 536 537 if (next.myOffset() >= mTree.mDataLen) { 538 mCurNode = null; 539 return (mEventCode=END_DOCUMENT); 540 } 541 542 if (mTree.validateNode(next) != NO_ERROR) { 543 mCurNode = null; 544 return (mEventCode=BAD_DOCUMENT); 545 } 546 547 mCurNode = next; 548 final int headerSize = dtohs(next.header.headerSize); 549 final int totalSize = dtohl(next.header.size); 550 mCurExt = next.myOffset() + headerSize; 551 int minExtSize = 0; 552 int eventCode = dtohs(next.header.type); 553 switch ((mEventCode=eventCode)) { 554 case RES_XML_START_NAMESPACE_TYPE: 555 case RES_XML_END_NAMESPACE_TYPE: 556 minExtSize = SIZEOF_RESXMLTREE_NAMESPACE_EXT /*sizeof(ResXMLTree_namespaceExt)*/; 557 break; 558 case RES_XML_START_ELEMENT_TYPE: 559 minExtSize = SIZEOF_RESXMLTREE_ATTR_EXT /*sizeof(ResXMLTree_attrExt)*/; 560 break; 561 case RES_XML_END_ELEMENT_TYPE: 562 minExtSize = ResXMLTree_endElementExt.SIZEOF /*sizeof(ResXMLTree_endElementExt)*/; 563 break; 564 case RES_XML_CDATA_TYPE: 565 minExtSize = SIZEOF_RESXMLTREE_CDATA_EXT /*sizeof(ResXMLTree_cdataExt)*/; 566 break; 567 default: 568 ALOGW("Unknown XML block: header type %d in node at %d\n", 569 (int)dtohs(next.header.type), 570 (next.myOffset()-mTree.mHeader.myOffset())); 571 continue; 572 } 573 574 if ((totalSize-headerSize) < minExtSize) { 575 ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", 576 (int)dtohs(next.header.type), 577 (next.myOffset()-mTree.mHeader.myOffset()), 578 (int)(totalSize-headerSize), (int)minExtSize); 579 return (mEventCode=BAD_DOCUMENT); 580 } 581 582 //printf("CurNode=%s, CurExt=%s, headerSize=%d, minExtSize=%d\n", 583 // mCurNode, mCurExt, headerSize, minExtSize); 584 585 return eventCode; 586 } while (true); 587 } 588 589 void getPosition(ResXMLPosition pos) 590 { 591 pos.eventCode = mEventCode; 592 pos.curNode = mCurNode; 593 pos.curExt = mCurExt; 594 } 595 596 void setPosition(final ResXMLPosition pos) 597 { 598 mEventCode = pos.eventCode; 599 mCurNode = pos.curNode; 600 mCurExt = pos.curExt; 601 } 602 603 static class ResXMLPosition 604 { 605 int eventCode; 606 ResXMLTree_node curNode; 607 int curExt; 608 }; 609 } 610