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