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