Fix AXMLPrinter2 bug: java.lang.ArrayIndexOutOfBoundsException in StringBlock.getShort
This commit is contained in:
parent
1dd0664568
commit
a5680fd61c
|
|
@ -1,156 +1,159 @@
|
|||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package wind.android.content.res;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Simple helper class that allows reading of integers.
|
||||
*
|
||||
* TODO:
|
||||
* * implement buffering
|
||||
*
|
||||
*/
|
||||
public final class IntReader {
|
||||
|
||||
public IntReader() {
|
||||
}
|
||||
public IntReader(InputStream stream,boolean bigEndian) {
|
||||
reset(stream,bigEndian);
|
||||
}
|
||||
|
||||
public final void reset(InputStream stream,boolean bigEndian) {
|
||||
m_stream=stream;
|
||||
m_bigEndian=bigEndian;
|
||||
m_position=0;
|
||||
}
|
||||
|
||||
public final void close() {
|
||||
if (m_stream==null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m_stream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
reset(null,false);
|
||||
}
|
||||
|
||||
public final InputStream getStream() {
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
public final boolean isBigEndian() {
|
||||
return m_bigEndian;
|
||||
}
|
||||
public final void setBigEndian(boolean bigEndian) {
|
||||
m_bigEndian=bigEndian;
|
||||
}
|
||||
|
||||
public final int readByte() throws IOException {
|
||||
return readInt(1);
|
||||
}
|
||||
public final int readShort() throws IOException {
|
||||
return readInt(2);
|
||||
}
|
||||
public final int readInt() throws IOException {
|
||||
return readInt(4);
|
||||
}
|
||||
|
||||
public final int readInt(int length) throws IOException {
|
||||
if (length<0 || length>4) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int result=0;
|
||||
if (m_bigEndian) {
|
||||
for (int i=(length-1)*8;i>=0;i-=8) {
|
||||
int b=m_stream.read();
|
||||
if (b==-1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
m_position+=1;
|
||||
result|=(b<<i);
|
||||
}
|
||||
} else {
|
||||
length*=8;
|
||||
for (int i=0;i!=length;i+=8) {
|
||||
int b=m_stream.read();
|
||||
if (b==-1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
m_position+=1;
|
||||
result|=(b<<i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final int[] readIntArray(int length) throws IOException {
|
||||
int[] array=new int[length];
|
||||
readIntArray(array,0,length);
|
||||
return array;
|
||||
}
|
||||
|
||||
public final void readIntArray(int[] array,int offset,int length) throws IOException {
|
||||
for (;length>0;length-=1) {
|
||||
array[offset++]=readInt();
|
||||
}
|
||||
}
|
||||
|
||||
public final byte[] readByteArray(int length) throws IOException {
|
||||
byte[] array=new byte[length];
|
||||
int read=m_stream.read(array);
|
||||
m_position+=read;
|
||||
if (read!=length) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public final void skip(int bytes) throws IOException {
|
||||
if (bytes<=0) {
|
||||
return;
|
||||
}
|
||||
long skipped=m_stream.skip(bytes);
|
||||
m_position+=skipped;
|
||||
if (skipped!=bytes) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
public final void skipInt() throws IOException {
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public final int available() throws IOException {
|
||||
return m_stream.available();
|
||||
}
|
||||
|
||||
public final int getPosition() {
|
||||
return m_position;
|
||||
}
|
||||
|
||||
/////////////////////////////////// data
|
||||
|
||||
private InputStream m_stream;
|
||||
private boolean m_bigEndian;
|
||||
private int m_position;
|
||||
}
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package wind.android.content.res;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Simple helper class that allows reading of integers.
|
||||
*
|
||||
* TODO:
|
||||
* * implement buffering
|
||||
*
|
||||
*/
|
||||
public final class IntReader {
|
||||
|
||||
public IntReader() {
|
||||
}
|
||||
public IntReader(InputStream stream,boolean bigEndian) {
|
||||
reset(stream,bigEndian);
|
||||
}
|
||||
|
||||
public final void reset(InputStream stream,boolean bigEndian) {
|
||||
m_stream=stream;
|
||||
m_bigEndian=bigEndian;
|
||||
m_position=0;
|
||||
}
|
||||
|
||||
public final void close() {
|
||||
if (m_stream==null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m_stream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
reset(null,false);
|
||||
}
|
||||
|
||||
public final InputStream getStream() {
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
public final boolean isBigEndian() {
|
||||
return m_bigEndian;
|
||||
}
|
||||
public final void setBigEndian(boolean bigEndian) {
|
||||
m_bigEndian=bigEndian;
|
||||
}
|
||||
|
||||
public final int readByte() throws IOException {
|
||||
return readInt(1);
|
||||
}
|
||||
public final int readShort() throws IOException {
|
||||
return readInt(2);
|
||||
}
|
||||
public final int readInt() throws IOException {
|
||||
return readInt(4);
|
||||
}
|
||||
public final void readFully(byte[] b) throws IOException {
|
||||
new DataInputStream(m_stream).readFully(b);
|
||||
}
|
||||
public final int readInt(int length) throws IOException {
|
||||
if (length<0 || length>4) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int result=0;
|
||||
if (m_bigEndian) {
|
||||
for (int i=(length-1)*8;i>=0;i-=8) {
|
||||
int b=m_stream.read();
|
||||
if (b==-1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
m_position+=1;
|
||||
result|=(b<<i);
|
||||
}
|
||||
} else {
|
||||
length*=8;
|
||||
for (int i=0;i!=length;i+=8) {
|
||||
int b=m_stream.read();
|
||||
if (b==-1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
m_position+=1;
|
||||
result|=(b<<i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final int[] readIntArray(int length) throws IOException {
|
||||
int[] array=new int[length];
|
||||
readIntArray(array,0,length);
|
||||
return array;
|
||||
}
|
||||
|
||||
public final void readIntArray(int[] array,int offset,int length) throws IOException {
|
||||
for (;length>0;length-=1) {
|
||||
array[offset++]=readInt();
|
||||
}
|
||||
}
|
||||
|
||||
public final byte[] readByteArray(int length) throws IOException {
|
||||
byte[] array=new byte[length];
|
||||
int read=m_stream.read(array);
|
||||
m_position+=read;
|
||||
if (read!=length) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public final void skip(int bytes) throws IOException {
|
||||
if (bytes<=0) {
|
||||
return;
|
||||
}
|
||||
long skipped=m_stream.skip(bytes);
|
||||
m_position+=skipped;
|
||||
if (skipped!=bytes) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
public final void skipInt() throws IOException {
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public final int available() throws IOException {
|
||||
return m_stream.available();
|
||||
}
|
||||
|
||||
public final int getPosition() {
|
||||
return m_position;
|
||||
}
|
||||
|
||||
/////////////////////////////////// data
|
||||
|
||||
private InputStream m_stream;
|
||||
private boolean m_bigEndian;
|
||||
private int m_position;
|
||||
}
|
||||
|
|
|
|||
546
axmlprinter/src/main/java/wind/android/content/res/StringBlock.java
Normal file → Executable file
546
axmlprinter/src/main/java/wind/android/content/res/StringBlock.java
Normal file → Executable file
|
|
@ -1,248 +1,298 @@
|
|||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package wind.android.content.res;
|
||||
|
||||
import wind.android.content.res.ChunkUtil;
|
||||
import wind.android.content.res.IntReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Block of strings, used in binary xml and arsc.
|
||||
*
|
||||
* TODO:
|
||||
* - implement get()
|
||||
*
|
||||
*/
|
||||
public class StringBlock {
|
||||
|
||||
/**
|
||||
* Reads whole (including chunk type) string block from stream.
|
||||
* Stream must be at the chunk type.
|
||||
*/
|
||||
public static StringBlock read(IntReader reader) throws IOException {
|
||||
wind.android.content.res.ChunkUtil.readCheckType(reader,CHUNK_TYPE);
|
||||
int chunkSize=reader.readInt();
|
||||
int stringCount=reader.readInt();
|
||||
int styleOffsetCount=reader.readInt();
|
||||
/*?*/reader.readInt();
|
||||
int stringsOffset=reader.readInt();
|
||||
int stylesOffset=reader.readInt();
|
||||
|
||||
StringBlock block=new StringBlock();
|
||||
block.m_stringOffsets=reader.readIntArray(stringCount);
|
||||
if (styleOffsetCount!=0) {
|
||||
block.m_styleOffsets=reader.readIntArray(styleOffsetCount);
|
||||
}
|
||||
{
|
||||
int size=((stylesOffset==0)?chunkSize:stylesOffset)-stringsOffset;
|
||||
if ((size%4)!=0) {
|
||||
throw new IOException("String data size is not multiple of 4 ("+size+").");
|
||||
}
|
||||
block.m_strings=reader.readIntArray(size/4);
|
||||
}
|
||||
if (stylesOffset!=0) {
|
||||
int size=(chunkSize-stylesOffset);
|
||||
if ((size%4)!=0) {
|
||||
throw new IOException("Style data size is not multiple of 4 ("+size+").");
|
||||
}
|
||||
block.m_styles=reader.readIntArray(size/4);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of strings in block.
|
||||
*/
|
||||
public int getCount() {
|
||||
return m_stringOffsets!=null?
|
||||
m_stringOffsets.length:
|
||||
0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns raw string (without any styling information) at specified index.
|
||||
*/
|
||||
public String getString(int index) {
|
||||
if (index<0 ||
|
||||
m_stringOffsets==null ||
|
||||
index>=m_stringOffsets.length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int offset=m_stringOffsets[index];
|
||||
int length=getShort(m_strings,offset);
|
||||
StringBuilder result=new StringBuilder(length);
|
||||
for (;length!=0;length-=1) {
|
||||
offset+=2;
|
||||
result.append((char)getShort(m_strings,offset));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not yet implemented.
|
||||
*
|
||||
* Returns string with style information (if any).
|
||||
*/
|
||||
public CharSequence get(int index) {
|
||||
return getString(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string with style tags (html-like).
|
||||
*/
|
||||
public String getHTML(int index) {
|
||||
String raw=getString(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(getString(style[j]));
|
||||
html.append('>');
|
||||
}
|
||||
if (offset<start) {
|
||||
html.append(raw,offset,start);
|
||||
offset=start;
|
||||
}
|
||||
if (i==-1) {
|
||||
break;
|
||||
}
|
||||
html.append('<');
|
||||
html.append(getString(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 find(String string) {
|
||||
if (string==null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i=0;i!=m_stringOffsets.length;++i) {
|
||||
int offset=m_stringOffsets[i];
|
||||
int length=getShort(m_strings,offset);
|
||||
if (length!=string.length()) {
|
||||
continue;
|
||||
}
|
||||
int j=0;
|
||||
for (;j!=length;++j) {
|
||||
offset+=2;
|
||||
if (string.charAt(j)!=getShort(m_strings,offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j==length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// implementation
|
||||
|
||||
private StringBlock() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private int[] getStyle(int index) {
|
||||
if (m_styleOffsets==null || m_styles==null ||
|
||||
index>=m_styleOffsets.length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int offset=m_styleOffsets[index]/4;
|
||||
int style[];
|
||||
{
|
||||
int count=0;
|
||||
for (int i=offset;i<m_styles.length;++i) {
|
||||
if (m_styles[i]==-1) {
|
||||
break;
|
||||
}
|
||||
count+=1;
|
||||
}
|
||||
if (count==0 || (count%3)!=0) {
|
||||
return null;
|
||||
}
|
||||
style=new int[count];
|
||||
}
|
||||
for (int i=offset,j=0;i<m_styles.length;) {
|
||||
if (m_styles[i]==-1) {
|
||||
break;
|
||||
}
|
||||
style[j++]=m_styles[i++];
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
private static final int getShort(int[] array,int offset) {
|
||||
int value=array[offset/4];
|
||||
if ((offset%4)/2==0) {
|
||||
return (value & 0xFFFF);
|
||||
} else {
|
||||
return (value >>> 16);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] m_stringOffsets;
|
||||
private int[] m_strings;
|
||||
private int[] m_styleOffsets;
|
||||
private int[] m_styles;
|
||||
|
||||
private static final int CHUNK_TYPE=0x001C0001;
|
||||
}
|
||||
/*
|
||||
* Copyright 2008 Android4ME
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package wind.android.content.res;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Block of strings, used in binary xml and arsc.
|
||||
*
|
||||
* TODO:
|
||||
* - implement get()
|
||||
*
|
||||
*/
|
||||
public class StringBlock {
|
||||
|
||||
private int[] m_stringOffsets;
|
||||
private byte[] m_strings;
|
||||
private int[] m_styleOffsets;
|
||||
private int[] m_styles;
|
||||
private boolean m_isUTF8;
|
||||
private static final int CHUNK_TYPE = 0x001C0001;
|
||||
private static final int UTF8_FLAG = 0x00000100;
|
||||
|
||||
private final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8").newDecoder();
|
||||
private final CharsetDecoder UTF16LE_DECODER = Charset.forName("UTF-16LE").newDecoder();
|
||||
|
||||
/**
|
||||
* Reads whole (including chunk type) string block from stream.
|
||||
* Stream must be at the chunk type.
|
||||
*/
|
||||
public static StringBlock read(IntReader reader) throws IOException {
|
||||
ChunkUtil.readCheckType(reader, CHUNK_TYPE);
|
||||
int chunkSize = reader.readInt();
|
||||
int stringCount = reader.readInt();
|
||||
int styleOffsetCount = reader.readInt();
|
||||
int flags = reader.readInt();
|
||||
int stringsOffset = reader.readInt();
|
||||
int stylesOffset = reader.readInt();
|
||||
|
||||
StringBlock block = new StringBlock();
|
||||
block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
|
||||
block.m_stringOffsets = reader.readIntArray(stringCount);
|
||||
if (styleOffsetCount != 0) {
|
||||
block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
|
||||
}
|
||||
{
|
||||
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset;
|
||||
block.m_strings = new byte[size];
|
||||
reader.readFully(block.m_strings);
|
||||
}
|
||||
if (stylesOffset != 0) {
|
||||
int size = (chunkSize - stylesOffset);
|
||||
if ((size % 4) != 0) {
|
||||
throw new IOException("Style data size is not multiple of 4 (" + size + ").");
|
||||
}
|
||||
block.m_styles = reader.readIntArray(size / 4);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of strings in block.
|
||||
*/
|
||||
public int getCount() {
|
||||
return m_stringOffsets != null ?
|
||||
m_stringOffsets.length :
|
||||
0;
|
||||
}
|
||||
|
||||
|
||||
public String getString(int index) {
|
||||
if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) {
|
||||
return null;
|
||||
}
|
||||
int offset = m_stringOffsets[index];
|
||||
int length;
|
||||
if (m_isUTF8) {
|
||||
int[] val = getUtf8(m_strings, offset);
|
||||
offset = val[0];
|
||||
length = val[1];
|
||||
} else {
|
||||
int[] val = getUtf16(m_strings, offset);
|
||||
offset += val[0];
|
||||
length = val[1];
|
||||
}
|
||||
return decodeString(offset, length);
|
||||
}
|
||||
|
||||
private String decodeString(int offset, int length) {
|
||||
try {
|
||||
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(
|
||||
ByteBuffer.wrap(m_strings, offset, length)).toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int getShort(byte[] array, int offset) {
|
||||
return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
|
||||
}
|
||||
|
||||
private static final int[] getUtf8(byte[] array, int offset) {
|
||||
int val = array[offset];
|
||||
int length;
|
||||
if ((val & 0x80) != 0) {
|
||||
offset += 2;
|
||||
} else {
|
||||
offset += 1;
|
||||
}
|
||||
val = array[offset];
|
||||
if ((val & 0x80) != 0) {
|
||||
offset += 2;
|
||||
} else {
|
||||
offset += 1;
|
||||
}
|
||||
length = 0;
|
||||
while (array[offset + length] != 0) {
|
||||
length++;
|
||||
}
|
||||
return new int[]{offset, length};
|
||||
}
|
||||
|
||||
private static final int[] getUtf16(byte[] array, int offset) {
|
||||
int val = (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
|
||||
if (val == 0x8000) {
|
||||
int heigh = (array[offset + 3] & 0xFF) << 8;
|
||||
int low = (array[offset + 2] & 0xFF);
|
||||
return new int[]{4, (heigh + low) * 2};
|
||||
}
|
||||
return new int[]{2, val * 2};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Not yet implemented.
|
||||
* <p>
|
||||
* Returns string with style information (if any).
|
||||
*/
|
||||
public CharSequence get(int index) {
|
||||
return getString(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string with style tags (html-like).
|
||||
*/
|
||||
public String getHTML(int index) {
|
||||
String raw=getString(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(getString(style[j]));
|
||||
html.append('>');
|
||||
}
|
||||
if (offset<start) {
|
||||
html.append(raw,offset,start);
|
||||
offset=start;
|
||||
}
|
||||
if (i==-1) {
|
||||
break;
|
||||
}
|
||||
html.append('<');
|
||||
html.append(getString(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 find(String string) {
|
||||
if (string==null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i=0;i!=m_stringOffsets.length;++i) {
|
||||
int offset=m_stringOffsets[i];
|
||||
int length=getShort(m_strings,offset);
|
||||
if (length!=string.length()) {
|
||||
continue;
|
||||
}
|
||||
int j=0;
|
||||
for (;j!=length;++j) {
|
||||
offset+=2;
|
||||
if (string.charAt(j)!=getShort(m_strings,offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j==length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// implementation
|
||||
|
||||
private StringBlock() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private int[] getStyle(int index) {
|
||||
if (m_styleOffsets==null || m_styles==null ||
|
||||
index>=m_styleOffsets.length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int offset=m_styleOffsets[index]/4;
|
||||
int style[];
|
||||
{
|
||||
int count=0;
|
||||
for (int i=offset;i<m_styles.length;++i) {
|
||||
if (m_styles[i]==-1) {
|
||||
break;
|
||||
}
|
||||
count+=1;
|
||||
}
|
||||
if (count==0 || (count%3)!=0) {
|
||||
return null;
|
||||
}
|
||||
style=new int[count];
|
||||
}
|
||||
for (int i=offset,j=0;i<m_styles.length;) {
|
||||
if (m_styles[i]==-1) {
|
||||
break;
|
||||
}
|
||||
style[j++]=m_styles[i++];
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
private static final int getShort(int[] array,int offset) {
|
||||
int value=array[offset/4];
|
||||
if ((offset%4)/2==0) {
|
||||
return (value & 0xFFFF);
|
||||
} else {
|
||||
return (value >>> 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue