package android.content.res; import java.io.IOException; import android.content.res.ResourceTypes.*; import android.util.Errors; public class ResStringPool { private int mEntriesOffset = -1; // private int[] mEntries = null; private IntArray mEntries = null; private int mStringsOffset = -1; private ResData_pointer mStrings = null; private int mEntryStylesOffset = -1; private IntArray mEntryStyles = null; private int mStylesOffset = -1; private IntArray mStyles = null; @SuppressWarnings("unused") private int mStylePoolSize; @SuppressWarnings("unused") private int mStringPoolSize; private int mError = Errors.NO_INIT; private ResStringPool_header mHeader = null; @SuppressWarnings("unused") private int mSize = -1; public ResStringPool(byte[] data, int offset, int size, boolean copyData) throws IOException { setTo(data, offset, size, copyData); } public int setTo(byte[] data, int offset, int size, boolean copyData) throws IOException { if (data == null || size <= 0) return (mError = Errors.BAD_TYPE); uninit(); IntReader reader = new IntReader(data, offset, false); ResChunk_header chunk = new ResChunk_header(data, reader.getPosition(), reader.readInt(2), reader.readInt(2), reader.readInt()); ChunkUtil.checkType(chunk.type, ResourceTypes.RES_STRING_POOL_TYPE); mHeader = new ResStringPool_header(chunk, reader.readInt(), reader.readInt(), reader.readInt(), reader.readInt(), reader.readInt()); mSize = mHeader.header.size; mEntriesOffset = mHeader.header.pointer.offset + mHeader.header.headerSize; reader.setPosition(mEntriesOffset); if (mHeader.stringCount > 0) { // mEntries = reader.readIntArray(mHeader.stringCount); mEntries = new IntArray(reader.getData(), reader.getPosition(), mHeader.stringCount); mStringsOffset = mHeader.header.pointer.offset + mHeader.stringsStart; reader.setPosition(mStringsOffset); int readSize = ((mHeader.styleCount == 0) ? mHeader.header.size : mHeader.stylesStart) - mHeader.stringsStart; if ((readSize % 4) != 0) { throw new IOException("String data size is not multiple of 4 (" + readSize + ")."); } mStrings = new ResData_pointer(reader.getPosition(), data); // mStrings = reader.readIntArray(readSize / 4); } if (mHeader.styleCount > 0) { mEntryStylesOffset = mEntriesOffset + mHeader.stringCount; reader.setPosition(mEntryStylesOffset); mEntryStyles = new IntArray(reader.getData(), reader.getPosition(), mHeader.styleCount); mStylesOffset = mHeader.header.pointer.offset + mHeader.stylesStart; reader.setPosition(mStylesOffset); int readSize = (mHeader.header.size - mHeader.stylesStart); if ((readSize % 4) != 0) { throw new IOException("Style data size is not multiple of 4 (" + readSize + ")."); } mStyles = new IntArray(reader.getData(), reader.getPosition(), readSize); } return (mError = Errors.NO_ERROR); } /** * Returns number of strings in block. */ public int getCount() { return this.mHeader.stringCount; } /** * Returns raw string (without any styling information) at specified index. */ public String stringAt(int index) { if (index < 0 || this.mEntries == null || index >= this.mEntries.length) { return null; } boolean isUTF8 = (mHeader.flags & ResStringPool_header.UTF8_FLAG) != 0; // int offset = this.mEntries[index]; int offset = mEntries.getEntry(index); if (isUTF8 == true) { int length = getByte(mStrings, offset); offset += 1; // skip one StringBuilder result = new StringBuilder(length); for (; length != 0; length -= 1) { offset += 1; result.append((char) getByte(mStrings, offset)); } return result.toString(); } else { int length = getShort(mStrings, offset); StringBuilder result = new StringBuilder(length); for (; length != 0; length -= 1) { offset += 2; result.append((char) getShort(mStrings, offset)); } return result.toString(); } } /** * Not yet implemented. * * Returns string with style information (if any). */ public CharSequence get(int index) { return stringAt(index); } /** * Returns string with style tags (html-like). */ public String getHTML(int index) { String raw = stringAt(index); if (raw == null) { return raw; } int[] style = getStyle(index); if (style == null) { return raw; } StringBuilder html = new StringBuilder(raw.length() + 32); int offset = 0; while (true) { int i = -1; for (int j = 0; j != style.length; j += 3) { if (style[j + 1] == -1) { continue; } if (i == -1 || style[i + 1] > style[j + 1]) { i = j; } } int start = ((i != -1) ? style[i + 1] : raw.length()); for (int j = 0; j != style.length; j += 3) { int end = style[j + 2]; if (end == -1 || end >= start) { continue; } if (offset <= end) { html.append(raw, offset, end + 1); offset = end + 1; } style[j + 2] = -1; html.append('<'); html.append('/'); html.append(stringAt(style[j])); html.append('>'); } if (offset < start) { html.append(raw, offset, start); offset = start; } if (i == -1) { break; } html.append('<'); html.append(stringAt(style[i])); html.append('>'); style[i + 1] = -1; } return html.toString(); } /** * Finds index of the string. * Returns -1 if the string was not found. */ public int indexOfString(String string) { if (string == null) { return -1; } for (int i = 0; i != this.mEntries.length; ++i) { // int offset = this.mEntries[i]; int offset = mEntries.getEntry(i); int length = getShort(mStrings, offset); if (length != string.length()) { continue; } int j = 0; for (; j != length; ++j) { offset += 2; if (string.charAt(j) != getShort(mStrings, offset)) { break; } } if (j == length) { return i; } } return -1; } public int indexOfString(String str , int length) { int len; // TODO optimize searching for UTF-8 strings taking into account // the cache fill to determine when to convert the searched-for // string key to UTF-8. // It is unusual to get the ID from an unsorted string block... // most often this happens because we want to get IDs for style // span tags; since those always appear at the end of the string // block, start searching at the back. for (int i = mHeader.stringCount - 1; i >= 0; i--) { String s = stringAt(i); if (s != null && s.equals(str)) { return i; } } return -1; } ///////////////////////////////////////////// implementation public ResStringPool() { } /** * Returns style information - array of int triplets, where in each triplet: * * first int is index of tag name ('b','i', etc.) * second int is tag * start index in string * third int is tag end index in string */ public int[] getStyle(int index) { if (this.mEntryStyles == null || mStyles == null || index >= this.mEntryStyles.length) { return null; } int style[]; int startIndex = 0; int count = 0; for (int i = 0; i < mStyles.length; ++i) { if (mStyles.getEntry(i) == -1) { count += 1; if (count == index) { startIndex = i + 1; break; } } } count = 0; for (int i = startIndex; i < mStyles.length; ++i) { if (mStyles.getEntry(i) == -1) { break; } count += 1; } style = new int[count]; for (int i = startIndex, j = 0; i < mStyles.length;) { if (mStyles.getEntry(i) == -1) { break; } style[j++] = mStyles.getEntry(i++); } return style; } private static final int getShort(ResData_pointer pointer, int offset) { IntReader reader = new IntReader(pointer.data, pointer.offset, false); int pos = reader.getPosition() + offset; reader.setPosition(pos); try { int value = reader.readInt(); return (value & 0xFFFF); } catch (IOException e) { e.printStackTrace(); } return -1; } private static final int getByte(ResData_pointer pointer, int offset) { IntReader reader = new IntReader(pointer.data, pointer.offset, false); int pos = reader.getPosition() + offset; reader.setPosition(pos); try { int value = reader.readInt(); // int value = array[offset / 4]; // switch (pos % 4) { // case 0: return (value & 0xff); // case 1: // return (value & 0xff00) >>> 8; // case 2: // return (value & 0xff0000) >>> 16; // case 3: // return (value & 0xff000000) >>> 24; // } } catch (IOException e) { e.printStackTrace(); } return -1; } public void uninit() { mError = Errors.NO_INIT; } public int getError() { return mError; } public int size() { return (mError == Errors.NO_ERROR) ? mHeader.stringCount : 0; } }