fix XML encode errors

This commit is contained in:
REAndroid 2023-01-04 10:51:58 -05:00
parent 1b9ed9c291
commit 7389358a18
18 changed files with 220 additions and 129 deletions

View File

@ -42,9 +42,11 @@ import java.util.*;
private final ApkModule apkModule; private final ApkModule apkModule;
private final Map<Integer, Set<ResConfig>> decodedEntries; private final Map<Integer, Set<ResConfig>> decodedEntries;
private XMLBagDecoder xmlBagDecoder; private XMLBagDecoder xmlBagDecoder;
private final Set<String> mDecodedPaths;
public ApkModuleXmlDecoder(ApkModule apkModule){ public ApkModuleXmlDecoder(ApkModule apkModule){
this.apkModule=apkModule; this.apkModule=apkModule;
this.decodedEntries = new HashMap<>(); this.decodedEntries = new HashMap<>();
this.mDecodedPaths = new HashSet<>();
} }
public void decodeTo(File outDir) public void decodeTo(File outDir)
throws IOException, XMLException { throws IOException, XMLException {
@ -60,6 +62,8 @@ import java.util.*;
decodeAndroidManifest(entryStore, outDir); decodeAndroidManifest(entryStore, outDir);
addDecodedPath(TableBlock.FILE_NAME);
logMessage("Decoding resource files ..."); logMessage("Decoding resource files ...");
List<ResFile> resFileList=apkModule.listResFiles(); List<ResFile> resFileList=apkModule.listResFiles();
for(ResFile resFile:resFileList){ for(ResFile resFile:resFileList){
@ -76,6 +80,7 @@ import java.util.*;
}else { }else {
decodeResRaw(outDir, resFile); decodeResRaw(outDir, resFile);
} }
addDecodedPath(resFile.getFilePath());
} }
private void decodeResRaw(File outDir, ResFile resFile) private void decodeResRaw(File outDir, ResFile resFile)
throws IOException { throws IOException {
@ -147,6 +152,7 @@ import java.util.*;
int currentPackageId= manifestBlock.guessCurrentPackageId(); int currentPackageId= manifestBlock.guessCurrentPackageId();
XMLDocument xmlDocument=manifestBlock.decodeToXml(entryStore, currentPackageId); XMLDocument xmlDocument=manifestBlock.decodeToXml(entryStore, currentPackageId);
xmlDocument.save(file, true); xmlDocument.save(file, true);
addDecodedPath(AndroidManifestBlock.FILE_NAME);
} }
private void addDecodedEntry(Collection<EntryBlock> entryBlockList){ private void addDecodedEntry(Collection<EntryBlock> entryBlockList){
for(EntryBlock entryBlock:entryBlockList){ for(EntryBlock entryBlock:entryBlockList){
@ -245,7 +251,11 @@ import java.util.*;
logMessage("Extracting root files"); logMessage("Extracting root files");
File rootDir = new File(outDir, "root"); File rootDir = new File(outDir, "root");
for(InputSource inputSource:apkModule.getApkArchive().listInputSources()){ for(InputSource inputSource:apkModule.getApkArchive().listInputSources()){
if(containsDecodedPath(inputSource.getAlias())){
continue;
}
extractRootFiles(rootDir, inputSource); extractRootFiles(rootDir, inputSource);
addDecodedPath(inputSource.getAlias());
} }
} }
private void extractRootFiles(File rootDir, InputSource inputSource) throws IOException { private void extractRootFiles(File rootDir, InputSource inputSource) throws IOException {
@ -260,6 +270,12 @@ import java.util.*;
inputSource.write(outputStream); inputSource.write(outputStream);
outputStream.close(); outputStream.close();
} }
private boolean containsDecodedPath(String path){
return mDecodedPaths.contains(path);
}
private void addDecodedPath(String path){
mDecodedPaths.add(path);
}
private void logMessage(String msg) { private void logMessage(String msg) {
APKLogger apkLogger=apkModule.getApkLogger(); APKLogger apkLogger=apkModule.getApkLogger();

View File

@ -49,7 +49,7 @@ public class ApkUtil {
public static List<File> recursiveFiles(File dir, String ext){ public static List<File> recursiveFiles(File dir, String ext){
List<File> results=new ArrayList<>(); List<File> results=new ArrayList<>();
if(dir.isFile()){ if(dir.isFile()){
if(ext==null || dir.getName().endsWith(ext)){ if(hasExtension(dir, ext)){
results.add(dir); results.add(dir);
} }
return results; return results;
@ -63,7 +63,7 @@ public class ApkUtil {
} }
for(File file:files){ for(File file:files){
if(file.isFile()){ if(file.isFile()){
if(ext!=null && !file.getName().endsWith(ext)){ if(!hasExtension(file, ext)){
continue; continue;
} }
results.add(file); results.add(file);
@ -74,26 +74,7 @@ public class ApkUtil {
return results; return results;
} }
public static List<File> recursiveFiles(File dir){ public static List<File> recursiveFiles(File dir){
List<File> results=new ArrayList<>(); return recursiveFiles(dir, null);
if(dir.isFile()){
results.add(dir);
return results;
}
if(!dir.isDirectory()){
return results;
}
File[] files=dir.listFiles();
if(files==null){
return results;
}
for(File file:files){
if(file.isFile()){
results.add(file);
continue;
}
results.addAll(recursiveFiles(file));
}
return results;
} }
public static List<File> listDirectories(File dir){ public static List<File> listDirectories(File dir){
List<File> results=new ArrayList<>(); List<File> results=new ArrayList<>();
@ -116,7 +97,7 @@ public class ApkUtil {
} }
for(File file:files){ for(File file:files){
if(file.isFile()){ if(file.isFile()){
if(ext!=null && !file.getName().endsWith(ext)){ if(!hasExtension(file, ext)){
continue; continue;
} }
results.add(file); results.add(file);
@ -124,6 +105,14 @@ public class ApkUtil {
} }
return results; return results;
} }
private static boolean hasExtension(File file, String ext){
if(ext==null){
return true;
}
String name=file.getName().toLowerCase();
ext=ext.toLowerCase();
return name.endsWith(ext);
}
public static String toModuleName(File file){ public static String toModuleName(File file){
String name=file.getName(); String name=file.getName();
int i=name.lastIndexOf('.'); int i=name.lastIndexOf('.');

View File

@ -39,6 +39,7 @@
private PackageBlock currentPackage; private PackageBlock currentPackage;
private final Set<FrameworkTable> frameworkTables = new HashSet<>(); private final Set<FrameworkTable> frameworkTables = new HashSet<>();
private APKLogger apkLogger; private APKLogger apkLogger;
private boolean mForceCreateNamespaces = true;
public EncodeMaterials(){ public EncodeMaterials(){
} }
public SpecString getSpecString(String name){ public SpecString getSpecString(String name){
@ -278,6 +279,11 @@
} }
return null; return null;
} }
public EncodeMaterials setForceCreateNamespaces(boolean force) {
this.mForceCreateNamespaces = force;
return this;
}
public EncodeMaterials setPackageIds(ResourceIds.Table.Package packageIds) { public EncodeMaterials setPackageIds(ResourceIds.Table.Package packageIds) {
this.packageIds = packageIds; this.packageIds = packageIds;
return this; return this;
@ -302,6 +308,10 @@
public PackageBlock getCurrentPackage() { public PackageBlock getCurrentPackage() {
return currentPackage; return currentPackage;
} }
public boolean isForceCreateNamespaces() {
return mForceCreateNamespaces;
}
public String getCurrentPackageName(){ public String getCurrentPackageName(){
return currentPackage.getName(); return currentPackage.getName();
} }

View File

@ -113,4 +113,9 @@ package com.reandroid.lib.apk.xmlencoder;
} }
public static final String NULL_PACKAGE_NAME = "NULL_PACKAGE_NAME"; public static final String NULL_PACKAGE_NAME = "NULL_PACKAGE_NAME";
private static final Pattern PATTERN_TYPE=Pattern.compile("^([a-z]+)[^a-z]*.*$"); private static final Pattern PATTERN_TYPE=Pattern.compile("^([a-z]+)[^a-z]*.*$");
public static final String URI_ANDROID = "http://schemas.android.com/apk/res/android";
public static final String URI_APP = "http://schemas.android.com/apk/res-auto";
public static final String PREFIX_ANDROID = "android";
public static final String PREFIX_APP = "app";
} }

View File

@ -63,10 +63,12 @@ public class XMLEncodeSource extends ByteInputSource {
} }
try { try {
XMLFileEncoder xmlFileEncoder=new XMLFileEncoder(encodeMaterials); XMLFileEncoder xmlFileEncoder=new XMLFileEncoder(encodeMaterials);
xmlFileEncoder.setCurrentPath(xmlSource.getPath());
encodeMaterials.logVerbose("Encoding xml: "+xmlSource.getPath()); encodeMaterials.logVerbose("Encoding xml: "+xmlSource.getPath());
resXmlBlock = xmlFileEncoder.encode(xmlSource.getXMLDocument()); resXmlBlock = xmlFileEncoder.encode(xmlSource.getXMLDocument());
} catch (XMLException ex) { } catch (XMLException ex) {
throw new IOException(ex.getMessage(), ex); throw new EncodeException("XMLException on: '"+xmlSource.getPath()
+"'\n '"+ex.getMessage()+"'");
} }
return resXmlBlock; return resXmlBlock;
} }

View File

@ -30,9 +30,15 @@ import java.io.InputStream;
public class XMLFileEncoder { public class XMLFileEncoder {
private final EncodeMaterials materials; private final EncodeMaterials materials;
private ResXmlBlock resXmlBlock; private ResXmlBlock resXmlBlock;
private String mCurrentPath;
public XMLFileEncoder(EncodeMaterials materials){ public XMLFileEncoder(EncodeMaterials materials){
this.materials=materials; this.materials=materials;
} }
// Just for logging purpose
public void setCurrentPath(String path) {
this.mCurrentPath = path;
}
public ResXmlBlock encode(String xmlString){ public ResXmlBlock encode(String xmlString){
try { try {
return encode(XMLDocument.load(xmlString)); return encode(XMLDocument.load(xmlString));
@ -50,6 +56,7 @@ public class XMLFileEncoder {
return null; return null;
} }
public ResXmlBlock encode(File xmlFile){ public ResXmlBlock encode(File xmlFile){
setCurrentPath(xmlFile.getAbsolutePath());
try { try {
return encode(XMLDocument.load(xmlFile)); return encode(XMLDocument.load(xmlFile));
} catch (XMLException ex) { } catch (XMLException ex) {
@ -100,6 +107,14 @@ public class XMLFileEncoder {
String prefix=attribute.getNamePrefix(); String prefix=attribute.getNamePrefix();
if(prefix!=null){ if(prefix!=null){
ResXmlStartNamespace ns = resXmlElement.getStartNamespaceByPrefix(prefix); ResXmlStartNamespace ns = resXmlElement.getStartNamespaceByPrefix(prefix);
if(ns==null){
ns=forceCreateNamespace(resXmlElement, resourceId, prefix);
}
if(ns==null){
throw new EncodeException("Namespace not found: "
+attribute.toString()
+", path="+mCurrentPath);
}
xmlAttribute.setNamespaceReference(ns.getUriReference()); xmlAttribute.setNamespaceReference(ns.getUriReference());
} }
@ -125,15 +140,14 @@ public class XMLFileEncoder {
xmlAttribute.setRawValue(encodeResult.value); xmlAttribute.setRawValue(encodeResult.value);
continue; continue;
} }
if(attributeBag.contains(AttributeValueType.STRING)) { if(attributeBag.isEqualType(AttributeValueType.STRING)) {
xmlAttribute.setValueAsString(valueText); xmlAttribute.setValueAsString(valueText);
continue; continue;
} }
} }
if(EncodeUtil.isEmpty(valueText)) { if(EncodeUtil.isEmpty(valueText)) {
xmlAttribute.setValueType(ValueType.NULL); xmlAttribute.setValueAsString("");
xmlAttribute.setRawValue(0);
}else{ }else{
ValueDecoder.EncodeResult encodeResult = ValueDecoder.EncodeResult encodeResult =
ValueDecoder.encodeGuessAny(valueText); ValueDecoder.encodeGuessAny(valueText);
@ -145,6 +159,7 @@ public class XMLFileEncoder {
} }
} }
} }
resXmlElement.calculatePositions();
} }
private void ensureNamespaces(XMLElement element, ResXmlElement resXmlElement){ private void ensureNamespaces(XMLElement element, ResXmlElement resXmlElement){
int count=element.getAttributeCount(); int count=element.getAttributeCount();
@ -181,4 +196,21 @@ public class XMLFileEncoder {
idBuilder.add(entryBlock.getResourceId(), entryBlock.getName()); idBuilder.add(entryBlock.getResourceId(), entryBlock.getName());
} }
} }
private ResXmlStartNamespace forceCreateNamespace(ResXmlElement resXmlElement,
int resourceId, String prefix){
if(!materials.isForceCreateNamespaces()){
return null;
}
int pkgId = (resourceId>>24) & 0xff;
String uri;
if(pkgId==materials.getCurrentPackageId()){
uri=EncodeUtil.URI_APP;
}else {
uri=EncodeUtil.URI_ANDROID;
}
ResXmlElement root=resXmlElement.getRootResXmlElement();
ResXmlStartNamespace ns=root.getOrCreateNamespace(uri, prefix);
materials.logMessage("Force created ns: "+prefix+":"+uri);
return ns;
}
} }

View File

@ -52,10 +52,8 @@ class XMLValuesEncoder {
encodeValue(entryBlock, element); encodeValue(entryBlock, element);
if(!entryBlock.isNull()){ SpecString specString = getMaterials().getSpecString(name);
SpecString specString = getMaterials().getSpecString(name); entryBlock.setSpecReference(specString);
entryBlock.setSpecReference(specString);
}
} }
void encodeValue(EntryBlock entryBlock, XMLElement element){ void encodeValue(EntryBlock entryBlock, XMLElement element){
String value = getValue(element); String value = getValue(element);

View File

@ -65,7 +65,7 @@ class XMLValuesEncoderStyle extends XMLValuesEncoderBag{
bagItem.setType(ValueType.REFERENCE); bagItem.setType(ValueType.REFERENCE);
} }
bagItem.setData(getMaterials().resolveReference(valueText)); bagItem.setData(getMaterials().resolveReference(valueText));
}else if(attributeBag.contains(AttributeValueType.STRING)) { }else if(attributeBag.isEqualType(AttributeValueType.STRING)) {
bagItem.setValueAsString(valueText); bagItem.setValueAsString(valueText);
}else if(EncodeUtil.isEmpty(valueText)) { }else if(EncodeUtil.isEmpty(valueText)) {
bagItem.setTypeAndData(ValueType.NULL, 0); bagItem.setTypeAndData(ValueType.NULL, 0);

View File

@ -247,7 +247,7 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
public int getHighestEntryCount(){ public int getHighestEntryCount(){
int result=0; int result=0;
for(TypeBlock typeBlock:getChildes()){ for(TypeBlock typeBlock:getChildes()){
int count=typeBlock.getEntryCount(); int count=typeBlock.getEntryBlockArray().childesCount();
if(count>result){ if(count>result){
result=count; result=count;
} }

View File

@ -60,6 +60,12 @@ import java.util.*;
addChild(4, mEndElementContainer); addChild(4, mEndElementContainer);
addChild(5, mEndNamespaceList); addChild(5, mEndNamespaceList);
} }
public void calculatePositions(){
ResXmlStartElement start = getStartElement();
if(start!=null){
start.calculatePositions();
}
}
public ResXmlAttribute newAttribute(){ public ResXmlAttribute newAttribute(){
return getStartElement().newAttribute(); return getStartElement().newAttribute();
} }

View File

@ -123,7 +123,7 @@ import java.util.regex.Pattern;
return new EncodeResult(ValueType.INT_HEX, parseHex(numString)); return new EncodeResult(ValueType.INT_HEX, parseHex(numString));
} }
if(isInteger(numString)){ if(isInteger(numString)){
return new EncodeResult(ValueType.INT_DEC, parseHex(numString)); return new EncodeResult(ValueType.INT_DEC, parseInteger(numString));
} }
return null; return null;
} }

View File

@ -75,7 +75,7 @@ public class EntryGroup extends ItemGroup<EntryBlock> {
} }
boolean renameOk=false; boolean renameOk=false;
for(EntryBlock block:items){ for(EntryBlock block:items){
if(block==null||block.isNull()){ if(block==null){
continue; continue;
} }
if(block.getSpecReference()==specReference){ if(block.getSpecReference()==specReference){
@ -87,15 +87,22 @@ public class EntryGroup extends ItemGroup<EntryBlock> {
return renameOk; return renameOk;
} }
public EntryBlock pickOne(){ public EntryBlock pickOne(){
EntryBlock defEntryBlock=getDefault(); EntryBlock[] items=getItems();
if(defEntryBlock!=null){ if(items==null){
return defEntryBlock; return null;
} }
Iterator<EntryBlock> itr=iterator(true); EntryBlock result = null;
while (itr.hasNext()){ for(EntryBlock entryBlock:items){
return itr.next(); if(entryBlock==null){
continue;
}
if(result==null || result.isNull()){
result=entryBlock;
}else if(entryBlock.isDefault()){
return entryBlock;
}
} }
return null; return result;
} }
public EntryBlock getDefault(){ public EntryBlock getDefault(){
Iterator<EntryBlock> itr=iterator(true); Iterator<EntryBlock> itr=iterator(true);

View File

@ -278,28 +278,45 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
private void setByteFlagsB(byte b){ private void setByteFlagsB(byte b){
mByteFlagsB.set(b); mByteFlagsB.set(b);
} }
public IntegerItem getSpecReferenceBlock(){ private IntegerItem getSpecReferenceBlock(){
return mSpecReference; return mSpecReference;
} }
public int getSpecReference(){ public int getSpecReference(){
if(mSpecReference==null){
return -1;
}
return mSpecReference.get(); return mSpecReference.get();
} }
public void setSpecReference(int ref){ public void setSpecReference(int ref){
if(mSpecReference==null){ boolean created = createNullSpecReference();
return;
}
int old=mSpecReference.get(); int old=mSpecReference.get();
if(ref==old){ if(ref==old){
return; return;
} }
mSpecReference.set(ref); mSpecReference.set(ref);
updateSpecRef(old, ref); updateSpecRef(old, ref);
if(created){
updatePackage();
}
} }
public void setSpecReference(SpecString specString){ public void setSpecReference(SpecString specString){
removeSpecRef(); removeSpecRef();
if(mSpecReference!=null){ if(specString==null){
mSpecReference.set(specString.getIndex()); return;
} }
boolean created = createNullSpecReference();
mSpecReference.set(specString.getIndex());
if(created){
updatePackage();
}
}
private boolean createNullSpecReference(){
if(mSpecReference==null){
mSpecReference = new IntegerItem();
mSpecReference.setNull(true);
return true;
}
return false;
} }
public BaseResValue getResValue(){ public BaseResValue getResValue(){
return mResValue; return mResValue;
@ -352,6 +369,13 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
} }
return specString.get(); return specString.get();
} }
public String getNameOrHex(){
String name = getName();
if(name==null){
name = String.format("@0x%08x", getResourceId());
}
return name;
}
private void setName(String name){ private void setName(String name){
PackageBlock packageBlock=getPackageBlock(); PackageBlock packageBlock=getPackageBlock();
EntryGroup entryGroup = packageBlock.getEntryGroup(getResourceId()); EntryGroup entryGroup = packageBlock.getEntryGroup(getResourceId());
@ -378,6 +402,9 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return packageBlock.getName(); return packageBlock.getName();
} }
public SpecString getSpecString(){ public SpecString getSpecString(){
if(mSpecReference==null){
return null;
}
PackageBlock packageBlock=getPackageBlock(); PackageBlock packageBlock=getPackageBlock();
if(packageBlock==null){ if(packageBlock==null){
return null; return null;
@ -486,7 +513,11 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
this.mHeaderSize =new ShortItem(); this.mHeaderSize =new ShortItem();
this.mFlagEntryType =new ByteItem(); this.mFlagEntryType =new ByteItem();
this.mByteFlagsB=new ByteItem(); this.mByteFlagsB=new ByteItem();
this.mSpecReference = new IntegerItem(); if(mSpecReference==null){
this.mSpecReference = new IntegerItem();
}else if(mSpecReference.isNull()){
mSpecReference.setNull(false);
}
mHeaderSize.setIndex(0); mHeaderSize.setIndex(0);
mFlagEntryType.setIndex(1); mFlagEntryType.setIndex(1);
@ -625,11 +656,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
updateSpecRef(-1, getSpecReference()); updateSpecRef(-1, getSpecReference());
} }
private void updateSpecRef(int oldRef, int newNef){ private void updateSpecRef(int oldRef, int newNef){
TypeBlock typeBlock=getTypeBlock(); PackageBlock packageBlock=getPackageBlock();
if(typeBlock==null){
return;
}
PackageBlock packageBlock=typeBlock.getPackageBlock();
if(packageBlock==null){ if(packageBlock==null){
return; return;
} }
@ -647,11 +674,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(mSpecReference==null){ if(mSpecReference==null){
return; return;
} }
TypeBlock typeBlock=getTypeBlock(); PackageBlock packageBlock=getPackageBlock();
if(typeBlock==null){
return;
}
PackageBlock packageBlock=typeBlock.getPackageBlock();
if(packageBlock==null){ if(packageBlock==null){
return; return;
} }
@ -662,11 +685,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
} }
} }
private void updatePackage(){ private void updatePackage(){
TypeBlock typeBlock=getTypeBlock(); PackageBlock packageBlock=getPackageBlock();
if(typeBlock==null){
return;
}
PackageBlock packageBlock=typeBlock.getPackageBlock();
if(packageBlock==null){ if(packageBlock==null){
return; return;
} }
@ -761,37 +780,6 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
} }
return new ResValueInt(); return new ResValueInt();
} }
@Override
public String toString(){
StringBuilder builder=new StringBuilder();
builder.append(getClass().getSimpleName());
builder.append(": ");
ResConfig resConfig=getResConfig();
if(resConfig!=null){
builder.append(resConfig.toString());
builder.append(", ");
}
builder.append(" resId=");
builder.append(String.format("0x%08x", getResourceId()));
if(isNull()){
builder.append(", null entry");
return builder.toString();
}
String name=getResourceName();
if(name!=null){
builder.append('(');
builder.append(name);
builder.append(')');
}
BaseResValue baseResValue=getResValue();
if(baseResValue instanceof ResValueInt){
ResValueInt resValueInt=(ResValueInt)baseResValue;
builder.append(" '");
builder.append(resValueInt.toString());
builder.append(" '");
}
return builder.toString();
}
public static String buildResourceName(char prefix, String packageName, String type, String name){ public static String buildResourceName(char prefix, String packageName, String type, String name){
if(name==null){ if(name==null){
return null; return null;
@ -811,6 +799,39 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
builder.append(name); builder.append(name);
return builder.toString(); return builder.toString();
} }
@Override
public String toString(){
StringBuilder builder=new StringBuilder();
builder.append(getClass().getSimpleName());
builder.append(": ");
ResConfig resConfig=getResConfig();
if(resConfig!=null){
builder.append(resConfig.toString());
builder.append(", ");
}
String name=getResourceName();
if(name==null){
name=getNameOrHex();
}else{
builder.append(" id=");
builder.append(String.format("0x%08x", getResourceId()));
}
builder.append('(');
builder.append(name);
builder.append(')');
if(isNull()){
builder.append(", null entry");
return builder.toString();
}
BaseResValue baseResValue=getResValue();
if(baseResValue instanceof ResValueInt){
ResValueInt resValueInt=(ResValueInt)baseResValue;
builder.append(" '");
builder.append(resValueInt.toString());
builder.append(" '");
}
return builder.toString();
}
private final static short HEADER_SIZE_BAG = 0x0010; private final static short HEADER_SIZE_BAG = 0x0010;
private final static short HEADER_SIZE_INT = 0x0008; private final static short HEADER_SIZE_INT = 0x0008;

View File

@ -31,6 +31,9 @@ public class AttributeBag {
public boolean contains(AttributeValueType valueType){ public boolean contains(AttributeValueType valueType){
return getFormat().contains(valueType); return getFormat().contains(valueType);
} }
public boolean isEqualType(AttributeValueType valueType){
return getFormat().isEqualType(valueType);
}
public ValueDecoder.EncodeResult encodeEnumOrFlagValue(String valueString){ public ValueDecoder.EncodeResult encodeEnumOrFlagValue(String valueString){
if(valueString==null || !isEnumOrFlag()){ if(valueString==null || !isEnumOrFlag()){
return null; return null;

View File

@ -92,6 +92,14 @@ public class AttributeBagItem {
int dataLow = 0xffff & getBagItem().getDataLow(); int dataLow = 0xffff & getBagItem().getDataLow();
return (dataLow & value) == value; return (dataLow & value) == value;
} }
public boolean isEqualType(AttributeValueType valueType){
if(valueType == null || getItemType()!=AttributeItemType.FORMAT){
return false;
}
int value = 0xff & valueType.getByte();
int dataLow = 0xffff & getBagItem().getDataLow();
return (dataLow == value);
}
public AttributeValueType[] getValueTypes(){ public AttributeValueType[] getValueTypes(){
AttributeItemType type=getItemType(); AttributeItemType type=getItemType();
if(type!=AttributeItemType.FORMAT){ if(type!=AttributeItemType.FORMAT){

View File

@ -33,12 +33,6 @@ public class XMLTextAttribute extends XMLAttribute {
if(unEscape){ if(unEscape){
return XMLUtil.unEscapeXmlChars(mText); return XMLUtil.unEscapeXmlChars(mText);
} }
if(mText!=null){
String junk= XMLUtil.unEscapeXmlChars(mText);
if(!mText.equals(junk)){
junk.trim();
}
}
return mText; return mText;
} }
@Override @Override

View File

@ -530,30 +530,17 @@ public class MXParser implements XmlPullParser
} }
return i; return i;
} }
public String getPositionDescription () @Override
public String getPositionDescription()
{ {
String fragment = null; return "line="+getLineNumber()+", col="+getColumnNumber();
if(posStart <= pos) {
final int start = findFragment(0, buf, posStart, pos);
if(start < pos) {
fragment = new String(buf, start, pos - start);
}
if(bufAbsoluteStart > 0 || start > 0) {
fragment = "..." + fragment;
}
}
return " "+TYPES[ eventType ] +
(fragment != null ? " seen "+printable(fragment)+"..." : "")
+" "+(location != null ? location : "")
+"@"+getLineNumber()+":"+getColumnNumber();
} }
@Override
public int getLineNumber() public int getLineNumber()
{ {
return lineNumber; return lineNumber;
} }
@Override
public int getColumnNumber() public int getColumnNumber()
{ {
return columnNumber; return columnNumber;
@ -740,11 +727,13 @@ public class MXParser implements XmlPullParser
return attributeValue[ index ]; return attributeValue[ index ];
} }
public String getAttributeValue(String namespace, @Override
String name) public String getAttributeValue(String namespace, String name)
{ {
if(eventType != START_TAG) throw new IndexOutOfBoundsException( if(eventType != START_TAG) {
"only START_TAG can have attributes"+getPositionDescription()); throw new IndexOutOfBoundsException("only START_TAG can have attributes "
+getPositionDescription());
}
if(name == null) { if(name == null) {
throw new IllegalArgumentException("attribute name can not be null"); throw new IllegalArgumentException("attribute name can not be null");
} }
@ -1651,7 +1640,7 @@ public class MXParser implements XmlPullParser
} // skip additional spaces } // skip additional spaces
if(ch != '=') { if(ch != '=') {
throw new XmlPullParserException( throw new XmlPullParserException(
"expected = after attribute name", this, null); "expected = after attribute name '"+name+processNamespaces+"'", this, null);
} }
ch = more(); ch = more();
while(isS(ch)) { while(isS(ch)) {
@ -2689,7 +2678,7 @@ public class MXParser implements XmlPullParser
return (ch < LOOKUP_MAX_CHAR && lookupNameChar[ ch ]) return (ch < LOOKUP_MAX_CHAR && lookupNameChar[ ch ])
|| (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027') || (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027')
|| (ch >= '\u202A' && ch <= '\u218F') || (ch >= '\u202A' && ch <= '\u218F')
|| (ch >= '\u2800' && ch <= '\uFFEF'); || (ch >= '\u2800' && ch <= '\uFFEF') || ch=='@';
} }
protected boolean isS(char ch) { protected boolean isS(char ch) {

View File

@ -17,10 +17,7 @@ public class XmlPullParserException extends Exception {
super(s); super(s);
} }
public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) { public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) {
super ((msg == null ? "" : msg+" ") super(buildMessage(msg, parser));
+ (parser == null ? "" : "(position:"+parser.getPositionDescription()+") ")
+ (chain == null ? "" : "caused by: "+chain));
if (parser != null) { if (parser != null) {
this.row = parser.getLineNumber(); this.row = parser.getLineNumber();
this.column = parser.getColumnNumber(); this.column = parser.getColumnNumber();
@ -30,5 +27,19 @@ public class XmlPullParserException extends Exception {
public Throwable getDetail() { return detail; } public Throwable getDetail() { return detail; }
public int getLineNumber() { return row; } public int getLineNumber() { return row; }
public int getColumnNumber() { return column; } public int getColumnNumber() { return column; }
private static String buildMessage(String msg, XmlPullParser parser){
StringBuilder builder=new StringBuilder();
if(parser!=null){
builder.append("[line=");
builder.append(parser.getLineNumber());
builder.append(", col=");
builder.append(parser.getColumnNumber());
builder.append("] ");
}
if(msg!=null){
builder.append(msg);
}
return builder.toString();
}
} }