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