mirror of
https://github.com/revanced/Apktool.git
synced 2024-12-05 02:22:55 +01:00
+AXmlResourceParser +StringBlock
These are tools from android4me project.
This commit is contained in:
parent
9afccd60b3
commit
f68d809ea9
927
src/brut/androlib/res/decoder/AXmlResourceParser.java
Normal file
927
src/brut/androlib/res/decoder/AXmlResourceParser.java
Normal file
@ -0,0 +1,927 @@
|
||||
/*
|
||||
* 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 brut.androlib.res.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import android.util.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Dmitry Skiba
|
||||
*
|
||||
* Binary xml files parser.
|
||||
*
|
||||
* Parser has only two states:
|
||||
* (1) Operational state, which parser obtains after first successful call
|
||||
* to next() and retains until open(), close(), or failed call to next().
|
||||
* (2) Closed state, which parser obtains after open(), close(), or failed
|
||||
* call to next(). In this state methods return invalid values or throw exceptions.
|
||||
*
|
||||
* TODO:
|
||||
* * check all methods in closed state
|
||||
*
|
||||
*/
|
||||
public class AXmlResourceParser implements XmlResourceParser {
|
||||
|
||||
public AXmlResourceParser() {
|
||||
resetEventInfo();
|
||||
}
|
||||
|
||||
public void open(InputStream stream) {
|
||||
close();
|
||||
if (stream!=null) {
|
||||
m_reader=new IntReader(stream,false);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (!m_operational) {
|
||||
return;
|
||||
}
|
||||
m_operational=false;
|
||||
m_reader.close();
|
||||
m_reader=null;
|
||||
m_strings=null;
|
||||
m_resourceIDs=null;
|
||||
m_namespaces.reset();
|
||||
resetEventInfo();
|
||||
}
|
||||
|
||||
/////////////////////////////////// iteration
|
||||
|
||||
public int next() throws XmlPullParserException,IOException {
|
||||
if (m_reader==null) {
|
||||
throw new XmlPullParserException("Parser is not opened.",this,null);
|
||||
}
|
||||
try {
|
||||
doNext();
|
||||
return m_event;
|
||||
}
|
||||
catch (IOException e) {
|
||||
close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public int nextToken() throws XmlPullParserException,IOException {
|
||||
return next();
|
||||
}
|
||||
|
||||
public int nextTag() throws XmlPullParserException,IOException {
|
||||
int eventType=next();
|
||||
if (eventType==TEXT && isWhitespace()) {
|
||||
eventType=next();
|
||||
}
|
||||
if (eventType!=START_TAG && eventType!=END_TAG) {
|
||||
throw new XmlPullParserException("Expected start or end tag.",this,null);
|
||||
}
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public String nextText() throws XmlPullParserException,IOException {
|
||||
if(getEventType()!=START_TAG) {
|
||||
throw new XmlPullParserException("Parser must be on START_TAG to read next text.",this,null);
|
||||
}
|
||||
int eventType=next();
|
||||
if (eventType==TEXT) {
|
||||
String result=getText();
|
||||
eventType=next();
|
||||
if (eventType!=END_TAG) {
|
||||
throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.",this,null);
|
||||
}
|
||||
return result;
|
||||
} else if (eventType==END_TAG) {
|
||||
return "";
|
||||
} else {
|
||||
throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.",this,null);
|
||||
}
|
||||
}
|
||||
|
||||
public void require(int type,String namespace,String name) throws XmlPullParserException,IOException {
|
||||
if (type!=getEventType() ||
|
||||
(namespace!=null && !namespace.equals(getNamespace())) ||
|
||||
(name!=null && !name.equals(getName())))
|
||||
{
|
||||
throw new XmlPullParserException(TYPES[type]+" is expected.",this,null);
|
||||
}
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return m_namespaces.getDepth()-1;
|
||||
}
|
||||
|
||||
public int getEventType() throws XmlPullParserException {
|
||||
return m_event;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return m_lineNumber;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (m_name==-1 || (m_event!=START_TAG && m_event!=END_TAG)) {
|
||||
return null;
|
||||
}
|
||||
return m_strings.getString(m_name);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
if (m_name==-1 || m_event!=TEXT) {
|
||||
return null;
|
||||
}
|
||||
return m_strings.getString(m_name);
|
||||
}
|
||||
|
||||
public char[] getTextCharacters(int[] holderForStartAndLength) {
|
||||
String text=getText();
|
||||
if (text==null) {
|
||||
return null;
|
||||
}
|
||||
holderForStartAndLength[0]=0;
|
||||
holderForStartAndLength[1]=text.length();
|
||||
char[] chars=new char[text.length()];
|
||||
text.getChars(0,text.length(),chars,0);
|
||||
return chars;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return m_strings.getString(m_namespaceUri);
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
int prefix=m_namespaces.findPrefix(m_namespaceUri);
|
||||
return m_strings.getString(prefix);
|
||||
}
|
||||
|
||||
public String getPositionDescription() {
|
||||
return "XML line #"+getLineNumber();
|
||||
}
|
||||
|
||||
public int getNamespaceCount(int depth) throws XmlPullParserException {
|
||||
return m_namespaces.getAccumulatedCount(depth);
|
||||
}
|
||||
|
||||
public String getNamespacePrefix(int pos) throws XmlPullParserException {
|
||||
int prefix=m_namespaces.getPrefix(pos);
|
||||
return m_strings.getString(prefix);
|
||||
}
|
||||
|
||||
public String getNamespaceUri(int pos) throws XmlPullParserException {
|
||||
int uri=m_namespaces.getUri(pos);
|
||||
return m_strings.getString(uri);
|
||||
}
|
||||
|
||||
/////////////////////////////////// attributes
|
||||
|
||||
public String getClassAttribute() {
|
||||
if (m_classAttribute==-1) {
|
||||
return null;
|
||||
}
|
||||
int offset=getAttributeOffset(m_classAttribute);
|
||||
int value=m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING];
|
||||
return m_strings.getString(value);
|
||||
}
|
||||
|
||||
public String getIdAttribute() {
|
||||
if (m_idAttribute==-1) {
|
||||
return null;
|
||||
}
|
||||
int offset=getAttributeOffset(m_idAttribute);
|
||||
int value=m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING];
|
||||
return m_strings.getString(value);
|
||||
}
|
||||
|
||||
public int getIdAttributeResourceValue(int defaultValue) {
|
||||
if (m_idAttribute==-1) {
|
||||
return defaultValue;
|
||||
}
|
||||
int offset=getAttributeOffset(m_idAttribute);
|
||||
int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType!=TypedValue.TYPE_REFERENCE) {
|
||||
return defaultValue;
|
||||
}
|
||||
return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
|
||||
public int getStyleAttribute() {
|
||||
if (m_styleAttribute==-1) {
|
||||
return 0;
|
||||
}
|
||||
int offset=getAttributeOffset(m_styleAttribute);
|
||||
return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
|
||||
public int getAttributeCount() {
|
||||
if (m_event!=START_TAG) {
|
||||
return -1;
|
||||
}
|
||||
return m_attributes.length/ATTRIBUTE_LENGHT;
|
||||
}
|
||||
|
||||
public String getAttributeNamespace(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int namespace=m_attributes[offset+ATTRIBUTE_IX_NAMESPACE_URI];
|
||||
if (namespace==-1) {
|
||||
return "";
|
||||
}
|
||||
return m_strings.getString(namespace);
|
||||
}
|
||||
|
||||
public String getAttributePrefix(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int uri=m_attributes[offset+ATTRIBUTE_IX_NAMESPACE_URI];
|
||||
int prefix=m_namespaces.findPrefix(uri);
|
||||
if (prefix==-1) {
|
||||
return "";
|
||||
}
|
||||
return m_strings.getString(prefix);
|
||||
}
|
||||
|
||||
public String getAttributeName(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int name=m_attributes[offset+ATTRIBUTE_IX_NAME];
|
||||
if (name==-1) {
|
||||
return "";
|
||||
}
|
||||
return m_strings.getString(name);
|
||||
}
|
||||
|
||||
public int getAttributeNameResource(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int name=m_attributes[offset+ATTRIBUTE_IX_NAME];
|
||||
if (m_resourceIDs==null ||
|
||||
name<0 || name>=m_resourceIDs.length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return m_resourceIDs[name];
|
||||
}
|
||||
|
||||
public int getAttributeValueType(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
return m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
}
|
||||
|
||||
public int getAttributeValueData(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
|
||||
public String getAttributeValue(int index) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType==TypedValue.TYPE_STRING) {
|
||||
int valueString=m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING];
|
||||
return m_strings.getString(valueString);
|
||||
}
|
||||
int valueData=m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
return "";//TypedValue.coerceToString(valueType,valueData);
|
||||
}
|
||||
|
||||
public boolean getAttributeBooleanValue(int index,boolean defaultValue) {
|
||||
return getAttributeIntValue(index,defaultValue?1:0)!=0;
|
||||
}
|
||||
|
||||
public float getAttributeFloatValue(int index,float defaultValue) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType==TypedValue.TYPE_FLOAT) {
|
||||
int valueData=m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
return Float.intBitsToFloat(valueData);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public int getAttributeIntValue(int index,int defaultValue) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType>=TypedValue.TYPE_FIRST_INT &&
|
||||
valueType<=TypedValue.TYPE_LAST_INT)
|
||||
{
|
||||
return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public int getAttributeUnsignedIntValue(int index,int defaultValue) {
|
||||
return getAttributeIntValue(index,defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeResourceValue(int index,int defaultValue) {
|
||||
int offset=getAttributeOffset(index);
|
||||
int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
if (valueType==TypedValue.TYPE_REFERENCE) {
|
||||
return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getAttributeValue(String namespace,String attribute) {
|
||||
int index=findAttribute(namespace,attribute);
|
||||
if (index==-1) {
|
||||
return null;
|
||||
}
|
||||
return getAttributeValue(index);
|
||||
}
|
||||
|
||||
public boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue) {
|
||||
int index=findAttribute(namespace,attribute);
|
||||
if (index==-1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeBooleanValue(index,defaultValue);
|
||||
}
|
||||
|
||||
public float getAttributeFloatValue(String namespace,String attribute,float defaultValue) {
|
||||
int index=findAttribute(namespace,attribute);
|
||||
if (index==-1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeFloatValue(index,defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeIntValue(String namespace,String attribute,int defaultValue) {
|
||||
int index=findAttribute(namespace,attribute);
|
||||
if (index==-1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeIntValue(index,defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue) {
|
||||
int index=findAttribute(namespace,attribute);
|
||||
if (index==-1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeUnsignedIntValue(index,defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeResourceValue(String namespace,String attribute,int defaultValue) {
|
||||
int index=findAttribute(namespace,attribute);
|
||||
if (index==-1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getAttributeResourceValue(index,defaultValue);
|
||||
}
|
||||
|
||||
public int getAttributeListValue(int index,String[] options,int defaultValue) {
|
||||
// TODO implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getAttributeListValue(String namespace,String attribute,String[] options,int defaultValue) {
|
||||
// TODO implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getAttributeType(int index) {
|
||||
return "CDATA";
|
||||
}
|
||||
|
||||
public boolean isAttributeDefault(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////////////////// dummies
|
||||
|
||||
public void setInput(InputStream stream,String inputEncoding) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
public void setInput(Reader reader) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public String getInputEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getColumnNumber() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean isEmptyElementTag() throws XmlPullParserException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isWhitespace() throws XmlPullParserException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void defineEntityReplacementText(String entityName,String replacementText) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public String getNamespace(String prefix) {
|
||||
throw new RuntimeException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public Object getProperty(String name) {
|
||||
return null;
|
||||
}
|
||||
public void setProperty(String name,Object value) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
public boolean getFeature(String feature) {
|
||||
return false;
|
||||
}
|
||||
public void setFeature(String name,boolean value) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// implementation
|
||||
|
||||
/**
|
||||
* Namespace stack, holds prefix+uri pairs, as well as
|
||||
* depth information.
|
||||
* All information is stored in one int[] array.
|
||||
* Array consists of depth frames:
|
||||
* Data=DepthFrame*;
|
||||
* DepthFrame=Count+[Prefix+Uri]*+Count;
|
||||
* Count='count of Prefix+Uri pairs';
|
||||
* Yes, count is stored twice, to enable bottom-up traversal.
|
||||
* increaseDepth adds depth frame, decreaseDepth removes it.
|
||||
* push/pop operations operate only in current depth frame.
|
||||
* decreaseDepth removes any remaining (not pop'ed) namespace pairs.
|
||||
* findXXX methods search all depth frames starting
|
||||
* from the last namespace pair of current depth frame.
|
||||
* All functions that operate with int, use -1 as 'invalid value'.
|
||||
*
|
||||
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
|
||||
*
|
||||
*/
|
||||
private static final class NamespaceStack {
|
||||
public NamespaceStack() {
|
||||
m_data=new int[32];
|
||||
}
|
||||
|
||||
public final void reset() {
|
||||
m_dataLength=0;
|
||||
m_count=0;
|
||||
m_depth=0;
|
||||
}
|
||||
|
||||
public final int getTotalCount() {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
public final int getCurrentCount() {
|
||||
if (m_dataLength==0) {
|
||||
return 0;
|
||||
}
|
||||
int offset=m_dataLength-1;
|
||||
return m_data[offset];
|
||||
}
|
||||
|
||||
public final int getAccumulatedCount(int depth) {
|
||||
if (m_dataLength==0 || depth<0) {
|
||||
return 0;
|
||||
}
|
||||
if (depth>m_depth) {
|
||||
depth=m_depth;
|
||||
}
|
||||
int accumulatedCount=0;
|
||||
int offset=0;
|
||||
for (;depth!=0;--depth) {
|
||||
int count=m_data[offset];
|
||||
accumulatedCount+=count;
|
||||
offset+=(2+count*2);
|
||||
}
|
||||
return accumulatedCount;
|
||||
}
|
||||
|
||||
public final void push(int prefix,int uri) {
|
||||
if (m_depth==0) {
|
||||
increaseDepth();
|
||||
}
|
||||
ensureDataCapacity(2);
|
||||
int offset=m_dataLength-1;
|
||||
int count=m_data[offset];
|
||||
m_data[offset-1-count*2]=count+1;
|
||||
m_data[offset]=prefix;
|
||||
m_data[offset+1]=uri;
|
||||
m_data[offset+2]=count+1;
|
||||
m_dataLength+=2;
|
||||
m_count+=1;
|
||||
}
|
||||
|
||||
public final boolean pop(int prefix,int uri) {
|
||||
if (m_dataLength==0) {
|
||||
return false;
|
||||
}
|
||||
int offset=m_dataLength-1;
|
||||
int count=m_data[offset];
|
||||
for (int i=0,o=offset-2;i!=count;++i,o-=2) {
|
||||
if (m_data[o]!=prefix || m_data[o+1]!=uri) {
|
||||
continue;
|
||||
}
|
||||
count-=1;
|
||||
if (i==0) {
|
||||
m_data[o]=count;
|
||||
o-=(1+count*2);
|
||||
m_data[o]=count;
|
||||
} else {
|
||||
m_data[offset]=count;
|
||||
offset-=(1+2+count*2);
|
||||
m_data[offset]=count;
|
||||
System.arraycopy(
|
||||
m_data,o+2,
|
||||
m_data,o,
|
||||
m_dataLength-o);
|
||||
}
|
||||
m_dataLength-=2;
|
||||
m_count-=1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean pop() {
|
||||
if (m_dataLength==0) {
|
||||
return false;
|
||||
}
|
||||
int offset=m_dataLength-1;
|
||||
int count=m_data[offset];
|
||||
if (count==0) {
|
||||
return false;
|
||||
}
|
||||
count-=1;
|
||||
offset-=2;
|
||||
m_data[offset]=count;
|
||||
offset-=(1+count*2);
|
||||
m_data[offset]=count;
|
||||
m_dataLength-=2;
|
||||
m_count-=1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public final int getPrefix(int index) {
|
||||
return get(index,true);
|
||||
}
|
||||
|
||||
public final int getUri(int index) {
|
||||
return get(index,false);
|
||||
}
|
||||
|
||||
public final int findPrefix(int uri) {
|
||||
return find(uri,false);
|
||||
}
|
||||
|
||||
public final int findUri(int prefix) {
|
||||
return find(prefix,true);
|
||||
}
|
||||
|
||||
public final int getDepth() {
|
||||
return m_depth;
|
||||
}
|
||||
|
||||
public final void increaseDepth() {
|
||||
ensureDataCapacity(2);
|
||||
int offset=m_dataLength;
|
||||
m_data[offset]=0;
|
||||
m_data[offset+1]=0;
|
||||
m_dataLength+=2;
|
||||
m_depth+=1;
|
||||
}
|
||||
public final void decreaseDepth() {
|
||||
if (m_dataLength==0) {
|
||||
return;
|
||||
}
|
||||
int offset=m_dataLength-1;
|
||||
int count=m_data[offset];
|
||||
if ((offset-1-count*2)==0) {
|
||||
return;
|
||||
}
|
||||
m_dataLength-=2+count*2;
|
||||
m_count-=count;
|
||||
m_depth-=1;
|
||||
}
|
||||
|
||||
private void ensureDataCapacity(int capacity) {
|
||||
int available=(m_data.length-m_dataLength);
|
||||
if (available>capacity) {
|
||||
return;
|
||||
}
|
||||
int newLength=(m_data.length+available)*2;
|
||||
int[] newData=new int[newLength];
|
||||
System.arraycopy(m_data,0,newData,0,m_dataLength);
|
||||
m_data=newData;
|
||||
}
|
||||
|
||||
private final int find(int prefixOrUri,boolean prefix) {
|
||||
if (m_dataLength==0) {
|
||||
return -1;
|
||||
}
|
||||
int offset=m_dataLength-1;
|
||||
for (int i=m_depth;i!=0;--i) {
|
||||
int count=m_data[offset];
|
||||
offset-=2;
|
||||
for (;count!=0;--count) {
|
||||
if (prefix) {
|
||||
if (m_data[offset]==prefixOrUri) {
|
||||
return m_data[offset+1];
|
||||
}
|
||||
} else {
|
||||
if (m_data[offset+1]==prefixOrUri) {
|
||||
return m_data[offset];
|
||||
}
|
||||
}
|
||||
offset-=2;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private final int get(int index,boolean prefix) {
|
||||
if (m_dataLength==0 || index<0) {
|
||||
return -1;
|
||||
}
|
||||
int offset=0;
|
||||
for (int i=m_depth;i!=0;--i) {
|
||||
int count=m_data[offset];
|
||||
if (index>=count) {
|
||||
index-=count;
|
||||
offset+=(2+count*2);
|
||||
continue;
|
||||
}
|
||||
offset+=(1+index*2);
|
||||
if (!prefix) {
|
||||
offset+=1;
|
||||
}
|
||||
return m_data[offset];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int[] m_data;
|
||||
private int m_dataLength;
|
||||
private int m_count;
|
||||
private int m_depth;
|
||||
}
|
||||
|
||||
/////////////////////////////////// package-visible
|
||||
|
||||
// final void fetchAttributes(int[] styleableIDs,TypedArray result) {
|
||||
// result.resetIndices();
|
||||
// if (m_attributes==null || m_resourceIDs==null) {
|
||||
// return;
|
||||
// }
|
||||
// boolean needStrings=false;
|
||||
// for (int i=0,e=styleableIDs.length;i!=e;++i) {
|
||||
// int id=styleableIDs[i];
|
||||
// for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) {
|
||||
// int name=m_attributes[o+ATTRIBUTE_IX_NAME];
|
||||
// if (name>=m_resourceIDs.length ||
|
||||
// m_resourceIDs[name]!=id)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE];
|
||||
// int valueData;
|
||||
// int assetCookie;
|
||||
// if (valueType==TypedValue.TYPE_STRING) {
|
||||
// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING];
|
||||
// assetCookie=-1;
|
||||
// needStrings=true;
|
||||
// } else {
|
||||
// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA];
|
||||
// assetCookie=0;
|
||||
// }
|
||||
// result.addValue(i,valueType,valueData,assetCookie,id,0);
|
||||
// }
|
||||
// }
|
||||
// if (needStrings) {
|
||||
// result.setStrings(m_strings);
|
||||
// }
|
||||
// }
|
||||
|
||||
final StringBlock getStrings() {
|
||||
return m_strings;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
|
||||
private final int getAttributeOffset(int index) {
|
||||
if (m_event!=START_TAG) {
|
||||
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
|
||||
}
|
||||
int offset=index*5;
|
||||
if (offset>=m_attributes.length) {
|
||||
throw new IndexOutOfBoundsException("Invalid attribute index ("+index+").");
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private final int findAttribute(String namespace,String attribute) {
|
||||
if (m_strings==null || attribute==null) {
|
||||
return -1;
|
||||
}
|
||||
int name=m_strings.find(attribute);
|
||||
if (name==-1) {
|
||||
return -1;
|
||||
}
|
||||
int uri=(namespace!=null)?
|
||||
m_strings.find(namespace):
|
||||
-1;
|
||||
for (int o=0;o!=m_attributes.length;++o) {
|
||||
if (name==m_attributes[o+ATTRIBUTE_IX_NAME] &&
|
||||
(uri==-1 || uri==m_attributes[o+ATTRIBUTE_IX_NAMESPACE_URI]))
|
||||
{
|
||||
return o/ATTRIBUTE_LENGHT;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private final void resetEventInfo() {
|
||||
m_event=-1;
|
||||
m_lineNumber=-1;
|
||||
m_name=-1;
|
||||
m_namespaceUri=-1;
|
||||
m_attributes=null;
|
||||
m_idAttribute=-1;
|
||||
m_classAttribute=-1;
|
||||
m_styleAttribute=-1;
|
||||
}
|
||||
|
||||
private final void doNext() throws IOException {
|
||||
// Delayed initialization.
|
||||
if (m_strings==null) {
|
||||
ChunkUtil.readCheckType(m_reader,CHUNK_AXML_FILE);
|
||||
/*chunkSize*/m_reader.skipInt();
|
||||
m_strings=StringBlock.read(m_reader);
|
||||
m_namespaces.increaseDepth();
|
||||
m_operational=true;
|
||||
}
|
||||
|
||||
if (m_event==END_DOCUMENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
int event=m_event;
|
||||
resetEventInfo();
|
||||
|
||||
while (true) {
|
||||
if (m_decreaseDepth) {
|
||||
m_decreaseDepth=false;
|
||||
m_namespaces.decreaseDepth();
|
||||
}
|
||||
|
||||
// Fake END_DOCUMENT event.
|
||||
if (event==END_TAG &&
|
||||
m_namespaces.getDepth()==1 &&
|
||||
m_namespaces.getCurrentCount()==0)
|
||||
{
|
||||
m_event=END_DOCUMENT;
|
||||
break;
|
||||
}
|
||||
|
||||
int chunkType;
|
||||
if (event==START_DOCUMENT) {
|
||||
// Fake event, see CHUNK_XML_START_TAG handler.
|
||||
chunkType=CHUNK_XML_START_TAG;
|
||||
} else {
|
||||
chunkType=m_reader.readInt();
|
||||
}
|
||||
|
||||
if (chunkType==CHUNK_RESOURCEIDS) {
|
||||
int chunkSize=m_reader.readInt();
|
||||
if (chunkSize<8 || (chunkSize%4)!=0) {
|
||||
throw new IOException("Invalid resource ids size ("+chunkSize+").");
|
||||
}
|
||||
m_resourceIDs=m_reader.readIntArray(chunkSize/4-2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chunkType<CHUNK_XML_FIRST || chunkType>CHUNK_XML_LAST) {
|
||||
throw new IOException("Invalid chunk type ("+chunkType+").");
|
||||
}
|
||||
|
||||
// Fake START_DOCUMENT event.
|
||||
if (chunkType==CHUNK_XML_START_TAG && event==-1) {
|
||||
m_event=START_DOCUMENT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Common header.
|
||||
/*chunkSize*/m_reader.skipInt();
|
||||
int lineNumber=m_reader.readInt();
|
||||
/*0xFFFFFFFF*/m_reader.skipInt();
|
||||
|
||||
if (chunkType==CHUNK_XML_START_NAMESPACE ||
|
||||
chunkType==CHUNK_XML_END_NAMESPACE)
|
||||
{
|
||||
if (chunkType==CHUNK_XML_START_NAMESPACE) {
|
||||
int prefix=m_reader.readInt();
|
||||
int uri=m_reader.readInt();
|
||||
m_namespaces.push(prefix,uri);
|
||||
} else {
|
||||
/*prefix*/m_reader.skipInt();
|
||||
/*uri*/m_reader.skipInt();
|
||||
m_namespaces.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m_lineNumber=lineNumber;
|
||||
|
||||
if (chunkType==CHUNK_XML_START_TAG) {
|
||||
m_namespaceUri=m_reader.readInt();
|
||||
m_name=m_reader.readInt();
|
||||
/*flags?*/m_reader.skipInt();
|
||||
int attributeCount=m_reader.readInt();
|
||||
m_idAttribute=(attributeCount>>>16)-1;
|
||||
attributeCount&=0xFFFF;
|
||||
m_classAttribute=m_reader.readInt();
|
||||
m_styleAttribute=(m_classAttribute>>>16)-1;
|
||||
m_classAttribute=(m_classAttribute & 0xFFFF)-1;
|
||||
m_attributes=m_reader.readIntArray(attributeCount*ATTRIBUTE_LENGHT);
|
||||
for (int i=ATTRIBUTE_IX_VALUE_TYPE;i<m_attributes.length;) {
|
||||
m_attributes[i]=(m_attributes[i]>>>24);
|
||||
i+=ATTRIBUTE_LENGHT;
|
||||
}
|
||||
m_namespaces.increaseDepth();
|
||||
m_event=START_TAG;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkType==CHUNK_XML_END_TAG) {
|
||||
m_namespaceUri=m_reader.readInt();
|
||||
m_name=m_reader.readInt();
|
||||
m_event=END_TAG;
|
||||
m_decreaseDepth=true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkType==CHUNK_XML_TEXT) {
|
||||
m_name=m_reader.readInt();
|
||||
/*?*/m_reader.skipInt();
|
||||
/*?*/m_reader.skipInt();
|
||||
m_event=TEXT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////// data
|
||||
|
||||
/*
|
||||
* All values are essentially indices, e.g. m_name is
|
||||
* an index of name in m_strings.
|
||||
*/
|
||||
|
||||
private IntReader m_reader;
|
||||
private boolean m_operational=false;
|
||||
|
||||
private StringBlock m_strings;
|
||||
private int[] m_resourceIDs;
|
||||
private NamespaceStack m_namespaces=new NamespaceStack();
|
||||
|
||||
private boolean m_decreaseDepth;
|
||||
|
||||
private int m_event;
|
||||
private int m_lineNumber;
|
||||
private int m_name;
|
||||
private int m_namespaceUri;
|
||||
private int[] m_attributes;
|
||||
private int m_idAttribute;
|
||||
private int m_classAttribute;
|
||||
private int m_styleAttribute;
|
||||
|
||||
private static final String
|
||||
E_NOT_SUPPORTED ="Method is not supported.";
|
||||
|
||||
private static final int
|
||||
ATTRIBUTE_IX_NAMESPACE_URI =0,
|
||||
ATTRIBUTE_IX_NAME =1,
|
||||
ATTRIBUTE_IX_VALUE_STRING =2,
|
||||
ATTRIBUTE_IX_VALUE_TYPE =3,
|
||||
ATTRIBUTE_IX_VALUE_DATA =4,
|
||||
ATTRIBUTE_LENGHT =5;
|
||||
|
||||
private static final int
|
||||
CHUNK_AXML_FILE =0x00080003,
|
||||
CHUNK_RESOURCEIDS =0x00080180,
|
||||
CHUNK_XML_FIRST =0x00100100,
|
||||
CHUNK_XML_START_NAMESPACE =0x00100100,
|
||||
CHUNK_XML_END_NAMESPACE =0x00100101,
|
||||
CHUNK_XML_START_TAG =0x00100102,
|
||||
CHUNK_XML_END_TAG =0x00100103,
|
||||
CHUNK_XML_TEXT =0x00100104,
|
||||
CHUNK_XML_LAST =0x00100104;
|
||||
}
|
245
src/brut/androlib/res/decoder/StringBlock.java
Normal file
245
src/brut/androlib/res/decoder/StringBlock.java
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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 brut.androlib.res.decoder;
|
||||
|
||||
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 {
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user