1 /* 2 * Copyright (C) 2006 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 android.content.res; 18 19 import static android.content.res.Resources.ID_NULL; 20 21 import android.annotation.AnyRes; 22 import android.annotation.Nullable; 23 import android.annotation.UnsupportedAppUsage; 24 import android.util.TypedValue; 25 26 import com.android.internal.util.XmlUtils; 27 28 import dalvik.annotation.optimization.FastNative; 29 30 import org.xmlpull.v1.XmlPullParserException; 31 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.Reader; 35 36 /** 37 * Wrapper around a compiled XML file. 38 * 39 * {@hide} 40 */ 41 final class XmlBlock implements AutoCloseable { 42 private static final boolean DEBUG=false; 43 44 @UnsupportedAppUsage XmlBlock(byte[] data)45 public XmlBlock(byte[] data) { 46 mAssets = null; 47 mNative = nativeCreate(data, 0, data.length); 48 mStrings = new StringBlock(nativeGetStringBlock(mNative), false); 49 } 50 XmlBlock(byte[] data, int offset, int size)51 public XmlBlock(byte[] data, int offset, int size) { 52 mAssets = null; 53 mNative = nativeCreate(data, offset, size); 54 mStrings = new StringBlock(nativeGetStringBlock(mNative), false); 55 } 56 57 @Override close()58 public void close() { 59 synchronized (this) { 60 if (mOpen) { 61 mOpen = false; 62 decOpenCountLocked(); 63 } 64 } 65 } 66 decOpenCountLocked()67 private void decOpenCountLocked() { 68 mOpenCount--; 69 if (mOpenCount == 0) { 70 nativeDestroy(mNative); 71 if (mAssets != null) { 72 mAssets.xmlBlockGone(hashCode()); 73 } 74 } 75 } 76 77 @UnsupportedAppUsage newParser()78 public XmlResourceParser newParser() { 79 return newParser(ID_NULL); 80 } 81 newParser(@nyRes int resId)82 public XmlResourceParser newParser(@AnyRes int resId) { 83 synchronized (this) { 84 if (mNative != 0) { 85 return new Parser(nativeCreateParseState(mNative, resId), this); 86 } 87 return null; 88 } 89 } 90 91 /*package*/ final class Parser implements XmlResourceParser { Parser(long parseState, XmlBlock block)92 Parser(long parseState, XmlBlock block) { 93 mParseState = parseState; 94 mBlock = block; 95 block.mOpenCount++; 96 } 97 98 @AnyRes getSourceResId()99 public int getSourceResId() { 100 return nativeGetSourceResId(mParseState); 101 } 102 setFeature(String name, boolean state)103 public void setFeature(String name, boolean state) throws XmlPullParserException { 104 if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) { 105 return; 106 } 107 if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) { 108 return; 109 } 110 throw new XmlPullParserException("Unsupported feature: " + name); 111 } getFeature(String name)112 public boolean getFeature(String name) { 113 if (FEATURE_PROCESS_NAMESPACES.equals(name)) { 114 return true; 115 } 116 if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) { 117 return true; 118 } 119 return false; 120 } setProperty(String name, Object value)121 public void setProperty(String name, Object value) throws XmlPullParserException { 122 throw new XmlPullParserException("setProperty() not supported"); 123 } getProperty(String name)124 public Object getProperty(String name) { 125 return null; 126 } setInput(Reader in)127 public void setInput(Reader in) throws XmlPullParserException { 128 throw new XmlPullParserException("setInput() not supported"); 129 } setInput(InputStream inputStream, String inputEncoding)130 public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException { 131 throw new XmlPullParserException("setInput() not supported"); 132 } defineEntityReplacementText(String entityName, String replacementText)133 public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { 134 throw new XmlPullParserException("defineEntityReplacementText() not supported"); 135 } getNamespacePrefix(int pos)136 public String getNamespacePrefix(int pos) throws XmlPullParserException { 137 throw new XmlPullParserException("getNamespacePrefix() not supported"); 138 } getInputEncoding()139 public String getInputEncoding() { 140 return null; 141 } getNamespace(String prefix)142 public String getNamespace(String prefix) { 143 throw new RuntimeException("getNamespace() not supported"); 144 } getNamespaceCount(int depth)145 public int getNamespaceCount(int depth) throws XmlPullParserException { 146 throw new XmlPullParserException("getNamespaceCount() not supported"); 147 } getPositionDescription()148 public String getPositionDescription() { 149 return "Binary XML file line #" + getLineNumber(); 150 } getNamespaceUri(int pos)151 public String getNamespaceUri(int pos) throws XmlPullParserException { 152 throw new XmlPullParserException("getNamespaceUri() not supported"); 153 } getColumnNumber()154 public int getColumnNumber() { 155 return -1; 156 } getDepth()157 public int getDepth() { 158 return mDepth; 159 } getText()160 public String getText() { 161 int id = nativeGetText(mParseState); 162 return id >= 0 ? mStrings.get(id).toString() : null; 163 } getLineNumber()164 public int getLineNumber() { 165 return nativeGetLineNumber(mParseState); 166 } getEventType()167 public int getEventType() throws XmlPullParserException { 168 return mEventType; 169 } isWhitespace()170 public boolean isWhitespace() throws XmlPullParserException { 171 // whitespace was stripped by aapt. 172 return false; 173 } getPrefix()174 public String getPrefix() { 175 throw new RuntimeException("getPrefix not supported"); 176 } getTextCharacters(int[] holderForStartAndLength)177 public char[] getTextCharacters(int[] holderForStartAndLength) { 178 String txt = getText(); 179 char[] chars = null; 180 if (txt != null) { 181 holderForStartAndLength[0] = 0; 182 holderForStartAndLength[1] = txt.length(); 183 chars = new char[txt.length()]; 184 txt.getChars(0, txt.length(), chars, 0); 185 } 186 return chars; 187 } getNamespace()188 public String getNamespace() { 189 int id = nativeGetNamespace(mParseState); 190 return id >= 0 ? mStrings.get(id).toString() : ""; 191 } getName()192 public String getName() { 193 int id = nativeGetName(mParseState); 194 return id >= 0 ? mStrings.get(id).toString() : null; 195 } getAttributeNamespace(int index)196 public String getAttributeNamespace(int index) { 197 int id = nativeGetAttributeNamespace(mParseState, index); 198 if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id); 199 if (id >= 0) return mStrings.get(id).toString(); 200 else if (id == -1) return ""; 201 throw new IndexOutOfBoundsException(String.valueOf(index)); 202 } getAttributeName(int index)203 public String getAttributeName(int index) { 204 int id = nativeGetAttributeName(mParseState, index); 205 if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id); 206 if (id >= 0) return mStrings.get(id).toString(); 207 throw new IndexOutOfBoundsException(String.valueOf(index)); 208 } getAttributePrefix(int index)209 public String getAttributePrefix(int index) { 210 throw new RuntimeException("getAttributePrefix not supported"); 211 } isEmptyElementTag()212 public boolean isEmptyElementTag() throws XmlPullParserException { 213 // XXX Need to detect this. 214 return false; 215 } getAttributeCount()216 public int getAttributeCount() { 217 return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1; 218 } getAttributeValue(int index)219 public String getAttributeValue(int index) { 220 int id = nativeGetAttributeStringValue(mParseState, index); 221 if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id); 222 if (id >= 0) return mStrings.get(id).toString(); 223 224 // May be some other type... check and try to convert if so. 225 int t = nativeGetAttributeDataType(mParseState, index); 226 if (t == TypedValue.TYPE_NULL) { 227 throw new IndexOutOfBoundsException(String.valueOf(index)); 228 } 229 230 int v = nativeGetAttributeData(mParseState, index); 231 return TypedValue.coerceToString(t, v); 232 } getAttributeType(int index)233 public String getAttributeType(int index) { 234 return "CDATA"; 235 } isAttributeDefault(int index)236 public boolean isAttributeDefault(int index) { 237 return false; 238 } nextToken()239 public int nextToken() throws XmlPullParserException,IOException { 240 return next(); 241 } getAttributeValue(String namespace, String name)242 public String getAttributeValue(String namespace, String name) { 243 int idx = nativeGetAttributeIndex(mParseState, namespace, name); 244 if (idx >= 0) { 245 if (DEBUG) System.out.println("getAttributeName of " 246 + namespace + ":" + name + " index = " + idx); 247 if (DEBUG) System.out.println( 248 "Namespace=" + getAttributeNamespace(idx) 249 + "Name=" + getAttributeName(idx) 250 + ", Value=" + getAttributeValue(idx)); 251 return getAttributeValue(idx); 252 } 253 return null; 254 } next()255 public int next() throws XmlPullParserException,IOException { 256 if (!mStarted) { 257 mStarted = true; 258 return START_DOCUMENT; 259 } 260 if (mParseState == 0) { 261 return END_DOCUMENT; 262 } 263 int ev = nativeNext(mParseState); 264 if (mDecNextDepth) { 265 mDepth--; 266 mDecNextDepth = false; 267 } 268 switch (ev) { 269 case START_TAG: 270 mDepth++; 271 break; 272 case END_TAG: 273 mDecNextDepth = true; 274 break; 275 } 276 mEventType = ev; 277 if (ev == END_DOCUMENT) { 278 // Automatically close the parse when we reach the end of 279 // a document, since the standard XmlPullParser interface 280 // doesn't have such an API so most clients will leave us 281 // dangling. 282 close(); 283 } 284 return ev; 285 } require(int type, String namespace, String name)286 public void require(int type, String namespace, String name) throws XmlPullParserException,IOException { 287 if (type != getEventType() 288 || (namespace != null && !namespace.equals( getNamespace () ) ) 289 || (name != null && !name.equals( getName() ) ) ) 290 throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription()); 291 } nextText()292 public String nextText() throws XmlPullParserException,IOException { 293 if(getEventType() != START_TAG) { 294 throw new XmlPullParserException( 295 getPositionDescription() 296 + ": parser must be on START_TAG to read next text", this, null); 297 } 298 int eventType = next(); 299 if(eventType == TEXT) { 300 String result = getText(); 301 eventType = next(); 302 if(eventType != END_TAG) { 303 throw new XmlPullParserException( 304 getPositionDescription() 305 + ": event TEXT it must be immediately followed by END_TAG", this, null); 306 } 307 return result; 308 } else if(eventType == END_TAG) { 309 return ""; 310 } else { 311 throw new XmlPullParserException( 312 getPositionDescription() 313 + ": parser must be on START_TAG or TEXT to read text", this, null); 314 } 315 } nextTag()316 public int nextTag() throws XmlPullParserException,IOException { 317 int eventType = next(); 318 if(eventType == TEXT && isWhitespace()) { // skip whitespace 319 eventType = next(); 320 } 321 if (eventType != START_TAG && eventType != END_TAG) { 322 throw new XmlPullParserException( 323 getPositionDescription() 324 + ": expected start or end tag", this, null); 325 } 326 return eventType; 327 } 328 getAttributeNameResource(int index)329 public int getAttributeNameResource(int index) { 330 return nativeGetAttributeResource(mParseState, index); 331 } 332 getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue)333 public int getAttributeListValue(String namespace, String attribute, 334 String[] options, int defaultValue) { 335 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute); 336 if (idx >= 0) { 337 return getAttributeListValue(idx, options, defaultValue); 338 } 339 return defaultValue; 340 } getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue)341 public boolean getAttributeBooleanValue(String namespace, String attribute, 342 boolean defaultValue) { 343 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute); 344 if (idx >= 0) { 345 return getAttributeBooleanValue(idx, defaultValue); 346 } 347 return defaultValue; 348 } getAttributeResourceValue(String namespace, String attribute, int defaultValue)349 public int getAttributeResourceValue(String namespace, String attribute, 350 int defaultValue) { 351 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute); 352 if (idx >= 0) { 353 return getAttributeResourceValue(idx, defaultValue); 354 } 355 return defaultValue; 356 } getAttributeIntValue(String namespace, String attribute, int defaultValue)357 public int getAttributeIntValue(String namespace, String attribute, 358 int defaultValue) { 359 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute); 360 if (idx >= 0) { 361 return getAttributeIntValue(idx, defaultValue); 362 } 363 return defaultValue; 364 } getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue)365 public int getAttributeUnsignedIntValue(String namespace, String attribute, 366 int defaultValue) 367 { 368 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute); 369 if (idx >= 0) { 370 return getAttributeUnsignedIntValue(idx, defaultValue); 371 } 372 return defaultValue; 373 } getAttributeFloatValue(String namespace, String attribute, float defaultValue)374 public float getAttributeFloatValue(String namespace, String attribute, 375 float defaultValue) { 376 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute); 377 if (idx >= 0) { 378 return getAttributeFloatValue(idx, defaultValue); 379 } 380 return defaultValue; 381 } 382 getAttributeListValue(int idx, String[] options, int defaultValue)383 public int getAttributeListValue(int idx, 384 String[] options, int defaultValue) { 385 int t = nativeGetAttributeDataType(mParseState, idx); 386 int v = nativeGetAttributeData(mParseState, idx); 387 if (t == TypedValue.TYPE_STRING) { 388 return XmlUtils.convertValueToList( 389 mStrings.get(v), options, defaultValue); 390 } 391 return v; 392 } getAttributeBooleanValue(int idx, boolean defaultValue)393 public boolean getAttributeBooleanValue(int idx, 394 boolean defaultValue) { 395 int t = nativeGetAttributeDataType(mParseState, idx); 396 // Note: don't attempt to convert any other types, because 397 // we want to count on aapt doing the conversion for us. 398 if (t >= TypedValue.TYPE_FIRST_INT && 399 t <= TypedValue.TYPE_LAST_INT) { 400 return nativeGetAttributeData(mParseState, idx) != 0; 401 } 402 return defaultValue; 403 } getAttributeResourceValue(int idx, int defaultValue)404 public int getAttributeResourceValue(int idx, int defaultValue) { 405 int t = nativeGetAttributeDataType(mParseState, idx); 406 // Note: don't attempt to convert any other types, because 407 // we want to count on aapt doing the conversion for us. 408 if (t == TypedValue.TYPE_REFERENCE) { 409 return nativeGetAttributeData(mParseState, idx); 410 } 411 return defaultValue; 412 } getAttributeIntValue(int idx, int defaultValue)413 public int getAttributeIntValue(int idx, int defaultValue) { 414 int t = nativeGetAttributeDataType(mParseState, idx); 415 // Note: don't attempt to convert any other types, because 416 // we want to count on aapt doing the conversion for us. 417 if (t >= TypedValue.TYPE_FIRST_INT && 418 t <= TypedValue.TYPE_LAST_INT) { 419 return nativeGetAttributeData(mParseState, idx); 420 } 421 return defaultValue; 422 } getAttributeUnsignedIntValue(int idx, int defaultValue)423 public int getAttributeUnsignedIntValue(int idx, int defaultValue) { 424 int t = nativeGetAttributeDataType(mParseState, idx); 425 // Note: don't attempt to convert any other types, because 426 // we want to count on aapt doing the conversion for us. 427 if (t >= TypedValue.TYPE_FIRST_INT && 428 t <= TypedValue.TYPE_LAST_INT) { 429 return nativeGetAttributeData(mParseState, idx); 430 } 431 return defaultValue; 432 } getAttributeFloatValue(int idx, float defaultValue)433 public float getAttributeFloatValue(int idx, float defaultValue) { 434 int t = nativeGetAttributeDataType(mParseState, idx); 435 // Note: don't attempt to convert any other types, because 436 // we want to count on aapt doing the conversion for us. 437 if (t == TypedValue.TYPE_FLOAT) { 438 return Float.intBitsToFloat( 439 nativeGetAttributeData(mParseState, idx)); 440 } 441 throw new RuntimeException("not a float!"); 442 } 443 getIdAttribute()444 public String getIdAttribute() { 445 int id = nativeGetIdAttribute(mParseState); 446 return id >= 0 ? mStrings.get(id).toString() : null; 447 } getClassAttribute()448 public String getClassAttribute() { 449 int id = nativeGetClassAttribute(mParseState); 450 return id >= 0 ? mStrings.get(id).toString() : null; 451 } 452 getIdAttributeResourceValue(int defaultValue)453 public int getIdAttributeResourceValue(int defaultValue) { 454 //todo: create and use native method 455 return getAttributeResourceValue(null, "id", defaultValue); 456 } 457 getStyleAttribute()458 public int getStyleAttribute() { 459 return nativeGetStyleAttribute(mParseState); 460 } 461 close()462 public void close() { 463 synchronized (mBlock) { 464 if (mParseState != 0) { 465 nativeDestroyParseState(mParseState); 466 mParseState = 0; 467 mBlock.decOpenCountLocked(); 468 } 469 } 470 } 471 finalize()472 protected void finalize() throws Throwable { 473 close(); 474 } 475 getPooledString(int id)476 /*package*/ final CharSequence getPooledString(int id) { 477 return mStrings.get(id); 478 } 479 480 @UnsupportedAppUsage 481 /*package*/ long mParseState; 482 @UnsupportedAppUsage 483 private final XmlBlock mBlock; 484 private boolean mStarted = false; 485 private boolean mDecNextDepth = false; 486 private int mDepth = 0; 487 private int mEventType = START_DOCUMENT; 488 } 489 finalize()490 protected void finalize() throws Throwable { 491 close(); 492 } 493 494 /** 495 * Create from an existing xml block native object. This is 496 * -extremely- dangerous -- only use it if you absolutely know what you 497 * are doing! The given native object must exist for the entire lifetime 498 * of this newly creating XmlBlock. 499 */ XmlBlock(@ullable AssetManager assets, long xmlBlock)500 XmlBlock(@Nullable AssetManager assets, long xmlBlock) { 501 mAssets = assets; 502 mNative = xmlBlock; 503 mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); 504 } 505 506 private @Nullable final AssetManager mAssets; 507 private final long mNative; 508 /*package*/ final StringBlock mStrings; 509 private boolean mOpen = true; 510 private int mOpenCount = 1; 511 nativeCreate(byte[] data, int offset, int size)512 private static final native long nativeCreate(byte[] data, 513 int offset, 514 int size); nativeGetStringBlock(long obj)515 private static final native long nativeGetStringBlock(long obj); nativeCreateParseState(long obj, int resId)516 private static final native long nativeCreateParseState(long obj, int resId); nativeDestroyParseState(long state)517 private static final native void nativeDestroyParseState(long state); nativeDestroy(long obj)518 private static final native void nativeDestroy(long obj); 519 520 // ----------- @FastNative ------------------ 521 522 @FastNative nativeNext(long state)523 /*package*/ static final native int nativeNext(long state); 524 @FastNative nativeGetNamespace(long state)525 private static final native int nativeGetNamespace(long state); 526 @FastNative nativeGetName(long state)527 /*package*/ static final native int nativeGetName(long state); 528 @FastNative nativeGetText(long state)529 private static final native int nativeGetText(long state); 530 @FastNative nativeGetLineNumber(long state)531 private static final native int nativeGetLineNumber(long state); 532 @FastNative nativeGetAttributeCount(long state)533 private static final native int nativeGetAttributeCount(long state); 534 @FastNative nativeGetAttributeNamespace(long state, int idx)535 private static final native int nativeGetAttributeNamespace(long state, int idx); 536 @FastNative nativeGetAttributeName(long state, int idx)537 private static final native int nativeGetAttributeName(long state, int idx); 538 @FastNative nativeGetAttributeResource(long state, int idx)539 private static final native int nativeGetAttributeResource(long state, int idx); 540 @FastNative nativeGetAttributeDataType(long state, int idx)541 private static final native int nativeGetAttributeDataType(long state, int idx); 542 @FastNative nativeGetAttributeData(long state, int idx)543 private static final native int nativeGetAttributeData(long state, int idx); 544 @FastNative nativeGetAttributeStringValue(long state, int idx)545 private static final native int nativeGetAttributeStringValue(long state, int idx); 546 @FastNative nativeGetIdAttribute(long state)547 private static final native int nativeGetIdAttribute(long state); 548 @FastNative nativeGetClassAttribute(long state)549 private static final native int nativeGetClassAttribute(long state); 550 @FastNative nativeGetStyleAttribute(long state)551 private static final native int nativeGetStyleAttribute(long state); 552 @FastNative nativeGetAttributeIndex(long state, String namespace, String name)553 private static final native int nativeGetAttributeIndex(long state, String namespace, String name); 554 @FastNative nativeGetSourceResId(long state)555 private static final native int nativeGetSourceResId(long state); 556 } 557