From 09af9d0514a61276b1cb2f9b3aac01463ed5400c Mon Sep 17 00:00:00 2001 From: REAndroid Date: Sun, 18 Jun 2023 19:06:50 +0200 Subject: [PATCH] [major] remove redundant classes --- .../com/reandroid/apk/ApkJsonEncoder.java | 26 - .../java/com/reandroid/apk/ApkModule.java | 25 +- .../reandroid/apk/ApkModuleXmlDecoder.java | 35 +- .../reandroid/apk/ApkModuleXmlEncoder.java | 1 + .../java/com/reandroid/apk/FrameworkApk.java | 3 +- .../com/reandroid/apk/FrameworkOptimizer.java | 6 +- src/main/java/com/reandroid/apk/ResFile.java | 26 +- .../reandroid/apk/xmldecoder/BagDecoder.java | 6 +- .../apk/xmldecoder/BagDecoderArray.java | 10 +- .../apk/xmldecoder/BagDecoderAttr.java | 24 +- .../apk/xmldecoder/BagDecoderCommon.java | 25 +- .../apk/xmldecoder/BagDecoderPlural.java | 16 +- .../apk/xmldecoder/DecoderResTableEntry.java | 13 +- .../xmldecoder/DecoderResTableEntryMap.java | 13 +- .../apk/xmldecoder/DecoderTableEntry.java | 20 +- .../xmldecoder/ResXmlDocumentSerializer.java | 41 +- .../apk/xmldecoder/XMLBagDecoder.java | 39 - .../apk/xmldecoder/XMLDecodeHelper.java | 8 +- .../apk/xmldecoder/XMLEntryDecoder.java | 33 +- .../xmldecoder/XMLEntryDecoderDocument.java | 5 +- .../xmldecoder/XMLEntryDecoderSerializer.java | 44 +- .../apk/xmldecoder/XMLNamespaceValidator.java | 35 +- .../apk/xmlencoder/EncodeMaterials.java | 2 +- .../apk/xmlencoder/FilePathEncoder.java | 2 +- .../reandroid/apk/xmlencoder/RESEncoder.java | 32 - .../apk/xmlencoder/ResourceValuesEncoder.java | 5 +- .../xmlencoder/ValuesStringPoolBuilder.java | 4 +- .../apk/xmlencoder/XMLEncodeSource.java | 1 + .../apk/xmlencoder/XMLFileEncoder.java | 16 +- .../apk/xmlencoder/XMLTableBlockEncoder.java | 61 +- .../apk/xmlencoder/XMLValuesEncoder.java | 2 +- .../apk/xmlencoder/XMLValuesEncoderArray.java | 6 +- .../xmlencoder/XMLValuesEncoderPlurals.java | 5 +- .../apk/xmlencoder/XMLValuesEncoderStyle.java | 6 +- .../reandroid/archive2/block/SignatureId.java | 1 - src/main/java/com/reandroid/arsc/ApkFile.java | 3 - .../arsc/array/LibraryInfoArray.java | 46 +- .../reandroid/arsc/array/PackageArray.java | 12 +- .../arsc/array/SpecTypePairArray.java | 8 - .../reandroid/arsc/array/TypeBlockArray.java | 30 +- .../reandroid/arsc/chunk/LibraryBlock.java | 42 +- .../com/reandroid/arsc/chunk/Overlayable.java | 303 +- .../arsc/chunk/OverlayablePolicy.java | 510 ++-- .../reandroid/arsc/chunk/PackageBlock.java | 254 +- .../com/reandroid/arsc/chunk/StagedAlias.java | 161 +- .../com/reandroid/arsc/chunk/TableBlock.java | 192 +- .../com/reandroid/arsc/chunk/TypeBlock.java | 19 +- .../arsc/chunk/xml/BaseXmlChunk.java | 394 +-- .../arsc/chunk/xml/ParserEventList.java | 4 +- .../arsc/chunk/xml/ResXmlAttribute.java | 149 +- .../arsc/chunk/xml/ResXmlDocument.java | 70 +- .../arsc/chunk/xml/ResXmlElement.java | 161 +- .../arsc/chunk/xml/ResXmlEndNamespace.java | 56 +- .../arsc/chunk/xml/ResXmlNamespace.java | 100 +- .../arsc/chunk/xml/ResXmlNamespaceChunk.java | 105 + .../arsc/chunk/xml/ResXmlPullParser.java | 139 +- .../arsc/chunk/xml/ResXmlStartElement.java | 74 +- .../arsc/chunk/xml/ResXmlStartNamespace.java | 56 +- .../arsc/chunk/xml/ResXmlTextNode.java | 4 +- .../com/reandroid/arsc/coder/CommonType.java | 8 + .../com/reandroid/arsc/coder/Decoder.java | 187 -- .../reandroid/arsc/coder/ReferenceString.java | 9 + .../reandroid/arsc/coder/ValueDecoder.java | 416 --- .../reandroid/arsc/coder/XmlSanitizer.java | 93 + .../reandroid/arsc/container/BlockList.java | 11 +- .../arsc/container/SpecTypePair.java | 94 +- .../com/reandroid/arsc/group/EntryGroup.java | 267 -- .../arsc/{group => model}/ResourceEntry.java | 72 +- .../reandroid/arsc/model/ResourceLibrary.java | 51 + .../reandroid/arsc/pool/TypeStringPool.java | 2 +- .../reandroid/arsc/util/FilterIterator.java | 25 + .../reandroid/arsc/util/FrameworkTable.java | 2 +- .../java/com/reandroid/arsc/util/IOUtil.java | 17 + .../util/SingleIterator.java} | 32 +- .../com/reandroid/arsc/util/StringsUtil.java | 27 + .../reandroid/arsc/value/AttributeType.java | 3 + .../reandroid/arsc/value/AttributeValue.java | 45 +- .../reandroid/arsc/value/CompoundEntry.java | 11 +- .../java/com/reandroid/arsc/value/Entry.java | 103 +- .../com/reandroid/arsc/value/LibraryInfo.java | 46 +- .../arsc/value/ResTableMapEntry.java | 81 + .../com/reandroid/arsc/value/ResValueMap.java | 64 +- .../com/reandroid/arsc/value/ValueItem.java | 87 +- .../com/reandroid/arsc/value/ValueType.java | 55 +- .../arsc/value/attribute/AttributeBag.java | 6 +- .../value/attribute/AttributeBagItem.java | 46 +- .../arsc/value/plurals/PluralsBag.java | 238 +- .../arsc/value/plurals/PluralsBagItem.java | 266 +- .../arsc/value/plurals/PluralsQuantity.java | 4 + .../reandroid/arsc/value/style/StyleBag.java | 10 +- .../arsc/value/style/StyleBagItem.java | 41 +- .../java/com/reandroid/common/EntryStore.java | 28 - .../java/com/reandroid/common/Frameworks.java | 43 - .../java/com/reandroid/common/Namespace.java | 46 + .../reandroid/common/ReferenceResolver.java | 45 +- .../com/reandroid/common/TableEntryStore.java | 224 -- .../identifiers/PackageIdentifier.java | 114 +- .../identifiers/ResourceIdentifier.java | 12 +- .../identifiers/TableIdentifier.java | 12 - .../com/reandroid/xml/parser/MXParser.java | 2718 ----------------- .../xml/parser/MXParserCachingStrings.java | 171 -- .../xml/parser/MXParserNonValidating.java | 309 -- .../xml/parser/XMLDocumentParser.java | 54 +- .../reandroid/xml/parser/XMLSpanParser.java | 5 +- .../reandroid/xml/parser/XmlPullParser.java | 90 - .../xml/parser/XmlPullParserException.java | 46 - 106 files changed, 2777 insertions(+), 7048 deletions(-) delete mode 100644 src/main/java/com/reandroid/apk/ApkJsonEncoder.java delete mode 100644 src/main/java/com/reandroid/apk/xmldecoder/XMLBagDecoder.java delete mode 100644 src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java create mode 100644 src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespaceChunk.java delete mode 100644 src/main/java/com/reandroid/arsc/coder/Decoder.java delete mode 100755 src/main/java/com/reandroid/arsc/coder/ValueDecoder.java create mode 100755 src/main/java/com/reandroid/arsc/coder/XmlSanitizer.java delete mode 100755 src/main/java/com/reandroid/arsc/group/EntryGroup.java rename src/main/java/com/reandroid/arsc/{group => model}/ResourceEntry.java (73%) create mode 100644 src/main/java/com/reandroid/arsc/model/ResourceLibrary.java rename src/main/java/com/reandroid/{apk/ApkJsonDecoder.java => arsc/util/SingleIterator.java} (52%) delete mode 100755 src/main/java/com/reandroid/common/EntryStore.java delete mode 100755 src/main/java/com/reandroid/common/Frameworks.java create mode 100644 src/main/java/com/reandroid/common/Namespace.java delete mode 100755 src/main/java/com/reandroid/common/TableEntryStore.java delete mode 100644 src/main/java/com/reandroid/xml/parser/MXParser.java delete mode 100644 src/main/java/com/reandroid/xml/parser/MXParserCachingStrings.java delete mode 100644 src/main/java/com/reandroid/xml/parser/MXParserNonValidating.java delete mode 100644 src/main/java/com/reandroid/xml/parser/XmlPullParser.java delete mode 100644 src/main/java/com/reandroid/xml/parser/XmlPullParserException.java diff --git a/src/main/java/com/reandroid/apk/ApkJsonEncoder.java b/src/main/java/com/reandroid/apk/ApkJsonEncoder.java deleted file mode 100644 index 90895df6..00000000 --- a/src/main/java/com/reandroid/apk/ApkJsonEncoder.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.apk; - -/** - * Renamed, use com.reandroid.apk.ApkModuleJsonEncoder - * */ -@Deprecated -public class ApkJsonEncoder extends ApkModuleJsonEncoder{ - public ApkJsonEncoder(){ - super(); - } -} diff --git a/src/main/java/com/reandroid/apk/ApkModule.java b/src/main/java/com/reandroid/apk/ApkModule.java index b728ab9b..c60965ac 100644 --- a/src/main/java/com/reandroid/apk/ApkModule.java +++ b/src/main/java/com/reandroid/apk/ApkModule.java @@ -27,7 +27,6 @@ import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.coder.Decoder; import com.reandroid.arsc.group.StringGroup; import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.pool.TableStringPool; @@ -39,7 +38,6 @@ import com.reandroid.identifiers.PackageIdentifier; import com.reandroid.xml.XMLDocument; import com.reandroid.xml.XMLElement; -import com.reandroid.xml.XMLException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -59,7 +57,6 @@ public class ApkModule implements ApkFile, Closeable { private InputSource mManifestOriginalSource; private final UncompressedFiles mUncompressedFiles; private APKLogger apkLogger; - private Decoder mDecoder; private ApkType mApkType; private ApkSignatureBlock apkSignatureBlock; private Integer preferredFramework; @@ -438,11 +435,17 @@ public List removeResFiles(int resourceId, ResConfig resConfig) { } return results; } - public XMLDocument decodeXMLFile(String path) throws IOException, XMLException { + public XMLDocument decodeXMLFile(String path) throws IOException { ResXmlDocument resXmlDocument = loadResXmlDocument(path); AndroidManifestBlock manifestBlock = getAndroidManifestBlock(); int pkgId = manifestBlock.guessCurrentPackageId(); - return resXmlDocument.decodeToXml(getTableBlock(), pkgId); + if(pkgId != 0 && hasTableBlock()){ + PackageBlock packageBlock = getTableBlock().pickOne(pkgId); + if(packageBlock != null){ + resXmlDocument.setPackageBlock(packageBlock); + } + } + return resXmlDocument.decodeToXml(); } public List listDexFiles(){ List results=new ArrayList<>(); @@ -801,16 +804,12 @@ public ResXmlDocument loadResXmlDocument(InputSource inputSource) throws IOExcep ResXmlDocument resXmlDocument = new ResXmlDocument(); resXmlDocument.setApkFile(this); resXmlDocument.readBytes(inputSource.openStream()); + TableBlock tableBlock = getTableBlock(); + if(tableBlock != null){ + resXmlDocument.setPackageBlock(tableBlock.pickOne()); + } return resXmlDocument; } - @Override - public Decoder getDecoder(){ - return mDecoder; - } - @Override - public void setDecoder(Decoder decoder){ - this.mDecoder = decoder; - } public ApkType getApkType(){ if(mApkType!=null){ return mApkType; diff --git a/src/main/java/com/reandroid/apk/ApkModuleXmlDecoder.java b/src/main/java/com/reandroid/apk/ApkModuleXmlDecoder.java index 0cb52a7e..73307602 100644 --- a/src/main/java/com/reandroid/apk/ApkModuleXmlDecoder.java +++ b/src/main/java/com/reandroid/apk/ApkModuleXmlDecoder.java @@ -132,7 +132,7 @@ private void decodeResXml(File mainDirectory, ResFile resFile) InputSource inputSource = resFile.getInputSource(); logVerbose(inputSource.getAlias()); - serializeXml(packageBlock.getId(), resFile.getInputSource(), file); + serializeXml(packageBlock, resFile.getInputSource(), file); if(!keepResPath()){ addDecodedEntry(entry); @@ -156,7 +156,7 @@ private File toDecodeResFile(File mainDirectory, ResFile resFile, PackageBlock p private ResXmlDocumentSerializer getDocumentSerializer(){ if(documentSerializer == null){ documentSerializer = new ResXmlDocumentSerializer(getApkModule()); - documentSerializer.setValidateXmlNamespace(true); + documentSerializer.setFixAttributesNamespace(true); } return documentSerializer; } @@ -216,8 +216,16 @@ private void decodeAndroidManifestXml(File mainDirectory) AndroidManifestBlock manifestBlock = getApkModule().getAndroidManifestBlock(); File file = new File(mainDirectory, AndroidManifestBlock.FILE_NAME); logMessage("Decoding: " + file.getName()); - int currentPackageId = manifestBlock.guessCurrentPackageId(); - serializeXml(currentPackageId, manifestBlock, file); + PackageBlock packageBlock = manifestBlock.getPackageBlock(); + if(packageBlock == null){ + int packageId = manifestBlock.guessCurrentPackageId(); + TableBlock tableBlock = getApkModule().getTableBlock(); + packageBlock = tableBlock.pickOne(packageId); + if(packageBlock == null){ + packageBlock = tableBlock.pickOne(); + } + } + serializeXml(packageBlock, manifestBlock, file); addDecodedPath(AndroidManifestBlock.FILE_NAME); } private void decodeAndroidManifestBin(File mainDirectory) @@ -232,29 +240,28 @@ private void decodeAndroidManifestBin(File mainDirectory) inputSource.write(file); addDecodedPath(AndroidManifestBlock.FILE_NAME); } - private void serializeXml(int currentPackageId, ResXmlDocument document, File outFile) + private void serializeXml(PackageBlock packageBlock, ResXmlDocument document, File outFile) throws IOException { - XMLNamespaceValidator.validateNamespaces(document); ResXmlDocumentSerializer serializer = getDocumentSerializer(); - if(currentPackageId != 0){ - serializer.getDecoder().setCurrentPackageId(currentPackageId); + if(packageBlock != null){ + document.setPackageBlock(packageBlock); + serializer.setCurrentPackage(packageBlock); } + document.autoSetAttributeNamespaces(); try { serializer.write(document, outFile); } catch (XmlPullParserException ex) { throw new IOException("Error: "+outFile.getName(), ex); } } - private void serializeXml(int currentPackageId, InputSource inputSource, File outFile) + private void serializeXml(PackageBlock packageBlock, InputSource inputSource, File outFile) throws IOException { ResXmlDocumentSerializer serializer = getDocumentSerializer(); - if(currentPackageId != 0){ - serializer.getDecoder().setCurrentPackageId(currentPackageId); - } + serializer.setCurrentPackage(packageBlock); try { serializer.write(inputSource, outFile); } catch (XmlPullParserException ex) { - throw new IOException("Error: "+outFile.getName(), ex); + throw new IOException("Error: " + outFile.getName(), ex); } } private void addDecodedEntry(Entry entry){ @@ -298,7 +305,7 @@ private void decodeValues(File resDir, SpecTypePair specTypePair) throws IOExcep } private XMLEntryDecoderSerializer getEntrySerializer(){ if(this.entrySerializer == null){ - this.entrySerializer = new XMLEntryDecoderSerializer(getApkModule().getTableBlock()); + this.entrySerializer = new XMLEntryDecoderSerializer(); this.entrySerializer.setDecodedEntries(this); } return entrySerializer; diff --git a/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java b/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java index dfe42a9a..c42d5ecb 100644 --- a/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java +++ b/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java @@ -81,6 +81,7 @@ private void encodeManifestXml(File mainDirectory) { PackageBlock packageBlock = encodeMaterials.pickMainPackageBlock(tableBlock); if(packageBlock != null){ encodeMaterials.setCurrentPackage(packageBlock); + tableBlock.setCurrentPackage(packageBlock); } XMLSource xmlSource = new XMLFileSource(AndroidManifestBlock.FILE_NAME, file); diff --git a/src/main/java/com/reandroid/apk/FrameworkApk.java b/src/main/java/com/reandroid/apk/FrameworkApk.java index 6c7bf0be..fdba66df 100644 --- a/src/main/java/com/reandroid/apk/FrameworkApk.java +++ b/src/main/java/com/reandroid/apk/FrameworkApk.java @@ -247,8 +247,7 @@ public static boolean isFramework(ApkModule apkModule) { } public static boolean isFramework(AndroidManifestBlock manifestBlock){ ResXmlElement root = manifestBlock.getManifestElement(); - ResXmlAttribute attribute = root.getStartElement() - .searchAttributeByName(AndroidManifestBlock.NAME_coreApp); + ResXmlAttribute attribute = root.searchAttributeByName(AndroidManifestBlock.NAME_coreApp); if(attribute==null || attribute.getValueType()!= ValueType.BOOLEAN){ return false; } diff --git a/src/main/java/com/reandroid/apk/FrameworkOptimizer.java b/src/main/java/com/reandroid/apk/FrameworkOptimizer.java index a02f7ccc..16e2f10a 100644 --- a/src/main/java/com/reandroid/apk/FrameworkOptimizer.java +++ b/src/main/java/com/reandroid/apk/FrameworkOptimizer.java @@ -22,7 +22,7 @@ import com.reandroid.arsc.chunk.xml.ResXmlAttribute; import com.reandroid.arsc.chunk.xml.ResXmlElement; import com.reandroid.arsc.chunk.xml.ResXmlNode; -import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.util.FrameworkTable; @@ -228,8 +228,8 @@ private Entry getEntryWithValue(TableBlock tableBlock, int resourceId, Set entryList = this.entryList; - if(entryList.size()==0){ + if(entryList.size() == 0){ return null; } - for(Entry entry :entryList){ + if(entryList.size() == 1){ + String p1 = entryList.get(0).getResValue().getValueAsString(); + String p2 = getInputSource().getName(); + if(p1.equals(p2)){ + return entryList.get(entryList.size()-1); + } + return entryList.get(0); + } + String path = getInputSource().getName(); + for(Entry entry : entryList){ + ResValue resValue = entry.getResValue(); + if(resValue == null || resValue.getValueType() != ValueType.STRING){ + continue; + } + if(path.equals(resValue.getValueAsString())){ + return entry; + } + } + for(Entry entry : entryList){ if(!entry.isNull() && entry.isDefault()){ return entry; } } - for(Entry entry :entryList){ + for(Entry entry : entryList){ if(!entry.isNull()){ return entry; } } - for(Entry entry :entryList){ + for(Entry entry : entryList){ if(entry.isDefault()){ return entry; } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoder.java b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoder.java index ba597822..0c05b4a7 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoder.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoder.java @@ -16,12 +16,10 @@ package com.reandroid.apk.xmldecoder; import com.reandroid.arsc.value.ResTableMapEntry; -import com.reandroid.common.EntryStore; -import com.reandroid.xml.XMLElement; abstract class BagDecoder extends DecoderTableEntry { - public BagDecoder(EntryStore entryStore){ - super(entryStore); + public BagDecoder(){ + super(); } public abstract boolean canDecode(ResTableMapEntry mapEntry); } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderArray.java b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderArray.java index 20cb604d..d4252af8 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderArray.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderArray.java @@ -18,17 +18,15 @@ import com.reandroid.apk.ApkUtil; import com.reandroid.apk.XmlHelper; import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.coder.ValueDecoder; import com.reandroid.arsc.value.*; -import com.reandroid.common.EntryStore; import java.io.IOException; import java.util.HashSet; import java.util.Set; class BagDecoderArray extends BagDecoder{ - public BagDecoderArray(EntryStore entryStore) { - super(entryStore); + public BagDecoderArray() { + super(); } @Override @@ -40,7 +38,6 @@ public OUTPUT decode(ResTableMapEntry mapEntry, EntryWriter writer) thro writer.attribute("name", entry.getName()); PackageBlock packageBlock = entry.getPackageBlock(); - EntryStore entryStore = getEntryStore(); ResValueMap[] resValueMaps = mapEntry.listResValueMap(); boolean zero_name = isZeroNameArray(resValueMaps); @@ -52,8 +49,7 @@ public OUTPUT decode(ResTableMapEntry mapEntry, EntryWriter writer) thro writer.writeTagIndent(INDENT_BAG); writer.startTag(childTag); if(zero_name){ - String name = ValueDecoder.decodeAttributeName( - entryStore, packageBlock, valueMap.getName()); + String name = valueMap.decodeName(); writer.attribute("name", name); } writeText(writer, packageBlock, valueMap); diff --git a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java index 03c28736..7ffdc6ab 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java @@ -17,18 +17,14 @@ import com.reandroid.apk.XmlHelper; import com.reandroid.arsc.array.CompoundItemArray; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.coder.ValueDecoder; -import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.value.*; import com.reandroid.arsc.value.attribute.AttributeBag; -import com.reandroid.common.EntryStore; import java.io.IOException; class BagDecoderAttr extends BagDecoder{ - public BagDecoderAttr(EntryStore entryStore){ - super(entryStore); + public BagDecoderAttr(){ + super(); } @Override @@ -45,9 +41,6 @@ public OUTPUT decode(ResTableMapEntry mapEntry, EntryWriter writer) thro ResValueMap[] bagItems = mapEntry.listResValueMap(); - EntryStore entryStore = getEntryStore(); - PackageBlock packageBlock = entry.getPackageBlock(); - boolean hasBags = false; for(int i = 0; i < bagItems.length; i++){ @@ -58,17 +51,8 @@ public OUTPUT decode(ResTableMapEntry mapEntry, EntryWriter writer) thro } writer.writeTagIndent(INDENT_BAG); writer.startTag(bagType.getName()); - String name = ValueDecoder.decodeAttributeName( - entryStore, packageBlock, valueMap.getName()); - writer.attribute("name", name); - int rawVal = valueMap.getData(); - String value; - if(valueMap.getValueType() == ValueType.HEX){ - value = HexUtil.toHex8(rawVal); - }else { - value = Integer.toString(rawVal); - } - writer.text(value); + writer.attribute("name", valueMap.decodeName()); + writer.text(valueMap.decodeValue()); writer.endTag(bagType.getName()); hasBags = true; diff --git a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderCommon.java b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderCommon.java index 9dce7bd2..abf711a9 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderCommon.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderCommon.java @@ -16,19 +16,14 @@ package com.reandroid.apk.xmldecoder; import com.reandroid.apk.XmlHelper; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.coder.ValueDecoder; import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ResValueMap; -import com.reandroid.arsc.value.ValueType; -import com.reandroid.common.EntryStore; - import java.io.IOException; class BagDecoderCommon extends BagDecoder{ - public BagDecoderCommon(EntryStore entryStore) { - super(entryStore); + public BagDecoderCommon() { + super(); } @Override @@ -39,21 +34,10 @@ public OUTPUT decode(ResTableMapEntry mapEntry, EntryWriter writer) thro writer.startTag(tag); writer.attribute("name", entry.getName()); - PackageBlock packageBlock = entry.getPackageBlock(); - - int parentId = mapEntry.getParentId(); - String parent; - if(parentId != 0){ - parent = ValueDecoder.decodeEntryValue(getEntryStore(), - packageBlock, ValueType.REFERENCE, parentId); - }else { - parent = null; - } + String parent = mapEntry.decodeParentId(); if(parent != null){ writer.attribute("parent", parent); } - - EntryStore entryStore = getEntryStore(); ResValueMap[] resValueMaps = mapEntry.listResValueMap(); boolean hasBags = false; @@ -64,8 +48,7 @@ public OUTPUT decode(ResTableMapEntry mapEntry, EntryWriter writer) thro writer.writeTagIndent(INDENT_BAG); writer.startTag(childTag); - String name = ValueDecoder.decodeAttributeName( - entryStore, packageBlock, valueMap.getName()); + String name = valueMap.decodeName(); writer.attribute("name", name); writeText(writer, valueMap); diff --git a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderPlural.java b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderPlural.java index b1977e0c..1aaaf6a9 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderPlural.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderPlural.java @@ -18,14 +18,12 @@ import com.reandroid.apk.XmlHelper; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.value.*; -import com.reandroid.arsc.value.plurals.PluralsQuantity; -import com.reandroid.common.EntryStore; import java.io.IOException; class BagDecoderPlural extends BagDecoder{ - public BagDecoderPlural(EntryStore entryStore) { - super(entryStore); + public BagDecoderPlural() { + super(); } @Override @@ -81,14 +79,8 @@ public static boolean isResBagPluralsValue(ResTableMapEntry valueItem){ int len=bagItems.length; for(int i=0;i> 16) & 0xffff; - if(high!=0x0100){ - return false; - } - int low = name & 0xffff; - PluralsQuantity pq=PluralsQuantity.valueOf((short) low); - if(pq==null){ + AttributeType attributeType = item.getAttributeType(); + if(attributeType == null || !attributeType.isPlural()){ return false; } } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntry.java b/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntry.java index c5af0a55..18f1b514 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntry.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntry.java @@ -21,13 +21,12 @@ import com.reandroid.arsc.value.ResTableEntry; import com.reandroid.arsc.value.ResValue; import com.reandroid.arsc.value.ValueType; -import com.reandroid.common.EntryStore; import java.io.IOException; public class DecoderResTableEntry extends DecoderTableEntry { - public DecoderResTableEntry(EntryStore entryStore){ - super(entryStore); + public DecoderResTableEntry(){ + super(); } @Override public OUTPUT decode(ResTableEntry tableEntry, EntryWriter writer) throws IOException{ @@ -39,11 +38,9 @@ public OUTPUT decode(ResTableEntry tableEntry, EntryWriter writer) throw ResValue value = tableEntry.getValue(); ValueType valueType = value.getValueType(); - if(!isReference(valueType)){ - CommonType commonType = CommonType.valueOf(tag); - if(commonType != null && !commonType.contains(valueType)){ - writer.attribute("type", valueType.getTypeName()); - } + CommonType commonType = CommonType.valueOf(tag); + if(commonType != null && commonType.isDifferent(valueType)){ + writer.attribute("type", valueType.getTypeName()); } if(!isId(tag)){ writeText(writer, entry.getPackageBlock(), tableEntry.getValue()); diff --git a/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntryMap.java b/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntryMap.java index f0e7cff2..5ffb6372 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntryMap.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/DecoderResTableEntryMap.java @@ -16,7 +16,6 @@ package com.reandroid.apk.xmldecoder; import com.reandroid.arsc.value.ResTableMapEntry; -import com.reandroid.common.EntryStore; import java.io.IOException; @@ -24,15 +23,15 @@ class DecoderResTableEntryMap extends DecoderTableEntry bagDecoderCommon; - public DecoderResTableEntryMap(EntryStore entryStore) { - super(entryStore); + public DecoderResTableEntryMap() { + super(); this.decoderList = new Object[] { - new BagDecoderAttr<>(entryStore), - new BagDecoderPlural<>(entryStore), - new BagDecoderArray<>(entryStore) + new BagDecoderAttr<>(), + new BagDecoderPlural<>(), + new BagDecoderArray<>() }; - this.bagDecoderCommon = new BagDecoderCommon<>(entryStore); + this.bagDecoderCommon = new BagDecoderCommon<>(); } @Override diff --git a/src/main/java/com/reandroid/apk/xmldecoder/DecoderTableEntry.java b/src/main/java/com/reandroid/apk/xmldecoder/DecoderTableEntry.java index 2acd8b67..25190dc1 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/DecoderTableEntry.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/DecoderTableEntry.java @@ -16,19 +16,12 @@ package com.reandroid.apk.xmldecoder; import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.coder.ValueDecoder; import com.reandroid.arsc.value.*; -import com.reandroid.common.EntryStore; import java.io.IOException; abstract class DecoderTableEntry, OUTPUT> { - private final EntryStore entryStore; - public DecoderTableEntry(EntryStore entryStore){ - this.entryStore = entryStore; - } - public EntryStore getEntryStore() { - return entryStore; + public DecoderTableEntry(){ } public abstract OUTPUT decode(INPUT tableEntry, EntryWriter writer) throws IOException; @@ -38,11 +31,7 @@ void writeText(EntryWriter writer, PackageBlock packageBlock, ValueItem value if(valueItem.getValueType() == ValueType.STRING){ XMLDecodeHelper.writeTextContent(writer, valueItem.getDataAsPoolString()); }else { - String value = ValueDecoder.decodeEntryValue( - getEntryStore(), - packageBlock, - valueItem.getValueType(), - valueItem.getData()); + String value = valueItem.decodeValue(); writer.text(value); } } @@ -51,10 +40,7 @@ void writeText(EntryWriter writer, ResValueMap attributeValue) if(attributeValue.getValueType() == ValueType.STRING){ XMLDecodeHelper.writeTextContent(writer, attributeValue.getDataAsPoolString()); }else { - String value = ValueDecoder.decode(getEntryStore(), - attributeValue.getPackageBlock().getId(), - attributeValue); - writer.text(value); + writer.text(attributeValue.decodeValue()); } } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/ResXmlDocumentSerializer.java b/src/main/java/com/reandroid/apk/xmldecoder/ResXmlDocumentSerializer.java index 128f123c..89f46571 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/ResXmlDocumentSerializer.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/ResXmlDocumentSerializer.java @@ -18,9 +18,10 @@ import com.android.org.kxml2.io.KXmlSerializer; import com.reandroid.apk.ApkModule; import com.reandroid.archive.InputSource; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlPullParser; -import com.reandroid.arsc.coder.Decoder; import com.reandroid.xml.XmlParserToSerializer; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -33,7 +34,7 @@ public class ResXmlDocumentSerializer implements ResXmlPullParser.DocumentLoaded private final ResXmlPullParser parser; private final XmlSerializer serializer; private final XmlParserToSerializer parserToSerializer; - private boolean validateXmlNamespace; + private boolean fixAttributesNamespace; private String mCurrentPath; public ResXmlDocumentSerializer(ResXmlPullParser parser){ this.parser = parser; @@ -41,11 +42,14 @@ public ResXmlDocumentSerializer(ResXmlPullParser parser){ this.parserToSerializer = new XmlParserToSerializer(parser, serializer); this.parser.setDocumentLoadedListener(this); } - public ResXmlDocumentSerializer(Decoder decoder){ - this(new ResXmlPullParser(decoder)); + public ResXmlDocumentSerializer(PackageBlock currentPackage){ + this(new ResXmlPullParser(currentPackage)); } public ResXmlDocumentSerializer(ApkModule apkModule){ - this(createDecoder(apkModule)); + this(pickPackage(apkModule)); + } + public void setCurrentPackage(PackageBlock packageBlock){ + this.parser.setCurrentPackage(packageBlock); } public void write(InputSource inputSource, File file) @@ -109,19 +113,15 @@ public void write(ResXmlDocument xmlDocument, Writer writer) writer.flush(); } } - public Decoder getDecoder(){ - return parser.getDecoder(); - } - public void setValidateXmlNamespace(boolean validateXmlNamespace) { - this.validateXmlNamespace = validateXmlNamespace; + public void setFixAttributesNamespace(boolean fixAttributesNamespace) { + this.fixAttributesNamespace = fixAttributesNamespace; } @Override public ResXmlDocument onDocumentLoaded(ResXmlDocument resXmlDocument) { - if(!validateXmlNamespace){ - return resXmlDocument; + if(fixAttributesNamespace){ + resXmlDocument.autoSetAttributeNamespaces(); } - XMLNamespaceValidator.validateNamespaces(resXmlDocument); return resXmlDocument; } private IOException getError(Exception exception){ @@ -146,9 +146,16 @@ private IOException getError(Exception exception){ return otherException; } - private static Decoder createDecoder(ApkModule apkModule){ - Decoder decoder = Decoder.create(apkModule.getTableBlock()); - decoder.setApkFile(apkModule); - return decoder; + private static PackageBlock pickPackage(ApkModule apkModule){ + TableBlock tableBlock = apkModule.getTableBlock(); + if(tableBlock == null){ + throw new NullPointerException("Does not have resource table"); + } + if(tableBlock.countPackages() == 0){ + // Empty resource table, create dummy package + tableBlock = new TableBlock(); + return tableBlock.newPackage(0, null); + } + return tableBlock.pickOne(); } } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/XMLBagDecoder.java b/src/main/java/com/reandroid/apk/xmldecoder/XMLBagDecoder.java deleted file mode 100644 index 688eaf87..00000000 --- a/src/main/java/com/reandroid/apk/xmldecoder/XMLBagDecoder.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.apk.xmldecoder; - -import com.reandroid.arsc.value.ResTableMapEntry; -import com.reandroid.common.EntryStore; -import com.reandroid.xml.XMLElement; - -import java.io.IOException; - -@Deprecated -public class XMLBagDecoder { - private final DecoderResTableEntryMap mDocumentDecoder; - private final EntryWriterElement mWriter; - public XMLBagDecoder(EntryStore entryStore){ - mDocumentDecoder = new DecoderResTableEntryMap<>(entryStore); - mWriter = new EntryWriterElement(); - } - public void decode(ResTableMapEntry mapEntry, XMLElement parentElement){ - try { - XMLElement child = mDocumentDecoder.decode(mapEntry, mWriter); - parentElement.addChild(child); - } catch (IOException exception) { - } - } -} diff --git a/src/main/java/com/reandroid/apk/xmldecoder/XMLDecodeHelper.java b/src/main/java/com/reandroid/apk/xmldecoder/XMLDecodeHelper.java index f0421f6f..f1463bc7 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/XMLDecodeHelper.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/XMLDecodeHelper.java @@ -15,7 +15,7 @@ */ package com.reandroid.apk.xmldecoder; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.arsc.item.StringItem; import com.reandroid.xml.*; import com.reandroid.xml.parser.XMLSpanParser; @@ -30,8 +30,8 @@ public static boolean writeTextContent(EntryWriter writer, StringItem stringI } if(!stringItem.hasStyle()){ String text = stringItem.get(); - text = ValueDecoder.escapeSpecialCharacter(text); - text = ValueDecoder.quoteWhitespace(text); + text = XmlSanitizer.escapeSpecialCharacter(text); + text = XmlSanitizer.quoteWhitespace(text); writer.text(text); return false; }else { @@ -51,7 +51,7 @@ public static void writeParsedSpannable(EntryWriter writer, XMLElement spanna if(xmlNode instanceof XMLText){ String text = ((XMLText)xmlNode).getText(true); writer.enableIndent(false); - writer.text(ValueDecoder.escapeSpecialCharacter(text)); + writer.text(XmlSanitizer.escapeSpecialCharacter(text)); }else if(xmlNode instanceof XMLElement){ writeElement(writer, (XMLElement) xmlNode); } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoder.java b/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoder.java index ad9fc1f0..4ecac535 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoder.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoder.java @@ -16,9 +16,9 @@ package com.reandroid.apk.xmldecoder; import com.reandroid.arsc.chunk.TypeBlock; -import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.container.SpecTypePair; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.value.*; -import com.reandroid.common.EntryStore; import java.io.File; import java.io.IOException; @@ -32,9 +32,9 @@ public class XMLEntryDecoder{ private final DecoderResTableEntryMap decoderEntryMap; private Predicate mDecodedEntries; - public XMLEntryDecoder(EntryStore entryStore){ - this.decoderEntry = new DecoderResTableEntry<>(entryStore); - this.decoderEntryMap = new DecoderResTableEntryMap<>(entryStore); + public XMLEntryDecoder(){ + this.decoderEntry = new DecoderResTableEntry<>(); + this.decoderEntryMap = new DecoderResTableEntryMap<>(); } public void setDecodedEntries(Predicate decodedEntries) { @@ -73,22 +73,17 @@ public int decode(EntryWriter writer, Collection entryList) throw } return count; } - public int decode(EntryWriter writer, ResConfig resConfig, Collection entryGroupList) throws IOException { - int count = 0; - for(EntryGroup entryGroup : entryGroupList){ - OUTPUT output = decode(writer, entryGroup.getEntry(resConfig)); - if(output != null){ - count ++; - } - } - return count; - } public int decode(EntryWriter writer, TypeBlock typeBlock) throws IOException { - Iterator iterator = typeBlock.getEntryArray() - .iterator(true); + SpecTypePair specTypePair = typeBlock.getParentSpecTypePair(); + ResConfig resConfig = typeBlock.getResConfig(); + Iterator resources = specTypePair.getResources(); int count = 0; - while (iterator.hasNext()){ - Entry entry = iterator.next(); + while (resources.hasNext()){ + ResourceEntry resourceEntry = resources.next(); + Entry entry = resourceEntry.get(resConfig); + if(entry == null){ + continue; + } OUTPUT output = decode(writer, entry); if(output != null){ count++; diff --git a/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderDocument.java b/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderDocument.java index 9313dbfc..c4a47b09 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderDocument.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderDocument.java @@ -17,7 +17,6 @@ import com.reandroid.apk.XmlHelper; import com.reandroid.arsc.value.Entry; -import com.reandroid.common.EntryStore; import com.reandroid.xml.XMLDocument; import com.reandroid.xml.XMLElement; @@ -26,8 +25,8 @@ public class XMLEntryDecoderDocument extends XMLEntryDecoder{ private final EntryWriterElement entryWriterElement; - public XMLEntryDecoderDocument(EntryStore entryStore) { - super(entryStore); + public XMLEntryDecoderDocument() { + super(); this.entryWriterElement = new EntryWriterElement(); } diff --git a/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderSerializer.java b/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderSerializer.java index 3ba4c381..031428dd 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderSerializer.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/XMLEntryDecoderSerializer.java @@ -19,50 +19,27 @@ import com.reandroid.apk.XmlHelper; import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.value.ResConfig; -import com.reandroid.common.EntryStore; import org.xmlpull.v1.XmlSerializer; import java.io.*; import java.nio.charset.StandardCharsets; -import java.util.Collection; import java.util.Iterator; -import java.util.List; public class XMLEntryDecoderSerializer extends XMLEntryDecoder implements Closeable { private final EntryWriterSerializer entryWriterSerializer; private Closeable mClosable; private boolean mStart; - public XMLEntryDecoderSerializer(EntryStore entryStore, XmlSerializer serializer) { - super(entryStore); + public XMLEntryDecoderSerializer(XmlSerializer serializer) { + super(); this.entryWriterSerializer = new EntryWriterSerializer(serializer); } - public XMLEntryDecoderSerializer(EntryStore entryStore) { - this(entryStore, new KXmlSerializer()); + public XMLEntryDecoderSerializer() { + this(new KXmlSerializer()); } public int decode(File resDirectory, SpecTypePair specTypePair) throws IOException { - int count; - if(specTypePair.hasDuplicateResConfig(true)){ - count = decodeDuplicateConfigs(resDirectory, specTypePair); - }else { - count = decodeUniqueConfigs(resDirectory, specTypePair); - } - return count; - } - private int decodeDuplicateConfigs(File resDirectory, SpecTypePair specTypePair) throws IOException { - List resConfigList = specTypePair.listResConfig(); - Collection entryGroupList = specTypePair - .createEntryGroups(true).values(); - int total = 0; - for(ResConfig resConfig : resConfigList){ - TypeBlock typeBlock = resConfig.getParentInstance(TypeBlock.class); - File outXml = toOutXmlFile(resDirectory, typeBlock); - total += decode(outXml, resConfig, entryGroupList); - } - return total; + return decodeUniqueConfigs(resDirectory, specTypePair); } private int decodeUniqueConfigs(File resDirectory, SpecTypePair specTypePair) throws IOException { int total = 0; @@ -74,13 +51,6 @@ private int decodeUniqueConfigs(File resDirectory, SpecTypePair specTypePair) th } return total; } - public int decode(File outXmlFile, ResConfig resConfig, Collection entryGroupList) throws IOException { - setOutput(outXmlFile); - int count = decode(resConfig, entryGroupList); - close(); - deleteIfZero(count, outXmlFile); - return count; - } public int decode(File outXmlFile, TypeBlock typeBlock) throws IOException { setOutput(outXmlFile); int count = super.decode(entryWriterSerializer, typeBlock); @@ -88,8 +58,8 @@ public int decode(File outXmlFile, TypeBlock typeBlock) throws IOException { deleteIfZero(count, outXmlFile); return count; } - public int decode(ResConfig resConfig, Collection entryGroupList) throws IOException { - return super.decode(entryWriterSerializer, resConfig, entryGroupList); + public int decode(TypeBlock typeBlock) throws IOException { + return super.decode(entryWriterSerializer, typeBlock); } public void setOutput(File file) throws IOException { File dir = file.getParentFile(); diff --git a/src/main/java/com/reandroid/apk/xmldecoder/XMLNamespaceValidator.java b/src/main/java/com/reandroid/apk/xmldecoder/XMLNamespaceValidator.java index 1f42220f..87869f02 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/XMLNamespaceValidator.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/XMLNamespaceValidator.java @@ -15,10 +15,19 @@ */ package com.reandroid.apk.xmldecoder; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.xml.*; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +/** + * Redundant class, use {@link ResXmlAttribute#autoSetNamespace()} + * or use {@link ResXmlDocument#autoSetAttributeNamespaces()} + * */ +@Deprecated public class XMLNamespaceValidator { private static final String URI_ANDROID = "http://schemas.android.com/apk/res/android"; private static final String URI_APP = "http://schemas.android.com/apk/res-auto"; @@ -29,10 +38,21 @@ public XMLNamespaceValidator(ResXmlDocument xmlBlock){ this.xmlBlock=xmlBlock; } public void validate(){ - validateNamespaces(xmlBlock); + validateNamespaces(xmlBlock.getResXmlElement()); + List post = listPackagePostfixes(); + post.size(); + } + private List listPackagePostfixes(){ + TableBlock tableBlock = xmlBlock.getPackageBlock().getTableBlock(); + List results = new ArrayList<>(); + for(PackageBlock packageBlock : tableBlock.listPackages()){ + String name = packageBlock.getPrefix(); + results.add(name); + } + return results; } - public static boolean isValid(ResXmlAttribute attribute){ + public boolean isValid(ResXmlAttribute attribute){ int resourceId = attribute.getNameResourceID(); if(resourceId == 0){ return attribute.getUri() == null; @@ -44,21 +64,22 @@ public static boolean isValid(ResXmlAttribute attribute){ } } public static void validateNamespaces(ResXmlDocument resXmlDocument){ - validateNamespaces(resXmlDocument.getResXmlElement()); + XMLNamespaceValidator validator = new XMLNamespaceValidator(resXmlDocument); + validator.validate(); } - public static void validateNamespaces(ResXmlElement element){ + public void validateNamespaces(ResXmlElement element){ validateNamespaces(element.listAttributes()); for(ResXmlElement child : element.listElements()){ validateNamespaces(child); } } - private static void validateNamespaces(Collection attributeList){ + private void validateNamespaces(Collection attributeList){ for(ResXmlAttribute attribute : attributeList){ validateNamespace(attribute); } } - private static void validateNamespace(ResXmlAttribute attribute){ + private void validateNamespace(ResXmlAttribute attribute){ int resourceId = attribute.getNameResourceID(); if(resourceId == 0){ attribute.setNamespaceReference(-1); @@ -75,7 +96,7 @@ private static void validateNamespace(ResXmlAttribute attribute){ } } - private static boolean isValidAppNamespace(ResXmlAttribute attribute){ + private boolean isValidAppNamespace(ResXmlAttribute attribute){ String uri = attribute.getUri(); String prefix = attribute.getNamePrefix(); if(URI_ANDROID.equals(uri) || PREFIX_ANDROID.equals(prefix)){ diff --git a/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java b/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java index 18f6648e..ae102165 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java @@ -20,7 +20,7 @@ import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.coder.*; -import com.reandroid.arsc.group.ResourceEntry; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.util.FrameworkTable; import com.reandroid.arsc.coder.EncodeResult; import com.reandroid.arsc.value.Entry; diff --git a/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java index 084d959e..6f281315 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java @@ -20,7 +20,7 @@ import com.reandroid.archive.InputSource; import com.reandroid.apk.ApkUtil; import com.reandroid.apk.UncompressedFiles; -import com.reandroid.arsc.group.ResourceEntry; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.value.Entry; import com.reandroid.xml.source.XMLFileSource; import com.reandroid.xml.source.XMLSource; diff --git a/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java deleted file mode 100644 index bfb4c1bf..00000000 --- a/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.apk.xmlencoder; - -import com.reandroid.apk.ApkModule; -import com.reandroid.arsc.chunk.TableBlock; - -/** - * Renamed, use com.reandroid.apk.xmlencoder.XMLTableBlockEncoder - * */ -@Deprecated -public class RESEncoder extends XMLTableBlockEncoder{ - public RESEncoder(){ - super(); - } - public RESEncoder(ApkModule module, TableBlock block){ - super(module, block); - } -} diff --git a/src/main/java/com/reandroid/apk/xmlencoder/ResourceValuesEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/ResourceValuesEncoder.java index 6ed02e9d..7871636b 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/ResourceValuesEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/ResourceValuesEncoder.java @@ -16,6 +16,7 @@ package com.reandroid.apk.xmlencoder; import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.util.IOUtil; import com.reandroid.arsc.value.Entry; import com.reandroid.xml.XMLDocument; import com.reandroid.xml.XMLElement; @@ -56,9 +57,7 @@ public void encodeValuesXml(File valuesXmlFile) throws XMLException { if(valuesXmlFile.getName().equals("public.xml")){ return; } - String simpleName = valuesXmlFile.getParentFile().getName() - +File.separator+valuesXmlFile.getName(); - materials.logVerbose("Encoding: "+simpleName); + materials.logVerbose("Encoding: " + IOUtil.shortPath(valuesXmlFile, 4)); String type = EncodeUtil.getTypeNameFromValuesXml(valuesXmlFile); String qualifiers = EncodeUtil.getQualifiersFromValuesXml(valuesXmlFile); diff --git a/src/main/java/com/reandroid/apk/xmlencoder/ValuesStringPoolBuilder.java b/src/main/java/com/reandroid/apk/xmlencoder/ValuesStringPoolBuilder.java index 139b554e..e3a4d77c 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/ValuesStringPoolBuilder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/ValuesStringPoolBuilder.java @@ -17,7 +17,7 @@ import com.reandroid.arsc.array.StringArray; import com.reandroid.arsc.array.StyleArray; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.arsc.item.StyleItem; import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.pool.TableStringPool; @@ -157,7 +157,7 @@ private void addStrings(XMLElement element){ if(element.hasChildElements()){ addStyleElement(element); }else { - String text = ValueDecoder + String text = XmlSanitizer .unEscapeUnQuote(element.getTextContent()); addString(text); } diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java index 2c703b12..ea938fd4 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java @@ -88,6 +88,7 @@ public ResXmlDocument getResXmlBlock() throws IOException{ PackageBlock packageBlock = getEntryPackageBlock(); if(packageBlock != null && packageBlock != currentPackage){ encodeMaterials.setCurrentPackage(packageBlock); + currentPackage.getTableBlock().setCurrentPackage(currentPackage); } resXmlDocument = xmlFileEncoder.encode(xmlSource.getXMLDocument()); } catch (XMLException ex) { diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLFileEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLFileEncoder.java index 4abe70f8..517a3605 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLFileEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLFileEncoder.java @@ -19,10 +19,9 @@ import com.reandroid.arsc.chunk.xml.*; import com.reandroid.arsc.coder.EncodeResult; import com.reandroid.arsc.coder.ValueCoder; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.arsc.value.AttributeDataFormat; import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.attribute.AttributeBag; import com.reandroid.xml.*; @@ -125,7 +124,7 @@ private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){ } ResXmlAttribute xmlAttribute = resXmlElement.createAttribute(name, resourceId); if(prefix != null){ - ResXmlStartNamespace ns = resXmlElement.getStartNamespaceByPrefix(prefix); + ResXmlNamespace ns = resXmlElement.getNamespaceByPrefix(prefix); if(ns == null){ ns = forceCreateNamespace(resXmlElement, resourceId, prefix); } @@ -134,7 +133,7 @@ private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){ + attribute.toString() + ", path=" + mCurrentPath); } - xmlAttribute.setNamespaceReference(ns.getUriReference()); + xmlAttribute.setNamespace(ns.getUri(), ns.getPrefix()); } String valueText = attribute.getValue(); EncodeResult encodeResult = materials.encodeReference(valueText); @@ -157,7 +156,7 @@ private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){ continue; } if(attributeBag.isEqualType(AttributeDataFormat.STRING)) { - xmlAttribute.setValueAsString(ValueDecoder + xmlAttribute.setValueAsString(XmlSanitizer .unEscapeUnQuote(valueText)); continue; } @@ -167,11 +166,10 @@ private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){ if(encodeResult != null){ xmlAttribute.setTypeAndData(encodeResult.valueType, encodeResult.value); }else { - xmlAttribute.setValueAsString(ValueDecoder + xmlAttribute.setValueAsString(XmlSanitizer .unEscapeUnQuote(valueText)); } } - resXmlElement.calculatePositions(); } private void ensureNamespaces(XMLElement element, ResXmlElement resXmlElement){ for(XMLAttribute attribute:element.listAttributes()){ @@ -220,7 +218,7 @@ private Entry getAttributeBlock(XMLAttribute attribute){ } return materials.getAttributeBlock(name); } - private ResXmlStartNamespace forceCreateNamespace(ResXmlElement resXmlElement, + private ResXmlNamespace forceCreateNamespace(ResXmlElement resXmlElement, int resourceId, String prefix){ if(!materials.isForceCreateNamespaces()){ return null; @@ -233,7 +231,7 @@ private ResXmlStartNamespace forceCreateNamespace(ResXmlElement resXmlElement, uri=EncodeUtil.URI_APP; } ResXmlElement root = resXmlElement.getRootResXmlElement(); - ResXmlStartNamespace ns = root.getOrCreateNamespace(uri, prefix); + ResXmlNamespace ns = root.getOrCreateXmlStartNamespace(uri, prefix); materials.logVerbose("Force created ns: "+prefix+":"+uri); return ns; } diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLTableBlockEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLTableBlockEncoder.java index 604e9086..6bcadcf5 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLTableBlockEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLTableBlockEncoder.java @@ -15,19 +15,16 @@ */ package com.reandroid.apk.xmlencoder; -import com.android.org.kxml2.io.KXmlParser; import com.reandroid.apk.*; import com.reandroid.archive.APKArchive; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.arsc.coder.ReferenceString; -import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.group.ResourceEntry; import com.reandroid.arsc.util.FrameworkTable; import com.reandroid.arsc.util.HexUtil; -import com.reandroid.arsc.value.Entry; -import com.reandroid.common.FileChannelInputStream; +import com.reandroid.arsc.util.IOUtil; +import com.reandroid.json.JSONObject; import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLParserFactory; import org.xmlpull.v1.XmlPullParser; @@ -35,7 +32,6 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.zip.ZipEntry; @@ -118,37 +114,45 @@ private void loadPublicXmlFiles(List pubXmlFileList) throws IOException { } } private void loadPublicXmlFile(File pubXmlFile) throws IOException { - XmlPullParser parser = new KXmlParser(); + PackageBlock packageBlock = tableBlock.newPackage(0, null); + loadPackageJson(packageBlock, pubXmlFile); try { - parser.setInput(new FileChannelInputStream(pubXmlFile), StandardCharsets.UTF_8.name()); - PackageBlock packageBlock = tableBlock.parsePublicXml(parser); + XmlPullParser parser = XMLParserFactory.newPullParser(pubXmlFile); packageBlock.setTag(pubXmlFile); - initializeIds(packageBlock); + packageBlock.parsePublicXml(parser); + packageBlock.initializeDefinedTypeIds(); + IOUtil.close(parser); } catch (XmlPullParserException ex) { throw new IOException(ex); } } - private void initializeIds(PackageBlock packageBlock){ - int typeId = packageBlock.typeIdOf("id"); - SpecTypePair specTypePair = packageBlock.getSpecTypePair(typeId); - if(specTypePair == null){ + private void loadPackageJson(PackageBlock packageBlock, File publicXml) throws IOException { + File json = toPackageJson(publicXml); + if(json == null){ return; } - Iterator itr = specTypePair.getResources(); - while (itr.hasNext()){ - Iterator entryIterator = itr.next() - .iterator(false); - while (entryIterator.hasNext()){ - Entry entry = entryIterator.next(); - if(!entry.isNull()){ - continue; - } - if(entry.getSpecReference() < 0){ - continue; - } - entry.setValueAsBoolean(false); - } + packageBlock.fromJson(new JSONObject(json)); + } + private File toPackageJson(File publicXml){ + File dir = publicXml.getParentFile(); + //values + if(dir == null || !"values".equals(dir.getName())){ + return null; + } + dir = dir.getParentFile(); + //res + if(dir == null){ + return null; + } + dir = dir.getParentFile(); + if(dir == null){ + return null; + } + File json = new File(dir, "package.json"); + if(!json.isFile()){ + return null; } + return json; } private void initializeFrameworkFromManifest(List pubXmlFileList) throws IOException { for(File pubXmlFile:pubXmlFileList){ @@ -191,6 +195,7 @@ private void encodeAttrs(List pubXmlFileList) throws XMLException { PackageBlock packageBlock = tableBlock.getPackageBlockByTag(pubXmlFile); encodeMaterials.setCurrentPackage(packageBlock); + tableBlock.setCurrentPackage(packageBlock); ResourceValuesEncoder valuesEncoder = new ResourceValuesEncoder(encodeMaterials); List attrFiles = listAttrs(pubXmlFile); diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java index e17a3ca4..49ef0549 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java @@ -83,7 +83,7 @@ public void encodeValue(Entry entry, ValueType[] expectedTypes, String text){ entry.setValueAsRaw(encodeResult.valueType, encodeResult.value); }else { // TODO: should check expectedTypes contains ValueType.STRING ? - text = ValueDecoder.unEscapeUnQuote(text); + text = XmlSanitizer.unEscapeUnQuote(text); entry.setValueAsString(text); } } diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java index 528c1bd6..38058e13 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java @@ -20,7 +20,7 @@ import com.reandroid.arsc.coder.CoderInteger; import com.reandroid.arsc.coder.EncodeResult; import com.reandroid.arsc.coder.ValueCoder; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ResValueMap; import com.reandroid.xml.XMLElement; @@ -67,7 +67,7 @@ protected void encodeChildes(XMLElement parentElement, ResTableMapEntry mapEntry continue; } if(force_string){ - bagItem.setValueAsString(ValueDecoder + bagItem.setValueAsString(XmlSanitizer .unEscapeUnQuote(valueText)); }else if(force_integer){ valueText = trimText(valueText); @@ -86,7 +86,7 @@ protected void encodeChildes(XMLElement parentElement, ResTableMapEntry mapEntry bagItem.setTypeAndData(encodeResult.valueType, encodeResult.value); }else { - bagItem.setValueAsString(ValueDecoder + bagItem.setValueAsString(XmlSanitizer .unEscapeUnQuote(valueText)); } } diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderPlurals.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderPlurals.java index 65669146..72ebec64 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderPlurals.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderPlurals.java @@ -17,11 +17,10 @@ import com.reandroid.arsc.array.ResValueMapArray; import com.reandroid.arsc.coder.EncodeResult; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.arsc.value.AttributeType; import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ResValueMap; -import com.reandroid.arsc.value.ValueType; import com.reandroid.xml.XMLElement; class XMLValuesEncoderPlurals extends XMLValuesEncoderBag{ @@ -51,7 +50,7 @@ protected void encodeChildes(XMLElement parentElement, ResTableMapEntry resValue bagItem.setTypeAndData(encodeResult.valueType, encodeResult.value); continue; } - bagItem.setValueAsString(ValueDecoder + bagItem.setValueAsString(XmlSanitizer .unEscapeUnQuote(valueText)); } } diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderStyle.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderStyle.java index 22057199..6f737af3 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderStyle.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderStyle.java @@ -18,7 +18,7 @@ import com.reandroid.arsc.array.ResValueMapArray; import com.reandroid.arsc.coder.EncodeResult; import com.reandroid.arsc.coder.ValueCoder; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.arsc.value.*; import com.reandroid.arsc.value.attribute.AttributeBag; import com.reandroid.xml.XMLElement; @@ -69,7 +69,7 @@ private void encodeChild(XMLElement child, Entry attributeEntry, ResValueMap bag return; } if(attributeBag.isEqualType(AttributeDataFormat.STRING)) { - bagItem.setValueAsString(ValueDecoder + bagItem.setValueAsString(XmlSanitizer .unEscapeUnQuote(valueText)); }else{ encodeResult = ValueCoder.encode(valueText); @@ -77,7 +77,7 @@ private void encodeChild(XMLElement child, Entry attributeEntry, ResValueMap bag bagItem.setTypeAndData(encodeResult.valueType, encodeResult.value); }else { - bagItem.setValueAsString(ValueDecoder.unEscapeUnQuote(valueText)); + bagItem.setValueAsString(XmlSanitizer.unEscapeUnQuote(valueText)); } } } diff --git a/src/main/java/com/reandroid/archive2/block/SignatureId.java b/src/main/java/com/reandroid/archive2/block/SignatureId.java index 6df983fa..81d9fba3 100644 --- a/src/main/java/com/reandroid/archive2/block/SignatureId.java +++ b/src/main/java/com/reandroid/archive2/block/SignatureId.java @@ -17,7 +17,6 @@ import com.reandroid.arsc.coder.CoderHex; import com.reandroid.arsc.coder.EncodeResult; -import com.reandroid.arsc.coder.ValueDecoder; import com.reandroid.arsc.util.HexUtil; import java.util.Objects; diff --git a/src/main/java/com/reandroid/arsc/ApkFile.java b/src/main/java/com/reandroid/arsc/ApkFile.java index bfa59ee5..aa0cf3c6 100644 --- a/src/main/java/com/reandroid/arsc/ApkFile.java +++ b/src/main/java/com/reandroid/arsc/ApkFile.java @@ -18,7 +18,6 @@ import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.arsc.chunk.xml.ResXmlDocument; -import com.reandroid.arsc.coder.Decoder; import java.io.IOException; @@ -26,8 +25,6 @@ public interface ApkFile { AndroidManifestBlock getAndroidManifestBlock(); TableBlock getTableBlock(); ResXmlDocument loadResXmlDocument(String path) throws IOException; - Decoder getDecoder(); - void setDecoder(Decoder decoder); enum ApkType { BASE, diff --git a/src/main/java/com/reandroid/arsc/array/LibraryInfoArray.java b/src/main/java/com/reandroid/arsc/array/LibraryInfoArray.java index 79e1ae58..47686a3d 100755 --- a/src/main/java/com/reandroid/arsc/array/LibraryInfoArray.java +++ b/src/main/java/com/reandroid/arsc/array/LibraryInfoArray.java @@ -1,18 +1,18 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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. - */ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.array; import com.reandroid.arsc.base.BlockArray; @@ -30,6 +30,16 @@ public class LibraryInfoArray extends BlockArray implements JSONCon public LibraryInfoArray(IntegerItem infoCount){ this.mInfoCount=infoCount; } + + public boolean containsLibraryInfo(String packageName){ + for(LibraryInfo info : getChildes()){ + if(info.packageNameMatches(packageName)){ + return true; + } + } + return false; + } + public LibraryInfo getOrCreate(int pkgId){ LibraryInfo info=getById(pkgId); if(info!=null){ @@ -38,12 +48,12 @@ public LibraryInfo getOrCreate(int pkgId){ int index=childesCount(); ensureSize(index+1); info=get(index); - info.setPackageId(pkgId); + info.setId(pkgId); return info; } public LibraryInfo getById(int pkgId){ for(LibraryInfo info:listItems()){ - if(pkgId==info.getPackageId()){ + if(info !=null && pkgId==info.getId()){ return info; } } @@ -100,7 +110,7 @@ public void merge(LibraryInfoArray infoArray){ return; } for(LibraryInfo info:infoArray.listItems()){ - LibraryInfo exist=getOrCreate(info.getPackageId()); + LibraryInfo exist=getOrCreate(info.getId()); exist.merge(info); } } diff --git a/src/main/java/com/reandroid/arsc/array/PackageArray.java b/src/main/java/com/reandroid/arsc/array/PackageArray.java index ee716485..c6149f08 100755 --- a/src/main/java/com/reandroid/arsc/array/PackageArray.java +++ b/src/main/java/com/reandroid/arsc/array/PackageArray.java @@ -63,14 +63,14 @@ private PackageBlock pickOne(PackageBlock[] items, int packageId){ if(packageBlock == null){ continue; } - if(packageId!=0 && packageId!=packageBlock.getId()){ + if(packageId !=0 && packageId != packageBlock.getId()){ continue; } - if(largest==null){ - largest=packageBlock; - }else if(packageBlock.getEntriesGroupMap().size() > - largest.getEntriesGroupMap().size()){ - largest=packageBlock; + if(largest == null){ + largest = packageBlock; + }else if(packageBlock.getHeaderBlock().getChunkSize() > + largest.getHeaderBlock().getChunkSize()){ + largest = packageBlock; } } return largest; diff --git a/src/main/java/com/reandroid/arsc/array/SpecTypePairArray.java b/src/main/java/com/reandroid/arsc/array/SpecTypePairArray.java index fb474c65..214a21fa 100755 --- a/src/main/java/com/reandroid/arsc/array/SpecTypePairArray.java +++ b/src/main/java/com/reandroid/arsc/array/SpecTypePairArray.java @@ -20,7 +20,6 @@ import com.reandroid.arsc.chunk.SpecBlock; import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.StringGroup; import com.reandroid.arsc.item.TypeString; import com.reandroid.arsc.pool.TypeStringPool; @@ -187,13 +186,6 @@ public Entry getEntry(ResConfig resConfig, String typeName, String entryName){ } return null; } - public EntryGroup getEntryGroup(String typeName, String entryName){ - SpecTypePair specTypePair = getSpecTypePair(typeName); - if(specTypePair != null){ - return specTypePair.getEntryGroup(entryName); - } - return null; - } @Override public SpecTypePair newInstance() { return new SpecTypePair(); diff --git a/src/main/java/com/reandroid/arsc/array/TypeBlockArray.java b/src/main/java/com/reandroid/arsc/array/TypeBlockArray.java index 02e06029..895dfe52 100755 --- a/src/main/java/com/reandroid/arsc/array/TypeBlockArray.java +++ b/src/main/java/com/reandroid/arsc/array/TypeBlockArray.java @@ -226,26 +226,24 @@ public byte getTypeId(){ } return 0; } - public List listResConfig(){ - return new AbstractList() { - @Override - public ResConfig get(int i) { - TypeBlock typeBlock=TypeBlockArray.this.get(i); - if(typeBlock!=null){ - return typeBlock.getResConfig(); - } - return null; - } - - @Override - public int size() { - return TypeBlockArray.this.childesCount(); - } - }; + public Set listResConfig(){ + Set unique = new HashSet<>(); + for(TypeBlock typeBlock : getChildes()){ + unique.add(typeBlock.getResConfig()); + } + return unique; } public Iterator iteratorNonEmpty(){ return super.iterator(NON_EMPTY_TESTER); } + public Iterator iterator(ResConfig resConfig){ + return iterator(new Predicate() { + @Override + public boolean test(TypeBlock typeBlock) { + return typeBlock.getResConfig().equals(resConfig); + } + }); + } public boolean hasDuplicateResConfig(boolean ignoreEmpty){ Set uniqueHashSet = new HashSet<>(); Iterator itr; diff --git a/src/main/java/com/reandroid/arsc/chunk/LibraryBlock.java b/src/main/java/com/reandroid/arsc/chunk/LibraryBlock.java index aeca72d9..ca53afc3 100755 --- a/src/main/java/com/reandroid/arsc/chunk/LibraryBlock.java +++ b/src/main/java/com/reandroid/arsc/chunk/LibraryBlock.java @@ -1,18 +1,18 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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. - */ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk; import com.reandroid.arsc.array.LibraryInfoArray; @@ -21,7 +21,7 @@ import java.util.Collection; - public class LibraryBlock extends Chunk { +public class LibraryBlock extends Chunk { private final LibraryInfoArray mLibraryInfoArray; public LibraryBlock() { super(new LibraryHeader(),1); @@ -30,6 +30,16 @@ public LibraryBlock() { addChild(mLibraryInfoArray); } + + public boolean containsLibraryInfo(String packageName){ + if(isEmpty()){ + return false; + } + return getLibraryInfoArray().containsLibraryInfo(packageName); + } + public boolean isEmpty(){ + return getLibraryCount() == 0; + } public LibraryInfoArray getLibraryInfoArray(){ return mLibraryInfoArray; } diff --git a/src/main/java/com/reandroid/arsc/chunk/Overlayable.java b/src/main/java/com/reandroid/arsc/chunk/Overlayable.java index ae8bbbcf..b924e4c7 100644 --- a/src/main/java/com/reandroid/arsc/chunk/Overlayable.java +++ b/src/main/java/com/reandroid/arsc/chunk/Overlayable.java @@ -1,169 +1,164 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.chunk; +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk; - import com.reandroid.arsc.container.BlockList; - import com.reandroid.arsc.header.HeaderBlock; - import com.reandroid.arsc.header.OverlayableHeader; - import com.reandroid.arsc.io.BlockReader; - import com.reandroid.arsc.item.ByteArray; - import com.reandroid.json.JSONArray; - import com.reandroid.json.JSONConvert; - import com.reandroid.json.JSONObject; +import com.reandroid.arsc.container.BlockList; +import com.reandroid.arsc.header.HeaderBlock; +import com.reandroid.arsc.header.OverlayableHeader; +import com.reandroid.arsc.io.BlockReader; +import com.reandroid.arsc.item.ByteArray; +import com.reandroid.json.JSONArray; +import com.reandroid.json.JSONConvert; +import com.reandroid.json.JSONObject; - import java.io.IOException; - import java.util.List; +import java.io.IOException; +import java.util.List; - /** - * Replica of struct "ResTable_overlayable_header" as on AOSP androidfw/ResourceTypes.h - * We didn't test this class with resource table, if someone found a resource/apk please - * create issue on https://github.com/REAndroid/ARSCLib - * */ - public class Overlayable extends Chunk implements JSONConvert { - private final BlockList policyList; - private final ByteArray extraBytes; +public class Overlayable extends Chunk implements JSONConvert { + private final BlockList policyList; + private final ByteArray extraBytes; - public Overlayable() { - super(new OverlayableHeader(), 2); - this.policyList = new BlockList<>(); - this.extraBytes = new ByteArray(); - addChild(this.policyList); - addChild(this.extraBytes); - } + public Overlayable() { + super(new OverlayableHeader(), 2); + this.policyList = new BlockList<>(); + this.extraBytes = new ByteArray(); + addChild(this.policyList); + addChild(this.extraBytes); + } - public OverlayablePolicy get(int flags){ - for(OverlayablePolicy policy:listOverlayablePolicies()){ - if(flags==policy.getFlags()){ - return policy; - } - } - return null; - } - public void addOverlayablePolicy(OverlayablePolicy overlayablePolicy){ - this.policyList.add(overlayablePolicy); - } - public List listOverlayablePolicies() { - return policyList.getChildes(); - } - public ByteArray getExtraBytes() { - return extraBytes; - } + public OverlayablePolicy get(int flags){ + for(OverlayablePolicy policy:listOverlayablePolicies()){ + if(flags==policy.getFlags()){ + return policy; + } + } + return null; + } + public void addOverlayablePolicy(OverlayablePolicy overlayablePolicy){ + this.policyList.add(overlayablePolicy); + } + public List listOverlayablePolicies() { + return policyList.getChildes(); + } + public ByteArray getExtraBytes() { + return extraBytes; + } - public String getName(){ - return getHeaderBlock().getName().get(); - } - public void setName(String str){ - getHeaderBlock().getName().set(str); - } - public String getActor(){ - return getHeaderBlock().getActor().get(); - } - public void setActor(String str){ - getHeaderBlock().getActor().set(str); - } + public String getName(){ + return getHeaderBlock().getName().get(); + } + public void setName(String str){ + getHeaderBlock().getName().set(str); + } + public String getActor(){ + return getHeaderBlock().getActor().get(); + } + public void setActor(String str){ + getHeaderBlock().getActor().set(str); + } - @Override - protected void onChunkRefreshed() { - } + @Override + protected void onChunkRefreshed() { + } - @Override - public void onReadBytes(BlockReader reader) throws IOException { - HeaderBlock headerBlock = reader.readHeaderBlock(); - checkInvalidChunk(headerBlock); + @Override + public void onReadBytes(BlockReader reader) throws IOException { + HeaderBlock headerBlock = reader.readHeaderBlock(); + checkInvalidChunk(headerBlock); - int size = headerBlock.getChunkSize(); - BlockReader chunkReader = reader.create(size); - headerBlock = getHeaderBlock(); - headerBlock.readBytes(chunkReader); + int size = headerBlock.getChunkSize(); + BlockReader chunkReader = reader.create(size); + headerBlock = getHeaderBlock(); + headerBlock.readBytes(chunkReader); - readOverlayablePlolicies(chunkReader); - readExtraBytes(chunkReader); + readOverlayablePlolicies(chunkReader); + readExtraBytes(chunkReader); - reader.offset(size); - chunkReader.close(); - onChunkLoaded(); - } - private void readOverlayablePlolicies(BlockReader reader) throws IOException { - HeaderBlock headerBlock = reader.readHeaderBlock(); - BlockList policyList = this.policyList; - while (headerBlock!=null && headerBlock.getChunkType()==ChunkType.OVERLAYABLE_POLICY){ - OverlayablePolicy policy = new OverlayablePolicy(); - policyList.add(policy); - policy.readBytes(reader); - headerBlock = reader.readHeaderBlock(); - } - } - private void readExtraBytes(BlockReader reader) throws IOException { - int remaining = reader.available(); - this.extraBytes.setSize(remaining); - this.extraBytes.readBytes(reader); - } + reader.offset(size); + chunkReader.close(); + onChunkLoaded(); + } + private void readOverlayablePlolicies(BlockReader reader) throws IOException { + HeaderBlock headerBlock = reader.readHeaderBlock(); + BlockList policyList = this.policyList; + while (headerBlock!=null && headerBlock.getChunkType()==ChunkType.OVERLAYABLE_POLICY){ + OverlayablePolicy policy = new OverlayablePolicy(); + policyList.add(policy); + policy.readBytes(reader); + headerBlock = reader.readHeaderBlock(); + } + } + private void readExtraBytes(BlockReader reader) throws IOException { + int remaining = reader.available(); + this.extraBytes.setSize(remaining); + this.extraBytes.readBytes(reader); + } - @Override - public JSONObject toJson() { - JSONObject jsonObject = new JSONObject(); - jsonObject.put(NAME_name, getName()); - jsonObject.put(NAME_actor, getActor()); - JSONArray jsonArray = new JSONArray(); - for(OverlayablePolicy policy:listOverlayablePolicies()){ - jsonArray.put(policy.toJson()); - } - jsonObject.put(NAME_policies, jsonArray); - return jsonObject; - } - @Override - public void fromJson(JSONObject json) { - setName(json.optString(NAME_name)); - setActor(json.optString(NAME_actor)); - JSONArray jsonArray = json.getJSONArray(NAME_policies); - int length = jsonArray.length(); - BlockList policyList = this.policyList; - for(int i=0;i policyList = this.policyList; + for(int i=0;i implements BlockLoad, - JSONConvert { - private final IntegerArray tableRefArray; - public OverlayablePolicy(){ - super(new OverlayablePolicyHeader(), 1); - this.tableRefArray = new IntegerArray(); +public class OverlayablePolicy extends Chunk implements BlockLoad, + JSONConvert { + private final IntegerArray tableRefArray; + public OverlayablePolicy(){ + super(new OverlayablePolicyHeader(), 1); + this.tableRefArray = new IntegerArray(); + addChild(this.tableRefArray); - addChild(this.tableRefArray); - - getHeaderBlock().getEntryCount().setBlockLoad(this); - } - @Override - public boolean isNull() { - return getTableReferenceCount()==0; - } - public int getTableReferenceCount(){ - return getTableRefArray().size(); - } - - public Collection listTableReferences(){ - return getTableRefArray().toList(); - } - public IntegerArray getTableRefArray() { - return tableRefArray; - } - public int getFlags() { - return getHeaderBlock().getFlags().get(); - } - public void setFlags(int flags){ - getHeaderBlock().getFlags().set(flags); - } - public void setFlags(PolicyFlag[] policyFlags){ - setFlags(PolicyFlag.sum(policyFlags)); - } - public void addFlag(PolicyFlag policyFlag){ - int i = policyFlag==null? 0 : policyFlag.getFlagValue(); - setFlags(getFlags() | i); - } - public PolicyFlag[] getPolicyFlags(){ - return PolicyFlag.valuesOf(getFlags()); - } - @Override - protected void onChunkRefreshed() { - getHeaderBlock().getEntryCount().set(getTableRefArray().size()); - } - @Override - public void onBlockLoaded(BlockReader reader, Block sender) throws IOException { - IntegerItem entryCount = getHeaderBlock().getEntryCount(); - if(sender==entryCount){ - this.tableRefArray.setSize(entryCount.get()); - } - } - - @Override - public JSONObject toJson() { - JSONObject jsonObject = new JSONObject(); - jsonObject.put(NAME_flags, getFlags()); - JSONArray jsonArray = new JSONArray(); - for(Integer reference:listTableReferences()){ - jsonArray.put(reference); - } - jsonObject.put(NAME_references, jsonArray); - return jsonObject; - } - @Override - public void fromJson(JSONObject json) { - setFlags(json.getInt(NAME_flags)); - JSONArray jsonArray = json.getJSONArray(NAME_references); - IntegerArray integerArray = getTableRefArray(); - int length = jsonArray.length(); - integerArray.setSize(length); - for(int i=0;i implements ParentChunk, JSONConvert, - Comparable { + Comparable, + ResourceLibrary { private final TypeStringPool mTypeStringPool; private final SpecStringPool mSpecStringPool; private final PackageBody mBody; - private final Map mEntriesGroup; - private boolean entryGroupMapLocked; + private String mPrefix; + private boolean mHasValidPrefix; private Object mTag; @@ -77,9 +79,6 @@ public PackageBlock() { this.mBody = new PackageBody(); - this.mEntriesGroup = new HashMap<>(); - this.entryGroupMapLocked = true; - addChild(mTypeStringPool); addChild(mSpecStringPool); addChild(mBody); @@ -90,6 +89,35 @@ public Object getTag(){ public void setTag(Object tag){ this.mTag = tag; } + public ResourceEntry getResource(int resourceId){ + int packageId = (resourceId >> 24 ) & 0xff; + if(packageId == 0){ + return null; + } + if(packageId == getId()){ + int typeId = (resourceId >> 16 ) & 0xff; + int entryId = resourceId & 0xffff; + ResourceEntry resourceEntry = getResource(typeId, entryId); + if(resourceEntry != null){ + return resourceEntry; + } + } + StagedAliasEntry aliasEntry = searchByStagedResId(resourceId); + if(aliasEntry == null){ + return null; + } + int alias = aliasEntry.getFinalizedResId(); + if(alias == 0 || alias == resourceId){ + return null; + } + packageId = (alias >> 24 ) & 0xff; + if(packageId != getId()){ + return null; + } + int typeId = (alias >> 16 ) & 0xff; + int entryId = alias & 0xffff; + return getResource(typeId, entryId); + } public ResourceEntry getResource(int typeId, int entryId){ SpecTypePair specTypePair = getSpecTypePair(typeId); @@ -188,7 +216,6 @@ public void linkSpecStringsInternal(SpecStringPool specStringPool){ } } public void destroy(){ - getEntriesGroupMap().clear(); getPackageBody().destroy(); getTypeStringPool().destroy(); getSpecStringPool().destroy(); @@ -274,11 +301,10 @@ public BlockList getUnknownChunkList(){ } public StagedAliasEntry searchByStagedResId(int stagedResId){ - for(StagedAlias stagedAlias:getStagedAliasList().getChildes()){ - StagedAliasEntry entry=stagedAlias.getStagedAliasEntryArray() - .searchByStagedResId(stagedResId); - if(entry!=null){ - return entry; + for(StagedAlias stagedAlias:listStagedAlias()){ + StagedAliasEntry aliasEntry = stagedAlias.searchByStagedResId(stagedResId); + if(aliasEntry != null){ + return aliasEntry; } } return null; @@ -305,6 +331,7 @@ public void removeEmpty(){ public boolean isEmpty(){ return getSpecTypePairArray().isEmpty(); } + @Override public int getId(){ return getHeaderBlock().getPackageId().get(); } @@ -313,12 +340,61 @@ public void setId(byte id){ } public void setId(int id){ getHeaderBlock().getPackageId().set(id); + mPrefix = null; + mHasValidPrefix = false; } + @Override public String getName(){ return getHeaderBlock().getPackageName().get(); } public void setName(String name){ getHeaderBlock().getPackageName().set(name); + mPrefix = null; + mHasValidPrefix = false; + } + @Override + public String getPrefix(){ + if(mPrefix != null){ + return mPrefix; + } + boolean hasValidPrefix; + String prefix; + if(getId() == 0x01){ + prefix = ResourceLibrary.PREFIX_ANDROID; + hasValidPrefix = ResourceLibrary.PREFIX_ANDROID.equals(getName()); + }else { + prefix = ResourceLibrary.toPrefix(getName()); + hasValidPrefix = Namespace.isValidPrefix(prefix); + if(!hasValidPrefix){ + prefix = ResourceLibrary.PREFIX_APP; + } + } + mPrefix = prefix; + mHasValidPrefix = hasValidPrefix; + return prefix; + } + @Override + public String getUri(){ + if(isAndroid()){ + return ResourceLibrary.URI_ANDROID; + } + return ResourceLibrary.URI_RES_AUTO; + } + @Override + public boolean packageNameMatches(String packageName){ + if(packageName == null){ + return false; + } + if(packageName.equals(getName())){ + return true; + } + if(mHasValidPrefix && packageName.equals(getPrefix())){ + return true; + } + return getLibraryBlock().containsLibraryInfo(packageName); + } + private boolean isAndroid(){ + return getId() == 0x01 && ResourceLibrary.PREFIX_ANDROID.equals(getName()); } public TableBlock getTableBlock(){ Block parent=getParent(); @@ -382,9 +458,6 @@ public void addLibraryInfo(LibraryInfo info){ public LibraryBlock getLibraryBlock(){ return mBody.getLibraryBlock(); } - public Set listResourceIds(){ - return getEntriesGroupMap().keySet(); - } public Entry getOrCreateEntry(byte typeId, short entryId, String qualifiers){ return getSpecTypePairArray().getOrCreateEntry(typeId, entryId, qualifiers); } @@ -411,119 +484,16 @@ public TypeBlock getTypeBlock(byte typeId, String qualifiers){ return getSpecTypePairArray().getTypeBlock(typeId, qualifiers); } - private void unlockEntryGroup() { - synchronized (this){ - if(!this.entryGroupMapLocked){ - return; - } - entryGroupMapLocked = false; - Map map = this.mEntriesGroup; - map.clear(); - createEntryGroupMap(map); - } - } - private void createEntryGroupMap(Map map){ - map.clear(); - for(SpecTypePair specTypePair : listSpecTypePairs()){ - map.putAll(specTypePair.createEntryGroups(true)); - } - } - public Map getEntriesGroupMap(){ - unlockEntryGroup(); - return mEntriesGroup; - } - public Collection listEntryGroup(){ - return getEntriesGroupMap().values(); - } - public int getEntryGroupCount(){ - return getEntriesGroupMap().size(); - } - - /** - * Searches entries by resource id from local map, then if not find - * search by alias resource id - * */ - public EntryGroup getEntryGroup(int resourceId){ - if(resourceId==0){ - return null; - } - EntryGroup entryGroup=getEntriesGroupMap().get(resourceId); - if(entryGroup!=null){ - return entryGroup; - } - StagedAliasEntry stagedAliasEntry = searchByStagedResId(resourceId); - if(stagedAliasEntry!=null){ - return getEntriesGroupMap() - .get(stagedAliasEntry.getFinalizedResId()); - } - return null; - } - public void updateEntry(Entry entry){ - if(this.entryGroupMapLocked){ - return; - } - if(entry == null || entry.isNull()){ - return; - } - int resourceId = entry.getResourceId(); - Map map = getEntriesGroupMap(); - EntryGroup group = map.get(resourceId); - if(group == null){ - group = new EntryGroup(resourceId); - map.put(resourceId, group); - } - group.add(entry); - } - public void removeEntryGroup(Entry entry){ - if(entry == null){ - return; - } - int resourceId = entry.getResourceId(); - Map map = getEntriesGroupMap(); - EntryGroup group = map.get(resourceId); - if(group == null){ - return; - } - group.remove(entry); - if(group.size() == 0){ - map.remove(resourceId); - } - } - public List listEntries(byte typeId, int entryId){ - List results=new ArrayList<>(); - for(SpecTypePair pair:listSpecTypePair(typeId)){ - results.addAll(pair.listEntries(entryId)); - } - return results; - } - public List listSpecTypePair(byte typeId){ - List results = new ArrayList<>(); - for(SpecTypePair specTypePair : listSpecTypePairs()){ - if(typeId == specTypePair.getTypeId()){ - results.add(specTypePair); - } - } - return results; - } public SpecTypePair getSpecTypePair(String typeName){ - return getSpecTypePairArray().getSpecTypePair(typeName); + return getSpecTypePair(typeIdOf(typeName)); } public SpecTypePair getSpecTypePair(int typeId){ return getSpecTypePairArray().getSpecTypePair((byte) typeId); } - public EntryGroup getEntryGroup(String typeName, String entryName){ - return getSpecTypePairArray().getEntryGroup(typeName, entryName); - } + public Collection listSpecTypePairs(){ return getSpecTypePairArray().listItems(); } - /** - * Use listSpecTypePairs() - * */ - @Deprecated - public Collection listAllSpecTypePair(){ - return listSpecTypePairs(); - } private void refreshTypeStringPoolOffset(){ int pos=countUpTo(mTypeStringPool); @@ -539,9 +509,6 @@ private void refreshSpecStringPoolOffset(){ private void refreshSpecStringCount(){ getHeaderBlock().getSpecStringPoolCount().set(mSpecStringPool.countStrings()); } - public void onEntryAdded(Entry entry){ - updateEntry(entry); - } @Override public void onChunkLoaded() { } @@ -676,18 +643,25 @@ private boolean parsePublicTag(XmlPullParser parser) throws XmlPullParserExcepti entry.setName(name, true); return true; } - private void parsePublicXmlAttributes(XmlPullParser parser) { - int count = parser.getAttributeCount(); - for(int i = 0; i < count; i++){ - onPublicXmlResourceAttributes( - parser.getAttributeName(i), parser.getAttributeValue(i)); + public void initializeDefinedTypeIds(){ + SpecTypePair specTypePair = getSpecTypePair("id"); + if(specTypePair == null){ + return; } - } - private void onPublicXmlResourceAttributes(String name, String value){ - if(ATTR_package.equals(name)){ - setName(value); - }else if(ATTR_id.equals(name)){ - setId(Integer.decode(value)); + Iterator itr = specTypePair.getResources(); + while (itr.hasNext()){ + Iterator entryIterator = itr.next() + .iterator(false); + while (entryIterator.hasNext()){ + Entry entry = entryIterator.next(); + if(!entry.isNull() || entry.getSpecReference() < 0){ + continue; + } + entry.setValueAsBoolean(false); + ValueHeader valueHeader = entry.getHeader(); + valueHeader.setWeak(true); + valueHeader.setPublic(true); + } } } @@ -819,6 +793,13 @@ static int readPackageId(XmlPullParser parser, int def) throws XmlPullParserExce } return def; } + public static boolean isResourceId(int resourceId){ + if(resourceId == 0){ + return false; + } + return (resourceId & 0x00ff0000) != 0 + && (resourceId & 0xff000000) != 0; + } public static final String NAME_package_id = "package_id"; public static final String NAME_package_name = "package_name"; @@ -840,4 +821,5 @@ static int readPackageId(XmlPullParser parser, int def) throws XmlPullParserExce public static final String ATTR_id = "id"; public static final String ATTR_type = "type"; public static final String ATTR_name = "name"; + } diff --git a/src/main/java/com/reandroid/arsc/chunk/StagedAlias.java b/src/main/java/com/reandroid/arsc/chunk/StagedAlias.java index ed1421f6..637ed6a4 100644 --- a/src/main/java/com/reandroid/arsc/chunk/StagedAlias.java +++ b/src/main/java/com/reandroid/arsc/chunk/StagedAlias.java @@ -1,83 +1,86 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.chunk; +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk; - import com.reandroid.arsc.array.StagedAliasEntryArray; - import com.reandroid.arsc.header.StagedAliasHeader; - import com.reandroid.arsc.value.StagedAliasEntry; +import com.reandroid.arsc.array.StagedAliasEntryArray; +import com.reandroid.arsc.header.StagedAliasHeader; +import com.reandroid.arsc.value.StagedAliasEntry; - import java.util.Collection; +import java.util.Collection; - public class StagedAlias extends Chunk { - private final StagedAliasEntryArray stagedAliasEntryArray; - public StagedAlias() { - super(new StagedAliasHeader(), 1); - StagedAliasHeader header = getHeaderBlock(); +public class StagedAlias extends Chunk { + private final StagedAliasEntryArray stagedAliasEntryArray; + public StagedAlias() { + super(new StagedAliasHeader(), 1); + StagedAliasHeader header = getHeaderBlock(); - stagedAliasEntryArray = new StagedAliasEntryArray(header.getCount()); - addChild(stagedAliasEntryArray); - } - public void merge(StagedAlias stagedAlias){ - if(stagedAlias==null||stagedAlias==this){ - return; - } - StagedAliasEntryArray exist = getStagedAliasEntryArray(); - for(StagedAliasEntry entry:stagedAlias.listStagedAliasEntry()){ - if(!exist.contains(entry)){ - exist.add(entry); - } - } - } - public StagedAliasEntryArray getStagedAliasEntryArray() { - return stagedAliasEntryArray; - } - public Collection listStagedAliasEntry(){ - return getStagedAliasEntryArray().listItems(); - } - public int getStagedAliasEntryCount(){ - return getStagedAliasEntryArray().childesCount(); - } - @Override - public boolean isNull(){ - return getStagedAliasEntryCount()==0; - } - @Override - protected void onChunkRefreshed() { - getHeaderBlock().getCount().set(getStagedAliasEntryCount()); - } - @Override - public String toString(){ - return getClass().getSimpleName()+ - ": count="+getStagedAliasEntryCount(); - } - public static StagedAlias mergeAll(Collection stagedAliasList){ - if(stagedAliasList.size()==0){ - return null; - } - StagedAlias result=new StagedAlias(); - for(StagedAlias stagedAlias:stagedAliasList){ - if(stagedAlias.isNull()){ - continue; - } - result.merge(stagedAlias); - } - if(!result.isNull()){ - result.refresh(); - return result; - } - return null; - } - } + stagedAliasEntryArray = new StagedAliasEntryArray(header.getCount()); + addChild(stagedAliasEntryArray); + } + public StagedAliasEntry searchByStagedResId(int stagedResId){ + return getStagedAliasEntryArray().searchByStagedResId(stagedResId); + } + public void merge(StagedAlias stagedAlias){ + if(stagedAlias==null||stagedAlias==this){ + return; + } + StagedAliasEntryArray exist = getStagedAliasEntryArray(); + for(StagedAliasEntry entry:stagedAlias.listStagedAliasEntry()){ + if(!exist.contains(entry)){ + exist.add(entry); + } + } + } + public StagedAliasEntryArray getStagedAliasEntryArray() { + return stagedAliasEntryArray; + } + public Collection listStagedAliasEntry(){ + return getStagedAliasEntryArray().listItems(); + } + public int getStagedAliasEntryCount(){ + return getStagedAliasEntryArray().childesCount(); + } + @Override + public boolean isNull(){ + return getStagedAliasEntryCount()==0; + } + @Override + protected void onChunkRefreshed() { + getHeaderBlock().getCount().set(getStagedAliasEntryCount()); + } + @Override + public String toString(){ + return getClass().getSimpleName()+ + ": count="+getStagedAliasEntryCount(); + } + public static StagedAlias mergeAll(Collection stagedAliasList){ + if(stagedAliasList.size()==0){ + return null; + } + StagedAlias result=new StagedAlias(); + for(StagedAlias stagedAlias:stagedAliasList){ + if(stagedAlias.isNull()){ + continue; + } + result.merge(stagedAlias); + } + if(!result.isNull()){ + result.refresh(); + return result; + } + return null; + } +} diff --git a/src/main/java/com/reandroid/arsc/chunk/TableBlock.java b/src/main/java/com/reandroid/arsc/chunk/TableBlock.java index 7cdcce5a..49dd72c5 100755 --- a/src/main/java/com/reandroid/arsc/chunk/TableBlock.java +++ b/src/main/java/com/reandroid/arsc/chunk/TableBlock.java @@ -18,8 +18,7 @@ import com.reandroid.arsc.ApkFile; import com.reandroid.arsc.BuildInfo; import com.reandroid.arsc.array.PackageArray; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.group.ResourceEntry; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.InfoHeader; import com.reandroid.arsc.header.TableHeader; @@ -27,7 +26,6 @@ import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.util.*; import com.reandroid.arsc.value.*; -import com.reandroid.common.EntryStore; import com.reandroid.common.ReferenceResolver; import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONArray; @@ -41,12 +39,13 @@ import java.util.function.Predicate; public class TableBlock extends Chunk - implements MainChunk, JSONConvert, EntryStore { + implements MainChunk, JSONConvert { private final TableStringPool mTableStringPool; private final PackageArray mPackageArray; private final List mFrameWorks; private ApkFile mApkFile; private ReferenceResolver referenceResolver; + private PackageBlock mCurrentPackage; public TableBlock() { super(new TableHeader(), 2); @@ -58,6 +57,12 @@ public TableBlock() { addChild(mPackageArray); } + public PackageBlock getCurrentPackage(){ + return mCurrentPackage; + } + public void setCurrentPackage(PackageBlock packageBlock){ + mCurrentPackage = packageBlock; + } public PackageBlock getPackageBlockByTag(Object tag){ for(PackageBlock packageBlock : listPackages()){ if(Objects.equals(tag, packageBlock.getTag())){ @@ -78,12 +83,36 @@ public ResourceEntry getResource(int resourceId){ if(resourceId == 0){ return null; } - Iterator iterator = getAllPackages((resourceId >> 24) & 0xff); - int typeId = (resourceId >> 16) & 0xff; - int entryId = resourceId & 0xffff; + Iterator iterator = getAllPackages(); + while (iterator.hasNext()){ + PackageBlock packageBlock = iterator.next(); + ResourceEntry resourceEntry = packageBlock.getResource(resourceId); + if(resourceEntry != null){ + return resourceEntry; + } + } + int staged = resolveStagedAlias(resourceId, 0); + if(staged == 0 || staged == resourceId){ + return null; + } + iterator = getAllPackages(); + while (iterator.hasNext()){ + PackageBlock packageBlock = iterator.next(); + ResourceEntry resourceEntry = packageBlock.getResource(staged); + if(resourceEntry != null){ + return resourceEntry; + } + } + return null; + } + public ResourceEntry getResource(PackageBlock context, int resourceId){ + if(resourceId == 0){ + return null; + } + Iterator iterator = getAllPackages(context); while (iterator.hasNext()){ PackageBlock packageBlock = iterator.next(); - ResourceEntry resourceEntry = packageBlock.getResource(typeId, entryId); + ResourceEntry resourceEntry = packageBlock.getResource(resourceId); if(resourceEntry != null){ return resourceEntry; } @@ -92,12 +121,10 @@ public ResourceEntry getResource(int resourceId){ if(staged == 0 || staged == resourceId){ return null; } - iterator = getAllPackages((staged >> 24) & 0xff); - typeId = (staged >> 16) & 0xff; - entryId = staged & 0xffff; + iterator = getAllPackages(context); while (iterator.hasNext()){ PackageBlock packageBlock = iterator.next(); - ResourceEntry resourceEntry = packageBlock.getResource(typeId, entryId); + ResourceEntry resourceEntry = packageBlock.getResource(staged); if(resourceEntry != null){ return resourceEntry; } @@ -174,8 +201,8 @@ public Iterator getPackages(String packageName){ return new FilterIterator(getPackages()) { @Override public boolean test(PackageBlock packageBlock){ - if(packageName != null){ - return packageName.equals(packageBlock.getName()); + if(packageName != null && packageName.length() > 0){ + return packageBlock.packageNameMatches(packageName); } return TableBlock.this == packageBlock.getTableBlock(); } @@ -193,11 +220,30 @@ public boolean test(PackageBlock packageBlock){ }; } public Iterator getPackages(){ - return getPackageArray().iterator(); + return getPackages((PackageBlock) null); + } + public Iterator getPackages(PackageBlock context){ + PackageBlock current; + if(context == null){ + current = getCurrentPackage(); + }else { + current = context; + } + Iterator iterator = getPackageArray().iterator(); + if(current == null){ + return iterator; + } + return new CombiningIterator<>( + SingleIterator.of(current), + new FilterIterator.Except<>(iterator, current) + ); } public Iterator getAllPackages(){ - return new CombiningIterator<>(getPackages(), - new IterableIterator(getFrameWorks().iterator()) { + return getAllPackages((PackageBlock) null); + } + public Iterator getAllPackages(PackageBlock context){ + return new CombiningIterator<>(getPackages(context), + new IterableIterator(frameworkIterator()) { @Override public Iterator iterator(TableBlock element) { return element.getPackages(); @@ -217,7 +263,7 @@ public Iterator getAllPackages(String packageName){ @Override public boolean test(PackageBlock packageBlock){ if(packageName != null){ - return packageName.equals(packageBlock.getName()); + return packageBlock.packageNameMatches(packageName); } return TableBlock.this == packageBlock.getTableBlock(); } @@ -307,6 +353,10 @@ public int countPackages(){ } public PackageBlock pickOne(){ + PackageBlock current = getCurrentPackage(); + if(current != null && current.getTableBlock() == this){ + return current; + } return getPackageArray().pickOne(); } public PackageBlock pickOne(int packageId){ @@ -429,103 +479,6 @@ public final int writeBytes(File file) throws IOException{ outputStream.close(); return length; } - - public Entry getAnyEntry(int resourceId){ - if(resourceId == 0 || ((resourceId >> 16) & 0xff) == 0){ - return null; - } - Entry result = null; - for(PackageBlock packageBlock : listPackages()){ - Entry entry = packageBlock.getAnyEntry(resourceId); - if(entry == null){ - continue; - } - if(!entry.isNull()){ - return entry; - } - if(result == null){ - result = entry; - } - } - if(result != null){ - return result; - } - for(TableBlock tableBlock : getFrameWorks()){ - Entry entry = tableBlock.getAnyEntry(resourceId); - if(entry == null){ - continue; - } - if(!entry.isNull()){ - return entry; - } - if(result == null){ - result = entry; - } - } - return result; - } - public EntryGroup search(int resourceId){ - if(resourceId==0){ - return null; - } - EntryGroup entryGroup = searchLocal(resourceId); - if(entryGroup!=null){ - return entryGroup; - } - for(TableBlock tableBlock:getFrameWorks()){ - entryGroup = tableBlock.search(resourceId); - if(entryGroup!=null){ - return entryGroup; - } - } - return null; - } - private EntryGroup searchLocal(int resourceId){ - if(resourceId == 0){ - return null; - } - int aliasId = searchResourceIdAlias(resourceId); - Iterator iterator = getPackages(); - while (iterator.hasNext()){ - PackageBlock packageBlock = iterator.next(); - EntryGroup entryGroup = packageBlock.getEntryGroup(resourceId); - if(entryGroup == null){ - entryGroup = packageBlock.getEntryGroup(aliasId); - } - if(entryGroup != null){ - return entryGroup; - } - } - return null; - } - @Override - public Collection getEntryGroups(int resourceId) { - List results = new ArrayList<>(); - EntryGroup entryGroup = searchLocal(resourceId); - if(entryGroup!=null){ - results.add(entryGroup); - } - for(TableBlock framework:getFrameWorks()){ - results.addAll(framework.getEntryGroups(resourceId)); - } - return results; - } - @Override - public EntryGroup getEntryGroup(int resourceId) { - return search(resourceId); - } - @Override - public Collection getPackageBlocks(int packageId) { - List results=new ArrayList<>(); - PackageBlock packageBlock = getPackageBlockById(packageId); - if(packageBlock!=null){ - results.add(packageBlock); - } - for(TableBlock tableBlock:getFrameWorks()){ - results.addAll(tableBlock.getPackageBlocks(packageId)); - } - return results; - } public int searchResourceIdAlias(int resourceId){ return resolveStagedAlias(resourceId, 0); } @@ -551,6 +504,13 @@ public StagedAliasEntry getStagedAlias(int stagedResId){ public List getFrameWorks(){ return mFrameWorks; } + public Iterator frameworkIterator(){ + List frameworkList = getFrameWorks(); + if(frameworkList.size() == 0){ + return EmptyIterator.of(); + } + return frameworkList.iterator(); + } public boolean isAndroid(){ PackageBlock packageBlock = pickOne(); if(packageBlock == null){ @@ -590,7 +550,7 @@ public PackageBlock parsePublicXml(XmlPullParser parser) throws IOException, } String name = PackageBlock.readPackageName(parser, null); - PackageBlock packageBlock = getOrCreatePackage(id, name); + PackageBlock packageBlock = newPackage(id, name); packageBlock.parsePublicXml(parser); IOUtil.close(parser); return packageBlock; diff --git a/src/main/java/com/reandroid/arsc/chunk/TypeBlock.java b/src/main/java/com/reandroid/arsc/chunk/TypeBlock.java index af64dd04..8d860fb3 100755 --- a/src/main/java/com/reandroid/arsc/chunk/TypeBlock.java +++ b/src/main/java/com/reandroid/arsc/chunk/TypeBlock.java @@ -171,22 +171,12 @@ public SpecTypePair getParentSpecTypePair(){ return getParent(SpecTypePair.class); } public void cleanEntries(){ - PackageBlock packageBlock=getPackageBlock(); - List allEntries=listEntries(true); - for(Entry entry :allEntries){ - if(packageBlock!=null){ - packageBlock.removeEntryGroup(entry); - } + Iterator iterator = getEntries(); + while (iterator.hasNext()){ + Entry entry = iterator.next(); entry.setNull(true); } } - public void removeEntry(Entry entry){ - PackageBlock packageBlock=getPackageBlock(); - if(packageBlock!=null){ - packageBlock.removeEntryGroup(entry); - } - entry.setNull(true); - } public Entry getOrCreateDefinedEntry(String name){ Entry entry = getEntry(name); if(entry != null){ @@ -262,9 +252,6 @@ public void ensureEntriesCount(int count){ entryArray.ensureSize(count); entryArray.refreshCount(); } - public List listEntries(){ - return listEntries(false); - } public List listEntries(boolean skipNullBlock){ List results=new ArrayList<>(); Iterator itr = getEntryArray().iterator(skipNullBlock); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java b/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java index f91b3629..a0566b1f 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java @@ -1,205 +1,205 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.chunk.xml; +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk.xml; - import com.reandroid.arsc.chunk.ChunkType; - import com.reandroid.arsc.base.Block; - import com.reandroid.arsc.chunk.Chunk; - import com.reandroid.arsc.header.XmlNodeHeader; - import com.reandroid.arsc.item.IntegerItem; - import com.reandroid.arsc.item.ResXmlString; - import com.reandroid.arsc.pool.ResXmlStringPool; +import com.reandroid.arsc.chunk.ChunkType; +import com.reandroid.arsc.base.Block; +import com.reandroid.arsc.chunk.Chunk; +import com.reandroid.arsc.header.XmlNodeHeader; +import com.reandroid.arsc.item.IntegerItem; +import com.reandroid.arsc.item.ResXmlString; +import com.reandroid.arsc.pool.ResXmlStringPool; - import java.util.HashSet; - import java.util.Set; +import java.util.HashSet; +import java.util.Set; - class BaseXmlChunk extends Chunk { - private final IntegerItem mNamespaceReference; - private final IntegerItem mStringReference; +class BaseXmlChunk extends Chunk { + private final IntegerItem mNamespaceReference; + private final IntegerItem mStringReference; - BaseXmlChunk(ChunkType chunkType, int initialChildesCount) { - super(new XmlNodeHeader(chunkType), initialChildesCount+2); + BaseXmlChunk(ChunkType chunkType, int initialChildesCount) { + super(new XmlNodeHeader(chunkType), initialChildesCount+2); - this.mNamespaceReference=new IntegerItem(-1); - this.mStringReference=new IntegerItem(-1); + this.mNamespaceReference=new IntegerItem(-1); + this.mStringReference=new IntegerItem(-1); - addChild(mNamespaceReference); - addChild(mStringReference); - } - void onRemoved(){ - ResXmlStringPool stringPool = getStringPool(); - if(stringPool==null){ - return; - } - stringPool.removeReference(getHeaderBlock().getCommentReference()); - stringPool.removeReference(mNamespaceReference); - stringPool.removeReference(mStringReference); - } - void linkStringReferences(){ - linkStringReference(getHeaderBlock().getCommentReference()); - linkStringReference(mNamespaceReference); - linkStringReference(mStringReference); - } - private void linkStringReference(IntegerItem item){ - ResXmlString xmlString = getResXmlString(item.get()); - if(xmlString!=null){ - xmlString.addReferenceIfAbsent(item); - } - } - void unLinkStringReference(IntegerItem item){ - ResXmlString xmlString = getResXmlString(item.get()); - if(xmlString!=null){ - xmlString.removeReference(item); - } - } - public void setLineNumber(int val){ - getHeaderBlock().getLineNumber().set(val); - } - public int getLineNumber(){ - return getHeaderBlock().getLineNumber().get(); - } - public void setCommentReference(int val){ - if(val == getCommentReference()){ - return; - } - IntegerItem comment=getHeaderBlock().getCommentReference(); - unLinkStringReference(comment); - getHeaderBlock().getCommentReference().set(val); - linkStringReference(comment); - } - public int getCommentReference(){ - return getHeaderBlock().getCommentReference().get(); - } - public void setNamespaceReference(int val){ - if(val == getNamespaceReference()){ - return; - } - unLinkStringReference(mNamespaceReference); - mNamespaceReference.set(val); - linkStringReference(mNamespaceReference); - } - public int getNamespaceReference(){ - return mNamespaceReference.get(); - } - public void setStringReference(int val){ - if(val == getStringReference()){ - return; - } - unLinkStringReference(mStringReference); - mStringReference.set(val); - linkStringReference(mStringReference); - } - public int getStringReference(){ - return mStringReference.get(); - } - public ResXmlString setString(String str){ - ResXmlStringPool pool = getStringPool(); - if(pool==null){ - return null; - } - ResXmlString xmlString = pool.getOrCreate(str); - setStringReference(xmlString.getIndex()); - return xmlString; - } - public ResXmlStringPool getStringPool(){ - Block parent=getParent(); - while (parent!=null){ - if(parent instanceof ResXmlDocument){ - return ((ResXmlDocument)parent).getStringPool(); - } - if(parent instanceof ResXmlElement){ - return ((ResXmlElement)parent).getStringPool(); - } - parent=parent.getParent(); - } - return null; - } - public ResXmlString getResXmlString(int ref){ - if(ref<0){ - return null; - } - ResXmlStringPool stringPool=getStringPool(); - if(stringPool!=null){ - return stringPool.get(ref); - } - return null; - } - ResXmlString getOrCreateResXmlString(String str){ - ResXmlStringPool stringPool=getStringPool(); - if(stringPool!=null){ - return stringPool.getOrCreate(str); - } - return null; - } - String getString(int ref){ - ResXmlString xmlString=getResXmlString(ref); - if(xmlString!=null){ - return xmlString.get(); - } - return null; - } - ResXmlString getOrCreateString(String str){ - ResXmlStringPool stringPool=getStringPool(); - if(stringPool==null){ - return null; - } - return stringPool.getOrCreate(str); - } + addChild(mNamespaceReference); + addChild(mStringReference); + } + void onRemoved(){ + ResXmlStringPool stringPool = getStringPool(); + if(stringPool==null){ + return; + } + stringPool.removeReference(getHeaderBlock().getCommentReference()); + stringPool.removeReference(mNamespaceReference); + stringPool.removeReference(mStringReference); + } + void linkStringReferences(){ + linkStringReference(getHeaderBlock().getCommentReference()); + linkStringReference(mNamespaceReference); + linkStringReference(mStringReference); + } + private void linkStringReference(IntegerItem item){ + ResXmlString xmlString = getResXmlString(item.get()); + if(xmlString!=null){ + xmlString.addReferenceIfAbsent(item); + } + } + void unLinkStringReference(IntegerItem item){ + ResXmlString xmlString = getResXmlString(item.get()); + if(xmlString!=null){ + xmlString.removeReference(item); + } + } + public void setLineNumber(int val){ + getHeaderBlock().getLineNumber().set(val); + } + public int getLineNumber(){ + return getHeaderBlock().getLineNumber().get(); + } + public void setCommentReference(int val){ + if(val == getCommentReference()){ + return; + } + IntegerItem comment=getHeaderBlock().getCommentReference(); + unLinkStringReference(comment); + getHeaderBlock().getCommentReference().set(val); + linkStringReference(comment); + } + public int getCommentReference(){ + return getHeaderBlock().getCommentReference().get(); + } + void setNamespaceReference(int val){ + if(val == getNamespaceReference()){ + return; + } + unLinkStringReference(mNamespaceReference); + mNamespaceReference.set(val); + linkStringReference(mNamespaceReference); + } + int getNamespaceReference(){ + return mNamespaceReference.get(); + } + void setStringReference(int val){ + if(val == getStringReference()){ + return; + } + unLinkStringReference(mStringReference); + mStringReference.set(val); + linkStringReference(mStringReference); + } + int getStringReference(){ + return mStringReference.get(); + } + ResXmlString setString(String str){ + ResXmlStringPool pool = getStringPool(); + if(pool==null){ + return null; + } + ResXmlString xmlString = pool.getOrCreate(str); + setStringReference(xmlString.getIndex()); + return xmlString; + } + ResXmlStringPool getStringPool(){ + Block parent=getParent(); + while (parent!=null){ + if(parent instanceof ResXmlDocument){ + return ((ResXmlDocument)parent).getStringPool(); + } + if(parent instanceof ResXmlElement){ + return ((ResXmlElement)parent).getStringPool(); + } + parent=parent.getParent(); + } + return null; + } + ResXmlString getResXmlString(int ref){ + if(ref<0){ + return null; + } + ResXmlStringPool stringPool=getStringPool(); + if(stringPool!=null){ + return stringPool.get(ref); + } + return null; + } + ResXmlString getOrCreateResXmlString(String str){ + ResXmlStringPool stringPool=getStringPool(); + if(stringPool!=null){ + return stringPool.getOrCreate(str); + } + return null; + } + String getString(int ref){ + ResXmlString xmlString=getResXmlString(ref); + if(xmlString!=null){ + return xmlString.get(); + } + return null; + } + ResXmlString getOrCreateString(String str){ + ResXmlStringPool stringPool=getStringPool(); + if(stringPool==null){ + return null; + } + return stringPool.getOrCreate(str); + } - public String getName(){ - return getString(getStringReference()); - } - public String getUri(){ - return getString(getNamespaceReference()); - } - public String getComment(){ - return getString(getCommentReference()); - } - public void setComment(String comment){ - if(comment==null||comment.length()==0){ - setCommentReference(-1); - }else { - String old=getComment(); - if(comment.equals(old)){ - return; - } - ResXmlString xmlString = getOrCreateResXmlString(comment); - setCommentReference(xmlString.getIndex()); - } - } - public ResXmlElement getParentResXmlElement(){ - return getParent(ResXmlElement.class); - } - @Override - protected void onChunkRefreshed() { + public String getName(){ + return getString(getStringReference()); + } + public String getUri(){ + return getString(getNamespaceReference()); + } + public String getComment(){ + return getString(getCommentReference()); + } + public void setComment(String comment){ + if(comment==null||comment.length()==0){ + setCommentReference(-1); + }else { + String old=getComment(); + if(comment.equals(old)){ + return; + } + ResXmlString xmlString = getOrCreateResXmlString(comment); + setCommentReference(xmlString.getIndex()); + } + } + public ResXmlElement getParentResXmlElement(){ + return getParent(ResXmlElement.class); + } + @Override + protected void onChunkRefreshed() { - } - @Override - public String toString(){ - ChunkType chunkType=getHeaderBlock().getChunkType(); - if(chunkType==null){ - return super.toString(); - } - StringBuilder builder=new StringBuilder(); - builder.append(chunkType.toString()); - builder.append(": line="); - builder.append(getLineNumber()); - builder.append(" {"); - builder.append(getName()); - builder.append("}"); - return builder.toString(); - } - } + } + @Override + public String toString(){ + ChunkType chunkType=getHeaderBlock().getChunkType(); + if(chunkType==null){ + return super.toString(); + } + StringBuilder builder=new StringBuilder(); + builder.append(chunkType.toString()); + builder.append(": line="); + builder.append(getLineNumber()); + builder.append(" {"); + builder.append(getName()); + builder.append("}"); + return builder.toString(); + } +} diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java b/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java index 919df028..c1378615 100644 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java @@ -16,7 +16,7 @@ package com.reandroid.arsc.chunk.xml; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import java.util.ArrayList; import java.util.Iterator; @@ -49,7 +49,7 @@ public String getText(){ if(text == null){ text = ""; } - return ValueDecoder.escapeSpecialCharacter(text); + return XmlSanitizer.escapeSpecialCharacter(text); } return null; } diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java index 04ecf494..b6e67782 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java @@ -15,30 +15,29 @@ */ package com.reandroid.arsc.chunk.xml; +import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.coder.ValueCoder; -import com.reandroid.arsc.coder.ValueDecoder; -import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.*; import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.value.AttributeValue; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ValueItem; import com.reandroid.arsc.value.ValueType; -import com.reandroid.common.EntryStore; import com.reandroid.json.JSONObject; import com.reandroid.xml.XMLAttribute; import java.io.IOException; import java.util.Objects; -public class ResXmlAttribute extends ValueItem implements AttributeValue, Comparable{ +public class ResXmlAttribute extends AttributeValue implements Comparable{ private ReferenceItem mNSReference; private ReferenceItem mNameReference; private ReferenceItem mNameIdReference; private ReferenceItem mValueStringReference; + private ResXmlStartNamespace mLinkedNamespace; + public ResXmlAttribute(int attributeUnitSize) { super(attributeUnitSize, OFFSET_SIZE); byte[] bts = getBytesInternal(); @@ -50,6 +49,18 @@ public ResXmlAttribute() { this(20); } + public void autoSetNamespace(){ + if(getNameResourceID() == 0){ + setNamespace(null, null); + return; + } + ResourceEntry nameEntry = resolveName(); + if(nameEntry == null){ + return; + } + PackageBlock packageBlock = nameEntry.getPackageBlock(); + setNamespace(packageBlock.getUri(), packageBlock.getPrefix()); + } @Override public boolean isUndefined(){ return getNameReference() < 0; @@ -71,16 +82,46 @@ public String getFullName(){ public String getName(){ return getString(getNameReference()); } - public String getNamePrefix(){ - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement==null){ + @Override + public String decodePrefix(){ + int resourceId = getNameResourceID(); + if(resourceId == 0){ return null; } - ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); - if(startNamespace==null){ - return null; + return getNamePrefix(); + } + @Override + public String decodeName(boolean includePrefix){ + int resourceId = getNameResourceID(); + if(resourceId == 0){ + return getName(); + } + ResourceEntry resourceEntry = resolveName(); + if(resourceEntry == null || !resourceEntry.isDeclared()){ + String name = ValueCoder.decodeUnknownResourceId(false, resourceId); + if(includePrefix){ + String prefix = getNamePrefix(); + if(prefix != null){ + name = prefix + ":" + name; + } + } + return name; + } + String name = resourceEntry.getName(); + if(includePrefix && name != null){ + String prefix = getNamePrefix(); + if(prefix != null){ + name = prefix + ":" + name; + } } - return startNamespace.getPrefix(); + return name; + } + public String getNamePrefix(){ + ResXmlStartNamespace namespace = getStartNamespace(); + if(namespace != null){ + return namespace.getPrefix(); + } + return null; } // WARN! Careful this is not real value public String getValueString(){ @@ -103,11 +144,6 @@ public void setNameResourceID(int resourceId){ ResXmlID xmlID = xmlIDMap.getOrCreate(resourceId); setNameReference(xmlID.getIndex()); } - @Override - public Entry resolveName(){ - return resolve(getNameResourceID()); - } - public void setName(String name, int resourceId){ if(Objects.equals(name, getName()) && resourceId==getNameResourceID()){ return; @@ -123,27 +159,36 @@ public void setName(String name, int resourceId){ linkNameId(); } private void linkStartNameSpace(){ - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement==null){ - return; - } - ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); - if(startNamespace==null){ + unLinkStartNameSpace(); + ResXmlStartNamespace startNamespace = getStartNamespace(); + if(startNamespace == null){ return; } + mLinkedNamespace = startNamespace; startNamespace.addAttributeReference(this); } private void unLinkStartNameSpace(){ - ResXmlElement xmlElement = getParentResXmlElement(); - if(xmlElement == null){ + ResXmlStartNamespace namespace = mLinkedNamespace; + if(namespace == null){ return; } - ResXmlStartNamespace startNamespace = - xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); - if(startNamespace == null){ - return; + this.mLinkedNamespace = null; + namespace.removeAttributeReference(this); + } + private ResXmlStartNamespace getStartNamespace(){ + int uriRef = getNamespaceReference(); + if(uriRef < 0){ + return null; + } + ResXmlStartNamespace namespace = mLinkedNamespace; + if(namespace != null && namespace.getUriReference() == uriRef){ + return namespace; + } + ResXmlElement parentElement = getParentResXmlElement(); + if(parentElement != null){ + return parentElement.getStartNamespaceByUriRef(uriRef); } - startNamespace.removeAttributeReference(this); + return null; } private ResXmlString getOrCreateAttributeName(String name, int resourceId){ ResXmlStringPool stringPool = getStringPool(); @@ -210,20 +255,23 @@ public void setNamespace(String uri, String prefix){ if(parentElement == null){ return; } - ResXmlStartNamespace ns = parentElement.getOrCreateNamespace(uri, prefix); + ResXmlStartNamespace ns = parentElement.getOrCreateXmlStartNamespace(uri, prefix); setNamespaceReference(ns.getUriReference()); } public void setNamespaceReference(int ref){ if(ref == getNamespaceReference()){ return; } + setUriReference(ref); + linkStartNameSpace(); + } + void setUriReference(int ref){ StringItem stringItem = getStringItem(getNamespaceReference()); putInteger(getBytesInternal(), OFFSET_NS, ref); if(stringItem != null){ stringItem.removeReference(mNSReference); } mNSReference = link(OFFSET_NS); - linkStartNameSpace(); } int getNameReference(){ return getInteger(getBytesInternal(), OFFSET_NAME); @@ -428,7 +476,7 @@ public void fromJson(JSONObject json) { ResXmlStartNamespace ns = getParentResXmlElement().getStartNamespaceByUri(uri); if(ns==null){ ns = getParentResXmlElement().getRootResXmlElement() - .getOrCreateNamespace(uri, ""); + .getOrCreateXmlStartNamespace(uri, ""); } setNamespaceReference(ns.getUriReference()); } @@ -442,35 +490,10 @@ public void fromJson(JSONObject json) { setData(json.getInt(NAME_data)); } } - public XMLAttribute decodeToXml(EntryStore entryStore, int currentPackageId) { - int resourceId=getNameResourceID(); - String name; - if(resourceId==0){ - name=getName(); - }else { - EntryGroup group = entryStore.getEntryGroup(resourceId); - if(group==null){ - //Lets ignore such error until XML encoder implemented - //throw new XMLException("Failed to decode attribute name: " - // resourceId); - name = ValueCoder.decodeUnknownResourceId(false, resourceId); - }else { - name = group.getSpecName(); - } - } - String prefix = getNamePrefix(); - if(prefix!=null){ - name=prefix+":"+name; - } - ValueType valueType = getValueType(); - int raw = getData(); - String value = ValueDecoder.decode(entryStore, currentPackageId, (AttributeValue) this); - XMLAttribute attribute = new XMLAttribute(name, value); - attribute.setNameId(resourceId); - if(valueType==ValueType.REFERENCE||valueType==ValueType.ATTRIBUTE){ - attribute.setValueId(raw); - } - return attribute; + public XMLAttribute decodeToXml() { + return new XMLAttribute( + decodeName(true), + decodeValue()); } @Override public String toString(){ diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlDocument.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlDocument.java index bcb49515..a095509d 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlDocument.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlDocument.java @@ -24,14 +24,11 @@ import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.value.ValueType; -import com.reandroid.common.EntryStore; -import com.reandroid.common.FileChannelInputStream; import com.reandroid.json.JSONArray; import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONObject; import com.reandroid.xml.XMLDocument; import com.reandroid.xml.XMLElement; -import com.reandroid.xml.XMLException; import java.io.*; import java.util.ArrayList; @@ -47,6 +44,8 @@ public class ResXmlDocument extends Chunk private final SingleBlockContainer mResXmlElementContainer; private ApkFile mApkFile; private PackageBlock mPackageBlock; + private boolean mDestroyed; + public ResXmlDocument() { super(new HeaderBlock(ChunkType.XML),3); this.mResXmlStringPool=new ResXmlStringPool(true); @@ -59,6 +58,20 @@ public ResXmlDocument() { addChild(mResXmlElementContainer); } + public void autoSetAttributeNamespaces(){ + ResXmlElement root = getResXmlElement(); + if(root != null){ + root.autoSetAttributeNamespaces(); + } + removeUnusedNamespaces(); + } + public int removeUnusedNamespaces(){ + ResXmlElement root = getResXmlElement(); + if(root != null){ + return root.removeUnusedNamespaces(); + } + return 0; + } public String refreshFull(){ int sizeOld = getHeaderBlock().getChunkSize(); StringBuilder message = new StringBuilder(); @@ -73,6 +86,15 @@ public String refreshFull(){ appendOnce = true; } } + count = removeUnusedNamespaces(); + if(count != 0){ + if(appendOnce){ + message.append("\n"); + } + message.append("Removed unused namespaces = "); + message.append(count); + appendOnce = true; + } count = getStringPool().removeUnusedStrings().size(); if(count != 0){ if(appendOnce){ @@ -100,14 +122,20 @@ public String refreshFull(){ return null; } public void destroy(){ - ResXmlElement root = getResXmlElement(); - if(root!=null){ - root.clearChildes(); - setResXmlElement(null); + synchronized (this){ + if(mDestroyed){ + return; + } + mDestroyed = true; + ResXmlElement root = getResXmlElement(); + if(root != null){ + root.clearChildes(); + setResXmlElement(null); + } + getResXmlIDMap().destroy(); + getStringPool().destroy(); + refresh(); } - getResXmlIDMap().destroy(); - getStringPool().destroy(); - refresh(); } public void setAttributesUnitSize(int size, boolean setToAll){ ResXmlElement root = getResXmlElement(); @@ -320,28 +348,10 @@ public void fromJson(JSONObject json) { xmlElement.fromJson(json.optJSONObject(ResXmlDocument.NAME_element)); refresh(); } - public XMLDocument decodeToXml() throws XMLException { - ApkFile apkFile = getApkFile(); - if(apkFile == null){ - throw new XMLException("Null parent apk file"); - } - int currentPackageId = 0; - AndroidManifestBlock manifestBlock; - if(this instanceof AndroidManifestBlock){ - manifestBlock = ((AndroidManifestBlock)this); - }else { - manifestBlock = apkFile.getAndroidManifestBlock(); - } - if(manifestBlock!=null){ - currentPackageId = manifestBlock.guessCurrentPackageId(); - } - TableBlock tableBlock = apkFile.getTableBlock(); - return decodeToXml(tableBlock, currentPackageId); - } - public XMLDocument decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException { + public XMLDocument decodeToXml() { XMLDocument xmlDocument = new XMLDocument(); XMLElement xmlElement = getResXmlElement() - .decodeToXml(entryStore, currentPackageId); + .decodeToXml(); xmlDocument.setDocumentElement(xmlElement); return xmlDocument; } diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java index 2eb84125..e291a1a3 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java @@ -17,13 +17,13 @@ import com.reandroid.arsc.chunk.ChunkType; import com.reandroid.arsc.base.Block; +import com.reandroid.arsc.model.ResourceLibrary; import com.reandroid.arsc.container.BlockList; import com.reandroid.arsc.container.SingleBlockContainer; import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.ResXmlString; import com.reandroid.arsc.pool.ResXmlStringPool; -import com.reandroid.common.EntryStore; import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONArray; import com.reandroid.json.JSONObject; @@ -54,7 +54,75 @@ public ResXmlElement() { addChild(3, mEndElementContainer); addChild(4, mEndNamespaceList); } - + public ResXmlAttribute getIdAttribute(){ + ResXmlStartElement startElement = getStartElement(); + if(startElement != null){ + return startElement.getIdAttribute(); + } + return null; + } + public ResXmlAttribute getClassAttribute(){ + ResXmlStartElement startElement = getStartElement(); + if(startElement != null){ + return startElement.getClassAttribute(); + } + return null; + } + public ResXmlAttribute getStyleAttribute(){ + ResXmlStartElement startElement = getStartElement(); + if(startElement != null){ + return startElement.getStyleAttribute(); + } + return null; + } + public ResXmlNamespace getNamespaceAt(int i){ + return mStartNamespaceList.get(i); + } + public int getNamespaceCount(){ + return mStartNamespaceList.size(); + } + public ResXmlNamespace getNamespace(String uri, String prefix){ + return getXmlStartNamespace(uri, prefix); + } + public ResXmlNamespace getOrCreateNamespace(String uri, String prefix){ + return getOrCreateXmlStartNamespace(uri, prefix); + } + public ResXmlNamespace newNamespace(String uri, String prefix){ + return createXmlStartNamespace(uri, prefix); + } + public ResXmlNamespace getNamespaceByUri(String uri){ + return getStartNamespaceByUri(uri); + } + public ResXmlNamespace getNamespaceByPrefix(String prefix){ + return getStartNamespaceByPrefix(prefix); + } + public void autoSetAttributeNamespaces(){ + for(ResXmlAttribute attribute : listAttributes()){ + attribute.autoSetNamespace(); + } + for(ResXmlNode child : getXmlNodes()){ + if(child instanceof ResXmlElement){ + ((ResXmlElement)child).autoSetAttributeNamespaces(); + } + } + } + int removeUnusedNamespaces(){ + int count = 0; + List nsList = new ArrayList<>(getStartNamespaceList()); + for(ResXmlStartNamespace ns : nsList){ + boolean removed = ns.removeIfNoReference(); + if(removed){ + count ++; + } + } + for(ResXmlNode node : getXmlNodes()){ + if(node instanceof ResXmlElement){ + ResXmlElement child = (ResXmlElement) node; + count += child.removeUnusedNamespaces(); + } + } + return count; + } public int removeUndefinedAttributes(){ int count = 0; ResXmlStartElement start = getStartElement(); @@ -149,7 +217,7 @@ public int getEndLineNumber(){ public void setComment(String comment){ getStartElement().setComment(comment); } - public void calculatePositions(){ + void calculatePositions(){ ResXmlStartElement start = getStartElement(); if(start!=null){ start.calculatePositions(); @@ -200,16 +268,18 @@ public ResXmlElement createChildElement(String tag){ return resXmlElement; } public ResXmlAttribute getOrCreateAndroidAttribute(String name, int resourceId){ - return getOrCreateAttribute(NS_ANDROID_URI, NS_ANDROID_PREFIX, name, resourceId); + return getOrCreateAttribute( + ResourceLibrary.URI_ANDROID, + ResourceLibrary.PREFIX_ANDROID, + name, + resourceId); } public ResXmlAttribute getOrCreateAttribute(String uri, String prefix, String name, int resourceId){ ResXmlAttribute attribute = searchAttribute(name, resourceId); if(attribute == null){ attribute = createAttribute(name, resourceId); if(uri != null && resourceId != 0){ - ResXmlElement root = getRootResXmlElement(); - ResXmlStartNamespace ns = root.getOrCreateNamespace(uri, prefix); - attribute.setNamespaceReference(ns.getUriReference()); + attribute.setNamespace(uri, prefix); } } return attribute; @@ -223,7 +293,8 @@ public ResXmlAttribute getOrCreateAttribute(String name, int resourceId){ } public ResXmlAttribute createAndroidAttribute(String name, int resourceId){ ResXmlAttribute attribute=createAttribute(name, resourceId); - ResXmlStartNamespace ns = getOrCreateNamespace(NS_ANDROID_URI, NS_ANDROID_PREFIX); + ResXmlStartNamespace ns = getOrCreateXmlStartNamespace(ResourceLibrary.URI_ANDROID, + ResourceLibrary.PREFIX_ANDROID); attribute.setNamespaceReference(ns.getUriReference()); return attribute; } @@ -311,12 +382,18 @@ public String getTagUri(){ return null; } public String getTagPrefix(){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ + ResXmlStartElement startElement = getStartElement(); + if(startElement != null){ return startElement.getPrefix(); } return null; } + public void setTagNamespace(String uri, String prefix){ + ResXmlStartElement startElement = getStartElement(); + if(startElement != null){ + startElement.setTagNamespace(uri, prefix); + } + } public int getAttributeCount() { ResXmlStartElement startElement=getStartElement(); if(startElement!=null){ @@ -511,7 +588,7 @@ public ResXmlElement getRootResXmlElement(){ public ResXmlElement getParentResXmlElement(){ return getParentInstance(ResXmlElement.class); } - public ResXmlStartNamespace getStartNamespaceByUriRef(int uriRef){ + ResXmlStartNamespace getStartNamespaceByUriRef(int uriRef){ if(uriRef<0){ return null; } @@ -526,7 +603,7 @@ public ResXmlStartNamespace getStartNamespaceByUriRef(int uriRef){ } return null; } - public ResXmlStartNamespace getNamespace(String uri, String prefix){ + ResXmlStartNamespace getXmlStartNamespace(String uri, String prefix){ if(uri == null || prefix == null){ return null; } @@ -537,18 +614,18 @@ public ResXmlStartNamespace getNamespace(String uri, String prefix){ } ResXmlElement xmlElement = getParentResXmlElement(); if(xmlElement != null){ - return xmlElement.getNamespace(uri, prefix); + return xmlElement.getXmlStartNamespace(uri, prefix); } return null; } - public ResXmlStartNamespace getOrCreateNamespace(String uri, String prefix){ - ResXmlStartNamespace exist = getNamespace(uri, prefix); + public ResXmlStartNamespace getOrCreateXmlStartNamespace(String uri, String prefix){ + ResXmlStartNamespace exist = getXmlStartNamespace(uri, prefix); if(exist != null){ return exist; } - return getRootResXmlElement().createNamespace(uri, prefix); + return getRootResXmlElement().createXmlStartNamespace(uri, prefix); } - public ResXmlStartNamespace createNamespace(String uri, String prefix){ + private ResXmlStartNamespace createXmlStartNamespace(String uri, String prefix){ ResXmlStartNamespace startNamespace = new ResXmlStartNamespace(); ResXmlEndNamespace endNamespace = new ResXmlEndNamespace(); startNamespace.setEnd(endNamespace); @@ -562,7 +639,7 @@ public ResXmlStartNamespace createNamespace(String uri, String prefix){ return startNamespace; } - public ResXmlStartNamespace getStartNamespaceByUri(String uri){ + ResXmlStartNamespace getStartNamespaceByUri(String uri){ if(uri==null){ return null; } @@ -577,8 +654,8 @@ public ResXmlStartNamespace getStartNamespaceByUri(String uri){ } return null; } - public ResXmlStartNamespace getStartNamespaceByPrefix(String prefix){ - if(prefix==null){ + private ResXmlStartNamespace getStartNamespaceByPrefix(String prefix){ + if(prefix == null){ return null; } for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ @@ -586,27 +663,18 @@ public ResXmlStartNamespace getStartNamespaceByPrefix(String prefix){ return ns; } } - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement!=null){ + ResXmlElement xmlElement = getParentResXmlElement(); + if(xmlElement != null){ return xmlElement.getStartNamespaceByPrefix(prefix); } return null; } - public List getStartNamespaceList(){ + private List getStartNamespaceList(){ return mStartNamespaceList.getChildes(); } - public int getNamespaceCount(){ - return mStartNamespaceList.size(); - } - public ResXmlStartNamespace getNamespace(int index){ - return mStartNamespaceList.get(index); - } - public void addStartNamespace(ResXmlStartNamespace item){ + private void addStartNamespace(ResXmlStartNamespace item){ mStartNamespaceList.add(item); } - private List getEndNamespaceList(){ - return mEndNamespaceList.getChildes(); - } private void addEndNamespace(ResXmlEndNamespace item, boolean at_first){ if(at_first){ mEndNamespaceList.add(0, item); @@ -639,7 +707,7 @@ ResXmlStartElement newStartElement(int lineNo){ return startElement; } - public ResXmlStartElement getStartElement(){ + private ResXmlStartElement getStartElement(){ return mStartElementContainer.getItem(); } private void setStartElement(ResXmlStartElement item){ @@ -653,7 +721,7 @@ private void setEndElement(ResXmlEndElement item){ mEndElementContainer.setItem(item); } - public void addResXmlTextNode(ResXmlTextNode xmlTextNode){ + private void addResXmlTextNode(ResXmlTextNode xmlTextNode){ mBody.add(xmlTextNode); } public void addResXmlText(ResXmlText xmlText){ @@ -924,7 +992,7 @@ public void fromJson(JSONObject json) { JSONObject nsObject=nsArray.getJSONObject(i); String uri=nsObject.optString(NAME_namespace_uri, ""); String prefix=nsObject.optString(NAME_namespace_prefix, ""); - getOrCreateNamespace(uri,prefix); + getOrCreateXmlStartNamespace(uri,prefix); } } setTag(json.getString(NAME_name)); @@ -975,13 +1043,7 @@ private boolean isTextNode(JSONObject childObject){ return childObject.has(NAME_text); } - /** - * Decodes binary {@link ResXmlElement} to readable {@link XMLElement} - * @param entryStore : used for decoding attribute name and values - * @param currentPackageId : is id of current package defining this xml, used for - * decoding reference names e.g @{package.name}:string/entry_name - * */ - public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException { + public XMLElement decodeToXml() { XMLElement xmlElement = new XMLElement(getTagName()); xmlElement.setLineNumber(getStartElement().getLineNumber()); for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){ @@ -989,7 +1051,7 @@ public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throw } for(ResXmlAttribute resXmlAttribute:listAttributes()){ XMLAttribute xmlAttribute = - resXmlAttribute.decodeToXml(entryStore, currentPackageId); + resXmlAttribute.decodeToXml(); xmlElement.addAttribute(xmlAttribute); } String comment=getStartComment(); @@ -1004,7 +1066,7 @@ public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throw if(xmlNode instanceof ResXmlElement){ ResXmlElement childResXmlElement=(ResXmlElement)xmlNode; XMLElement childXMLElement = - childResXmlElement.decodeToXml(entryStore, currentPackageId); + childResXmlElement.decodeToXml(); xmlElement.addChild(childXMLElement); }else if(xmlNode instanceof ResXmlTextNode){ ResXmlTextNode childResXmlTextNode=(ResXmlTextNode)xmlNode; @@ -1040,15 +1102,6 @@ public String toString(){ } return "NULL"; } - static ResXmlElement newResXmlElement(String tag){ - ResXmlElement resXmlElement=new ResXmlElement(); - ResXmlStartElement startElement=new ResXmlStartElement(); - resXmlElement.setStartElement(startElement); - ResXmlEndElement endElement=new ResXmlEndElement(); - resXmlElement.setEndElement(endElement); - resXmlElement.setTag(tag); - return resXmlElement; - } public static final String NS_ANDROID_URI = "http://schemas.android.com/apk/res/android"; public static final String NS_ANDROID_PREFIX = "android"; diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlEndNamespace.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlEndNamespace.java index 22edb1b6..8ff388b1 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlEndNamespace.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlEndNamespace.java @@ -1,30 +1,30 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.chunk.xml; +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk.xml; - import com.reandroid.arsc.chunk.ChunkType; +import com.reandroid.arsc.chunk.ChunkType; - public class ResXmlEndNamespace extends ResXmlNamespace{ - public ResXmlEndNamespace() { - super(ChunkType.XML_END_NAMESPACE); - } - public ResXmlStartNamespace getStart(){ - return (ResXmlStartNamespace) getPair(); - } - public void setStart(ResXmlStartNamespace namespace){ - setPair(namespace); - } - } +public class ResXmlEndNamespace extends ResXmlNamespaceChunk { + public ResXmlEndNamespace() { + super(ChunkType.XML_END_NAMESPACE); + } + public ResXmlStartNamespace getStart(){ + return (ResXmlStartNamespace) getPair(); + } + public void setStart(ResXmlStartNamespace namespace){ + setPair(namespace); + } +} diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespace.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespace.java index 1d26435c..1cad78ac 100644 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespace.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespace.java @@ -1,87 +1,23 @@ /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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. - */ + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk.xml; -import com.reandroid.arsc.chunk.ChunkType; -import com.reandroid.arsc.item.ResXmlString; +import com.reandroid.common.Namespace; -abstract class ResXmlNamespace extends BaseXmlChunk{ - private ResXmlNamespace mPair; - ResXmlNamespace(ChunkType chunkType) { - super(chunkType, 0); - } - @Override - public String getUri(){ - return getString(getUriReference()); - } - public void setUri(String uri){ - ResXmlString xmlString = getOrCreateString(uri); - if(xmlString==null){ - throw new IllegalArgumentException("Null ResXmlString, add to parent element first"); - } - setUriReference(xmlString.getIndex()); - } - public String getPrefix(){ - return getString(getPrefixReference()); - } - public void setPrefix(String prefix){ - ResXmlString xmlString = getOrCreateString(prefix); - if(xmlString==null){ - throw new IllegalArgumentException("Null ResXmlString, add to parent element first"); - } - setPrefixReference(xmlString.getIndex()); - } - public int getUriReference(){ - return getStringReference(); - } - public void setUriReference(int ref){ - setStringReference(ref); - ResXmlNamespace pair=getPair(); - if(pair!=null && pair.getUriReference()!=ref){ - pair.setUriReference(ref); - } - } - public int getPrefixReference(){ - return getNamespaceReference(); - } - public void setPrefixReference(int ref){ - setNamespaceReference(ref); - ResXmlNamespace pair=getPair(); - if(pair!=null && pair.getPrefixReference()!=ref){ - pair.setPrefixReference(ref); - } - } - ResXmlNamespace getPair(){ - return mPair; - } - void setPair(ResXmlNamespace pair){ - if(pair==this){ - return; - } - this.mPair=pair; - if(pair !=null && pair.getPair()!=this){ - pair.setPair(this); - } - } - @Override - public String toString(){ - String uri=getUri(); - if(uri==null){ - return super.toString(); - } - return "xmlns:"+getPrefix()+"=\""+getUri()+"\""; - } +public interface ResXmlNamespace extends Namespace { + void setUri(String uri); + void setPrefix(String uri); } diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespaceChunk.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespaceChunk.java new file mode 100644 index 00000000..967d623b --- /dev/null +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlNamespaceChunk.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.chunk.xml; + +import com.reandroid.arsc.chunk.ChunkType; +import com.reandroid.arsc.item.ResXmlString; + +import java.util.Objects; + +abstract class ResXmlNamespaceChunk extends BaseXmlChunk implements ResXmlNamespace{ + private ResXmlNamespaceChunk mPair; + ResXmlNamespaceChunk(ChunkType chunkType) { + super(chunkType, 0); + } + @Override + public String getUri(){ + return getString(getUriReference()); + } + @Override + public void setUri(String uri){ + if(uri == null){ + setUriReference(-1); + return; + } + ResXmlString xmlString = getOrCreateString(uri); + if(xmlString == null){ + throw new IllegalArgumentException("Null ResXmlString, add to parent element first"); + } + setUriReference(xmlString.getIndex()); + } + public String getPrefix(){ + return getString(getPrefixReference()); + } + public void setPrefix(String prefix){ + if(prefix == null){ + setPrefixReference(-1); + return; + } + ResXmlString xmlString = getOrCreateString(prefix); + if(xmlString == null){ + throw new IllegalArgumentException("Null ResXmlString, add to parent element first"); + } + setPrefixReference(xmlString.getIndex()); + } + public int getUriReference(){ + return getStringReference(); + } + void setUriReference(int ref){ + int old = getUriReference(); + setStringReference(ref); + ResXmlNamespaceChunk pair=getPair(); + if(pair!=null && pair.getUriReference()!=ref){ + pair.setUriReference(ref); + } + if(old != ref){ + onUriReferenceChanged(old, ref); + } + } + void onUriReferenceChanged(int old, int uriReference){ + + } + public int getPrefixReference(){ + return getNamespaceReference(); + } + public void setPrefixReference(int ref){ + setNamespaceReference(ref); + ResXmlNamespaceChunk pair=getPair(); + if(pair!=null && pair.getPrefixReference()!=ref){ + pair.setPrefixReference(ref); + } + } + ResXmlNamespaceChunk getPair(){ + return mPair; + } + void setPair(ResXmlNamespaceChunk pair){ + if(pair==this){ + return; + } + this.mPair=pair; + if(pair !=null && pair.getPair()!=this){ + pair.setPair(this); + } + } + @Override + public String toString(){ + String uri=getUri(); + if(uri==null){ + return super.toString(); + } + return "xmlns:"+getPrefix()+"=\""+getUri()+"\""; + } +} diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java index 393a9006..886f03c2 100644 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java @@ -16,8 +16,7 @@ package com.reandroid.arsc.chunk.xml; import android.content.res.XmlResourceParser; -import com.reandroid.arsc.ApkFile; -import com.reandroid.arsc.coder.Decoder; +import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.value.ValueType; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; @@ -29,7 +28,7 @@ import java.util.Objects; public class ResXmlPullParser implements XmlResourceParser { - private Decoder mDecoder; + private PackageBlock mCurrentPackage; private final ParserEventList mEventList = new ParserEventList(); private ResXmlDocument mDocument; private boolean mDocumentCreatedHere; @@ -38,17 +37,26 @@ public class ResXmlPullParser implements XmlResourceParser { private boolean reportNamespaceAttrs; private boolean mIsTagStared; - public ResXmlPullParser(Decoder decoder){ - this.mDecoder = decoder; + public ResXmlPullParser(PackageBlock packageBlock){ + this.mCurrentPackage = packageBlock; this.processNamespaces = false; this.reportNamespaceAttrs = false; } public ResXmlPullParser(){ this(null); } + public PackageBlock getCurrentPackage(){ + return mCurrentPackage; + } + public void setCurrentPackage(PackageBlock packageBlock){ + this.mCurrentPackage = packageBlock; + if(mDocument != null){ + mDocument.setPackageBlock(packageBlock); + } + } public synchronized ResXmlPullParser getParser(){ if(isBusy()){ - return new ResXmlPullParser(getDecoder()); + return new ResXmlPullParser(getCurrentPackage()); } closeDocument(); return this; @@ -59,39 +67,16 @@ public synchronized boolean isBusy() { public synchronized void setResXmlDocument(ResXmlDocument xmlDocument){ closeDocument(); this.mDocument = xmlDocument; + PackageBlock packageBlock = xmlDocument.getPackageBlock(); + if(packageBlock == null){ + xmlDocument.setPackageBlock(getCurrentPackage()); + } initDefaultFeatures(); - initializeDecoder(xmlDocument); xmlDocument.addEvents(mEventList); } public ResXmlDocument getResXmlDocument() { return mDocument; } - - public void setDecoder(Decoder decoder) { - this.mDecoder = decoder; - } - public Decoder getDecoder(){ - return mDecoder; - } - private void initializeDecoder(ResXmlDocument xmlDocument){ - Decoder decoder = this.mDecoder; - if(decoder!=null){ - if(decoder.getApkFile()==null){ - decoder.setApkFile(xmlDocument.getApkFile()); - } - return; - } - ApkFile apkFile = xmlDocument.getApkFile(); - if(apkFile!=null){ - decoder = apkFile.getDecoder(); - if(decoder!=null){ - this.mDecoder = decoder; - return; - } - } - mDecoder = Decoder.create(xmlDocument); - } - public void closeDocument(){ mEventList.clear(); mIsTagStared = false; @@ -302,10 +287,10 @@ public float getAttributeFloatValue(int index, float defaultValue) { @Override public String getIdAttribute() { - ResXmlStartElement startElement = getResXmlStartElement(); - if(startElement!=null){ - ResXmlAttribute attribute = startElement.getIdAttribute(); - if(attribute!=null){ + ResXmlElement currentElement = getCurrentElement(); + if(currentElement != null){ + ResXmlAttribute attribute = currentElement.getIdAttribute(); + if(attribute != null){ return attribute.getName(); } } @@ -313,10 +298,10 @@ public String getIdAttribute() { } @Override public String getClassAttribute() { - ResXmlStartElement startElement = getResXmlStartElement(); - if(startElement!=null){ - ResXmlAttribute attribute = startElement.getClassAttribute(); - if(attribute!=null){ + ResXmlElement currentElement = getCurrentElement(); + if(currentElement!=null){ + ResXmlAttribute attribute = currentElement.getClassAttribute(); + if(attribute != null){ return attribute.getName(); } } @@ -324,10 +309,10 @@ public String getClassAttribute() { } @Override public int getIdAttributeResourceValue(int defaultValue) { - ResXmlStartElement startElement = getResXmlStartElement(); - if(startElement!=null){ - ResXmlAttribute attribute = startElement.getIdAttribute(); - if(attribute!=null){ + ResXmlElement currentElement = getCurrentElement(); + if(currentElement != null){ + ResXmlAttribute attribute = currentElement.getIdAttribute(); + if(attribute != null){ return attribute.getNameResourceID(); } } @@ -335,10 +320,10 @@ public int getIdAttributeResourceValue(int defaultValue) { } @Override public int getStyleAttribute() { - ResXmlStartElement startElement = getResXmlStartElement(); - if(startElement!=null){ - ResXmlAttribute attribute = startElement.getStyleAttribute(); - if(attribute!=null){ + ResXmlElement currentElement = getCurrentElement(); + if(currentElement != null){ + ResXmlAttribute attribute = currentElement.getStyleAttribute(); + if(attribute != null){ return attribute.getNameResourceID(); } } @@ -417,7 +402,7 @@ public int getNamespaceCount(int depth) throws XmlPullParserException { public String getNamespacePrefix(int pos) throws XmlPullParserException { ResXmlElement element = getCurrentElement(); if(element!=null){ - return element.getNamespace(pos).getPrefix(); + return element.getNamespaceAt(pos).getPrefix(); } return null; } @@ -425,17 +410,17 @@ public String getNamespacePrefix(int pos) throws XmlPullParserException { public String getNamespaceUri(int pos) throws XmlPullParserException { ResXmlElement element = getCurrentElement(); if(element!=null){ - return element.getNamespace(pos).getUri(); + return element.getNamespaceAt(pos).getUri(); } return null; } @Override public String getNamespace(String prefix) { ResXmlElement element = getCurrentElement(); - if(element!=null){ - ResXmlStartNamespace startNamespace = element.getStartNamespaceByPrefix(prefix); - if(startNamespace!=null){ - return startNamespace.getUri(); + if(element != null){ + ResXmlNamespace namespace = element.getNamespaceByPrefix(prefix); + if(namespace != null){ + return namespace.getUri(); } } return null; @@ -545,26 +530,16 @@ public boolean isAttributeDefault(int index) { return false; } private String decodeAttributeName(ResXmlAttribute attribute){ - if(attribute == null){ - return null; - } - String name; - int resourceId = attribute.getNameResourceID(); - if(resourceId == 0 || mDecoder==null){ - name = attribute.getName(); - }else { - name = mDecoder.decodeResourceName(attribute.getNameResourceID(), true); - if(processNamespaces){ - name = attribute.getNamePrefix() + ":" + name; - } + if(attribute != null){ + return attribute.decodeName(processNamespaces); } - return name; + return null; } private String decodeAttributeValue(ResXmlAttribute attribute){ - if(attribute==null){ - return null; + if(attribute != null){ + return attribute.decodeValue(); } - return mDecoder.decodeAttributeValue(attribute); + return null; } public ResXmlAttribute getResXmlAttributeAt(int index){ index = getRealAttributeIndex(index); @@ -587,13 +562,6 @@ public ResXmlAttribute getAttribute(String namespace, String name) { } return null; } - private ResXmlStartElement getResXmlStartElement(){ - ResXmlElement element = getCurrentElement(); - if(element!=null){ - return element.getStartElement(); - } - return null; - } public ResXmlElement getCurrentElement() { int type = mEventList.getType(); if(type==START_TAG||type==END_TAG){ @@ -618,17 +586,13 @@ private boolean isCountNamespacesAsAttribute(){ return processNamespaces & reportNamespaceAttrs; } private String getNamespaceAttributeName(int index){ - ResXmlStartNamespace namespace = getCurrentElement() - .getNamespace(index); - String prefix = namespace.getPrefix(); - if(processNamespaces){ - prefix = "xmlns:" + prefix; - } - return prefix; + ResXmlNamespace namespace = getCurrentElement() + .getNamespaceAt(index); + return "xmlns:" + namespace.getPrefix(); } private String getNamespaceAttributeValue(int index){ - ResXmlStartNamespace namespace = getCurrentElement() - .getNamespace(index); + ResXmlNamespace namespace = getCurrentElement() + .getNamespaceAt(index); return namespace.getUri(); } @Override @@ -706,6 +670,7 @@ private void loadResXmlDocument(InputStream inputStream) throws XmlPullParserExc ResXmlDocument xmlDocument = new ResXmlDocument(); try { xmlDocument.readBytes(inputStream); + xmlDocument.setPackageBlock(getCurrentPackage()); } catch (IOException exception) { XmlPullParserException pullParserException = new XmlPullParserException(exception.getMessage()); pullParserException.initCause(exception); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartElement.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartElement.java index 2f2f3349..36b57be5 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartElement.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartElement.java @@ -17,11 +17,9 @@ import com.reandroid.arsc.chunk.ChunkType; import com.reandroid.arsc.array.ResXmlAttributeArray; -import com.reandroid.arsc.item.ResXmlString; import com.reandroid.arsc.item.ShortItem; import java.util.Collection; -import java.util.Set; public class ResXmlStartElement extends BaseXmlChunk { private final ShortItem mAttributeStart; @@ -76,15 +74,17 @@ public ResXmlAttribute newAttribute(){ void linkStringReferences(){ super.linkStringReferences(); ResXmlEndElement end = getResXmlEndElement(); - if(end!=null){ + if(end != null){ end.linkStringReferences(); } + linkNamespace(); } @Override void onRemoved(){ super.onRemoved(); + unlinkNamespace(); ResXmlEndElement end = getResXmlEndElement(); - if(end!=null){ + if(end != null){ end.onRemoved(); } for(ResXmlAttribute attr:listResXmlAttributes()){ @@ -95,6 +95,18 @@ void onRemoved(){ protected void onPreRefreshRefresh(){ sortAttributes(); } + void unlinkNamespace(){ + ResXmlStartNamespace namespace = getResXmlStartNamespace(); + if(namespace != null){ + namespace.removeElementReference(this); + } + } + void linkNamespace(){ + ResXmlStartNamespace namespace = getResXmlStartNamespace(); + if(namespace != null){ + namespace.addElementReference(this); + } + } private void sortAttributes(){ ResXmlAttributeArray array = getResXmlAttributeArray(); @@ -196,12 +208,17 @@ public ResXmlAttribute searchAttributeByResourceId(int resourceId){ return null; } public String getTagName(){ - String prefix=getPrefix(); - String name=getName(); - if(prefix==null){ - return name; + return getTagName(true); + } + public String getTagName(boolean includePrefix){ + String name = getName(); + if(includePrefix){ + String prefix = getPrefix(); + if(prefix != null){ + name = prefix + ":" + name; + } } - return prefix+":"+name; + return name; } public void setName(String name){ setString(name); @@ -218,26 +235,39 @@ public ResXmlAttributeArray getResXmlAttributeArray(){ } public String getUri(){ - int uriRef=getNamespaceReference(); - if(uriRef<0){ - return null; - } - ResXmlElement parentElement=getParentResXmlElement(); - ResXmlStartNamespace startNamespace=parentElement.getStartNamespaceByUriRef(uriRef); - if(startNamespace!=null){ + ResXmlStartNamespace startNamespace = getResXmlStartNamespace(); + if(startNamespace != null){ return startNamespace.getUri(); } return null; } public String getPrefix(){ - int uriRef=getNamespaceReference(); - if(uriRef<0){ + ResXmlStartNamespace startNamespace = getResXmlStartNamespace(); + if(startNamespace != null){ + return startNamespace.getPrefix(); + } + return null; + } + public void setTagNamespace(String uri, String prefix){ + unlinkNamespace(); + if(uri == null || prefix == null){ + return; + } + ResXmlElement parentElement = getParentResXmlElement(); + if(parentElement == null){ + return; + } + ResXmlStartNamespace ns = parentElement.getOrCreateXmlStartNamespace(uri, prefix); + setNamespaceReference(ns.getUriReference()); + } + private ResXmlStartNamespace getResXmlStartNamespace(){ + int uriRef = getNamespaceReference(); + if(uriRef < 0){ return null; } - ResXmlElement parentElement=getParentResXmlElement(); - ResXmlStartNamespace startNamespace=parentElement.getStartNamespaceByUriRef(uriRef); - if(startNamespace!=null){ - return startNamespace.getPrefix(); + ResXmlElement parentElement = getParentResXmlElement(); + if(parentElement != null){ + return parentElement.getStartNamespaceByUriRef(uriRef); } return null; } diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java index 35705379..e2600417 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java @@ -20,19 +20,31 @@ import com.reandroid.xml.XMLAttribute; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; -public class ResXmlStartNamespace extends ResXmlNamespace { +public class ResXmlStartNamespace extends ResXmlNamespaceChunk { private final Set mReferencedAttributes; + private final Set mReferencedElements; public ResXmlStartNamespace() { super(ChunkType.XML_START_NAMESPACE); this.mReferencedAttributes = new HashSet<>(); + this.mReferencedElements = new HashSet<>(); } - public ResXmlEndNamespace getEnd(){ + @Override + void onUriReferenceChanged(int old, int uriReference){ + for(ResXmlAttribute attribute : mReferencedAttributes){ + attribute.setUriReference(uriReference); + } + for(ResXmlStartElement element : mReferencedElements){ + element.setNamespaceReference(uriReference); + } + } + ResXmlEndNamespace getEnd(){ return (ResXmlEndNamespace) getPair(); } - public void setEnd(ResXmlEndNamespace namespace){ + void setEnd(ResXmlEndNamespace namespace){ setPair(namespace); } @Override @@ -50,26 +62,46 @@ void onRemoved(){ end.onRemoved(); } mReferencedAttributes.clear(); + mReferencedElements.clear(); } - public boolean hasReferencedAttributes(){ - return mReferencedAttributes.size()>0; + public boolean hasReferences(){ + return mReferencedAttributes.size() > 0 + || mReferencedElements.size() > 0; } - public void clearReferencedAttributes(){ - mReferencedAttributes.clear(); - } - public Set getReferencedAttributes(){ - return mReferencedAttributes; + public Iterator getReferencedAttributes(){ + return mReferencedAttributes.iterator(); } void addAttributeReference(ResXmlAttribute attribute){ - if(attribute!=null){ + if(attribute != null){ mReferencedAttributes.add(attribute); } } void removeAttributeReference(ResXmlAttribute attribute){ - if(attribute!=null){ + if(attribute != null){ mReferencedAttributes.remove(attribute); } } + void addElementReference(ResXmlStartElement element){ + if(element != null){ + mReferencedElements.add(element); + } + } + void removeElementReference(ResXmlStartElement element){ + if(element != null){ + mReferencedElements.remove(element); + } + } + boolean removeIfNoReference(){ + if(hasReferences()){ + return false; + } + ResXmlElement parent = getParentResXmlElement(); + if(parent != null){ + parent.removeNamespace(this); + return true; + } + return false; + } public XMLAttribute decodeToXml(){ String uri=getUri(); String prefix=getPrefix(); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlTextNode.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlTextNode.java index bb3b5a8b..4229e872 100644 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlTextNode.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlTextNode.java @@ -15,7 +15,7 @@ */ package com.reandroid.arsc.chunk.xml; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.XmlSanitizer; import com.reandroid.json.JSONObject; import com.reandroid.xml.XMLText; @@ -102,7 +102,7 @@ public void fromJson(JSONObject json) { setText(json.optString(NAME_text, null)); } public XMLText decodeToXml() { - XMLText xmlText=new XMLText(ValueDecoder.escapeSpecialCharacter(getText())); + XMLText xmlText=new XMLText(XmlSanitizer.escapeSpecialCharacter(getText())); xmlText.setLineNumber(getLineNumber()); return xmlText; } diff --git a/src/main/java/com/reandroid/arsc/coder/CommonType.java b/src/main/java/com/reandroid/arsc/coder/CommonType.java index f3b3753c..141ae8d1 100644 --- a/src/main/java/com/reandroid/arsc/coder/CommonType.java +++ b/src/main/java/com/reandroid/arsc/coder/CommonType.java @@ -203,6 +203,14 @@ public String name(){ public ValueType[] valueTypes() { return valueTypes; } + public boolean isDifferent(ValueType valueType){ + if(valueType == null + || valueType == ValueType.NULL + || valueType.isReference()){ + return false; + } + return !contains(valueType); + } public boolean contains(ValueType valueType){ for(ValueType vt : this.valueTypes){ if(vt == valueType){ diff --git a/src/main/java/com/reandroid/arsc/coder/Decoder.java b/src/main/java/com/reandroid/arsc/coder/Decoder.java deleted file mode 100644 index efaedbe0..00000000 --- a/src/main/java/com/reandroid/arsc/coder/Decoder.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.coder; - -import com.reandroid.apk.AndroidFrameworks; -import com.reandroid.apk.FrameworkApk; -import com.reandroid.arsc.ApkFile; -import com.reandroid.arsc.chunk.MainChunk; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.TableBlock; -import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; -import com.reandroid.arsc.chunk.xml.ResXmlDocument; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.util.FrameworkTable; -import com.reandroid.arsc.value.AttributeValue; -import com.reandroid.arsc.value.Value; -import com.reandroid.arsc.value.ValueType; -import com.reandroid.common.EntryStore; - -import java.io.IOException; - -public class Decoder { - private final EntryStore entryStore; - private int currentPackageId; - private ApkFile mApkFile; - public Decoder(EntryStore entryStore, int currentPackageId){ - this.entryStore = entryStore; - this.currentPackageId = currentPackageId; - } - public String decodeResourceName(int resourceId){ - return decodeResourceName(resourceId, true); - } - public String decodeResourceName(int resourceId, boolean defaultHex){ - if(resourceId == 0){ - return null; - } - EntryGroup entryGroup = getEntryStore().getEntryGroup(resourceId); - if(entryGroup!=null){ - return entryGroup.getSpecName(); - } - if(defaultHex){ - return hexResourceName(resourceId); - } - return null; - } - private String hexResourceName(int resourceId){ - return ValueCoder.decodeUnknownResourceId(false, resourceId); - } - public String decodeValue(Value value){ - if(value==null){ - return null; - } - ValueType valueType = value.getValueType(); - if(valueType == ValueType.STRING){ - return value.getValueAsString(); - } - return ValueDecoder.decode(getEntryStore(), getCurrentPackageId(), value); - } - public String decodeAttributeValue(AttributeValue attributeValue){ - return decodeAttributeValue(attributeValue, true); - } - public String decodeAttributeValue(AttributeValue attributeValue, boolean escapeStartChar){ - if(attributeValue == null){ - return null; - } - String result = ValueDecoder.decode(getEntryStore(), getCurrentPackageId(), attributeValue); - if(!escapeStartChar || result == null || result.length() == 0 - || attributeValue.getValueType() != ValueType.STRING){ - return result; - } - return ValueDecoder.escapeSpecialCharacter(result); - } - private EntryStore getEntryStore() { - return entryStore; - } - public int getCurrentPackageId() { - return currentPackageId; - } - public void setCurrentPackageId(int currentPackageId) { - this.currentPackageId = currentPackageId; - } - public ApkFile getApkFile(){ - return mApkFile; - } - public void setApkFile(ApkFile apkFile) { - this.mApkFile = apkFile; - } - public boolean isNullDecoder(){ - return false; - } - - public static Decoder create(ResXmlDocument resXmlDocument){ - MainChunk mainChunk = resXmlDocument.getMainChunk(); - if(mainChunk == null){ - return getNullEntryStoreDecoder(); - } - ApkFile apkFile = mainChunk.getApkFile(); - if(apkFile == null){ - return getNullEntryStoreDecoder(); - } - TableBlock tableBlock = apkFile.getTableBlock(); - if(tableBlock == null){ - return getNullEntryStoreDecoder(); - } - AndroidManifestBlock manifestBlock = apkFile.getAndroidManifestBlock(); - if(manifestBlock!=null){ - int currentPackageId = manifestBlock.guessCurrentPackageId(); - if(currentPackageId!=0){ - return create(tableBlock, currentPackageId); - } - } - return create(tableBlock); - } - public static Decoder create(TableBlock tableBlock){ - if(!tableBlock.hasFramework() && !tableBlock.isAndroid()){ - tableBlock.addFramework(getFramework()); - } - int currentPackageId; - PackageBlock packageBlock = tableBlock.pickOne(); - if(packageBlock!=null){ - currentPackageId = packageBlock.getId(); - }else { - // 0x7f most common - currentPackageId = 0x7f; - } - return create(tableBlock, currentPackageId); - } - public static Decoder create(TableBlock tableBlock, int currentPackageId){ - if(!tableBlock.hasFramework() && !tableBlock.isAndroid()){ - TableBlock framework = getFramework(); - if(framework!=null){ - PackageBlock packageBlock = framework.pickOne(); - if(packageBlock!=null && packageBlock.getId() != currentPackageId){ - tableBlock.addFramework(framework); - } - } - } - return new Decoder(tableBlock, currentPackageId); - } - private static TableBlock getFramework(){ - try { - FrameworkApk frameworkApk = AndroidFrameworks.getCurrent(); - if(frameworkApk == null){ - frameworkApk = AndroidFrameworks.getLatest(); - AndroidFrameworks.setCurrent(frameworkApk); - } - return frameworkApk.getTableBlock(); - } catch (IOException ignored) { - } - // Should not reach here but to be safe return dummy - return new FrameworkTable(); - } - - public static Decoder getNullEntryStoreDecoder(){ - if(NULL_ENTRY_STORE_DECODER!=null){ - return NULL_ENTRY_STORE_DECODER; - } - synchronized (Decoder.class){ - NullEntryDecoder decoder = new NullEntryDecoder(getFramework(), 0x7f); - NULL_ENTRY_STORE_DECODER = decoder; - return decoder; - } - } - static class NullEntryDecoder extends Decoder{ - public NullEntryDecoder(EntryStore entryStore, int currentPackageId) { - super(entryStore, currentPackageId); - } - @Override - public boolean isNullDecoder(){ - return true; - } - } - private static NullEntryDecoder NULL_ENTRY_STORE_DECODER; -} diff --git a/src/main/java/com/reandroid/arsc/coder/ReferenceString.java b/src/main/java/com/reandroid/arsc/coder/ReferenceString.java index 1f555b6e..0f0e9521 100644 --- a/src/main/java/com/reandroid/arsc/coder/ReferenceString.java +++ b/src/main/java/com/reandroid/arsc/coder/ReferenceString.java @@ -15,6 +15,8 @@ */ package com.reandroid.arsc.coder; +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.value.ValueType; public class ReferenceString { @@ -29,6 +31,13 @@ public ReferenceString(String prefix, String packageName, String type, String na this.type = type; this.name = name; } + public EncodeResult encode(TableBlock tableBlock){ + ResourceEntry resourceEntry = tableBlock.getResource(packageName, type, name); + if(resourceEntry != null){ + return new EncodeResult(getValueType(), resourceEntry.getResourceId()); + } + return null; + } public ValueType getValueType(){ if("?".equals(prefix)){ return ValueType.ATTRIBUTE; diff --git a/src/main/java/com/reandroid/arsc/coder/ValueDecoder.java b/src/main/java/com/reandroid/arsc/coder/ValueDecoder.java deleted file mode 100755 index 9acba043..00000000 --- a/src/main/java/com/reandroid/arsc/coder/ValueDecoder.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.coder; - -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.TableBlock; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.item.TableString; -import com.reandroid.arsc.pool.TableStringPool; -import com.reandroid.arsc.value.*; -import com.reandroid.arsc.value.attribute.AttributeBag; -import com.reandroid.common.EntryStore; - -import java.util.Collection; -import java.util.Iterator; - -public class ValueDecoder { - - public static String escapeSpecialCharacter(String text){ - if(text==null || text.length()==0){ - return text; - } - if(isSpecialCharacter(text.charAt(0))){ - return '\\' +text; - } - return text; - } - public static String unEscapeUnQuote(String text){ - if(text==null || text.length()<2){ - return text; - } - char first = text.charAt(0); - if(first == '"'){ - return unQuoteWhitespace(text); - } - if(first != '\\' || !isSpecialCharacter(text.charAt(1))){ - return text; - } - return text.substring(1); - } - public static String unEscapeSpecialCharacter(String text){ - if(text==null || text.length()<2){ - return text; - } - if(text.charAt(0)!='\\' || !isSpecialCharacter(text.charAt(1))){ - return text; - } - return text.substring(1); - } - public static String quoteWhitespace(String text){ - if(!isWhiteSpace(text)){ - return text; - } - return "\"" + text + "\""; - } - public static String unQuoteWhitespace(String text){ - if(text == null || text.length() < 3){ - return text; - } - if(text.charAt(0) != '"' || text.charAt(text.length()-1) != '"'){ - return text; - } - String unQuoted = text.substring(1, text.length()-1); - if(!isWhiteSpace(unQuoted)){ - return text; - } - return unQuoted; - } - private static boolean isWhiteSpace(String text){ - if(text == null || text.length() == 0){ - return false; - } - char[] chars = text.toCharArray(); - for(int i = 0; i < chars.length; i++){ - if(!isWhiteSpace(chars[i])){ - return false; - } - } - return true; - } - private static boolean isWhiteSpace(char ch){ - switch (ch){ - case ' ': - case '\n': - case '\r': - case '\t': - return true; - default: - return false; - } - } - private static boolean isSpecialCharacter(char ch){ - switch (ch){ - case '@': - case '?': - case '#': - return true; - default: - return false; - } - } - public static EncodeResult encodeColor(String value){ - return ValueCoder.encode(value, CommonType.COLOR.valueTypes()); - } - public static EncodeResult encodeDimensionOrFraction(String value){ - EncodeResult encodeResult = ValueCoder.encode(value, ValueType.DIMENSION); - if(encodeResult == null){ - encodeResult = ValueCoder.encode(value, ValueType.FRACTION); - } - return encodeResult; - } - - public static String decodeAttributeName(EntryStore store, PackageBlock currentPackage, int resourceId){ - EntryGroup entryGroup = searchEntryGroup(store, currentPackage, resourceId); - if(entryGroup==null){ - return ValueCoder.decodeUnknownResourceId(true, resourceId); - } - Entry entry = entryGroup.pickOne(); - if(entry == null){ - return ValueCoder.decodeUnknownResourceId(true, resourceId); - } - String prefix=null; - if(currentPackage!=null){ - String name=currentPackage.getName(); - String other= entry.getPackageBlock().getName(); - if(!name.equals(other)){ - prefix=other+":"; - } - } - String name=entryGroup.getSpecName(); - if(prefix!=null){ - name=prefix+name; - } - return name; - } - public static String decodeAttribute(EntryStore store, int attrResId, int rawValue){ - AttributeBag attributeBag = getAttributeBag(store, attrResId); - if(attributeBag==null){ - return null; - } - return attributeBag.decodeAttributeValue(store, rawValue); - } - - public static String decodeEntryValue(EntryStore store, PackageBlock currentPackage, ValueType valueType, int data){ - if(store==null || currentPackage==null){ - return null; - } - if(valueType==ValueType.STRING){ - return decodeIntEntryString(currentPackage, data); - } - boolean is_reference=false; - boolean is_attribute=false; - if(valueType==ValueType.REFERENCE){ - if(data==0){ - return "@null"; - } - is_reference=true; - } - if(valueType==ValueType.ATTRIBUTE){ - if(data==0){ - return "?null"; - } - is_attribute=true; - } - if(is_reference || is_attribute){ - String ref=buildReferenceValue(store, valueType, currentPackage.getName(), data); - if(ref!=null){ - return ref; - } - return ValueCoder.decodeUnknownResourceId(is_reference, data); - } - return decode(valueType, data); - } - public static String decode(EntryStore entryStore, int currentPackageId, Value value){ - - ValueType valueType = value.getValueType(); - if(valueType == ValueType.STRING){ - return value.getValueAsString(); - } - int data = value.getData(); - if(valueType==ValueType.REFERENCE || valueType==ValueType.ATTRIBUTE){ - String currentPackageName = getPackageName(entryStore, currentPackageId); - return buildReferenceValue(entryStore, - valueType, currentPackageName, - data); - } - return decode(valueType, data); - } - public static String decode(EntryStore entryStore, int currentPackageId, AttributeValue attributeValue){ - ValueType valueType = attributeValue.getValueType(); - if(valueType == ValueType.STRING){ - return attributeValue.getValueAsString(); - } - int data = attributeValue.getData(); - if(valueType==ValueType.REFERENCE || valueType==ValueType.ATTRIBUTE){ - String currentPackageName = getPackageName(entryStore, currentPackageId); - return buildReferenceValue(entryStore, - valueType, currentPackageName, - data); - } - if(valueType==ValueType.DEC || valueType==ValueType.HEX){ - String result = decodeAttribute(entryStore, - attributeValue.getNameResourceID(), - attributeValue.getData()); - if(result!=null){ - return result; - } - } - return decode(valueType, data); - } - public static String decode(ValueType valueType, int data){ - return ValueCoder.decode(valueType, data); - } - - public static String buildReference(String currentPackageName, - String referredPackageName, - char atOrQues, - String typeName, - String resourceName){ - StringBuilder builder=new StringBuilder(); - if(atOrQues!=0){ - builder.append(atOrQues); - } - if(!isEqualString(currentPackageName, referredPackageName)){ - if(!isEmpty(currentPackageName) && !isEmpty(referredPackageName)){ - builder.append(referredPackageName); - if(!referredPackageName.endsWith(":")){ - builder.append(':'); - } - } - } - if(!isEmpty(typeName)){ - builder.append(typeName); - builder.append('/'); - } - builder.append(resourceName); - return builder.toString(); - } - - private static String buildReferenceValue(EntryStore entryStore, ValueType valueType, String currentPackageName, int resourceId){ - char atOrQues; - if(valueType==ValueType.REFERENCE){ - if(resourceId==0){ - return "@null"; - } - atOrQues='@'; - }else if(valueType==ValueType.ATTRIBUTE){ - if(resourceId==0){ - return "?null"; - } - atOrQues='?'; - }else { - return null; - } - EntryGroup value=null; - if(entryStore!=null){ - value=entryStore.getEntryGroup(resourceId); - } - if(value==null){ - return ValueCoder.decodeUnknownResourceId(valueType != ValueType.ATTRIBUTE, resourceId); - } - String referredPackageName=getPackageName(value); - String typeName=value.getTypeName(); - String name=value.getSpecName(); - return buildReference(currentPackageName, referredPackageName, atOrQues, typeName, name); - } - private static String getPackageName(EntryStore entryStore, int packageOrResourceId){ - if(entryStore==null || packageOrResourceId==0){ - return null; - } - int pkgId=(packageOrResourceId>>24)&0xFF; - if(pkgId==0){ - pkgId=packageOrResourceId; - } - pkgId = pkgId & 0xff; - Collection allPkg = entryStore.getPackageBlocks(pkgId); - if(allPkg==null){ - return null; - } - for(PackageBlock packageBlock:allPkg){ - String name=packageBlock.getName(); - if(name!=null){ - return name; - } - } - return null; - } - private static String getPackageName(EntryGroup entryGroup){ - if(entryGroup==null){ - return null; - } - return getPackageName(entryGroup.pickOne()); - } - private static String getPackageName(Entry entry){ - if(entry ==null){ - return null; - } - PackageBlock packageBlock= entry.getPackageBlock(); - if(packageBlock==null){ - return null; - } - return packageBlock.getName(); - } - private static EntryGroup searchEntryGroup(EntryStore store, PackageBlock packageBlock, int resourceId){ - if(packageBlock!=null){ - TableBlock tableBlock=packageBlock.getTableBlock(); - if(tableBlock!=null){ - for(PackageBlock pkg:tableBlock.listPackages()){ - EntryGroup entryGroup=pkg.getEntryGroup(resourceId); - if(entryGroup!=null){ - return entryGroup; - } - } - } - } - if(store!=null){ - return store.getEntryGroup(resourceId); - } - return null; - } - private static String decodeIntEntryString(PackageBlock packageBlock, int data){ - if(packageBlock==null){ - return null; - } - TableBlock tableBlock=packageBlock.getTableBlock(); - if(tableBlock==null){ - return null; - } - TableStringPool pool = tableBlock.getTableStringPool(); - TableString tableString=pool.get(data); - if(tableString==null){ - return null; - } - return escapeSpecialCharacter(tableString.getHtml()); - } - private static AttributeBag getAttributeBag(EntryStore store, int resourceId){ - ResTableMapEntry mapEntry=getAttributeValueBag(store, resourceId); - if(mapEntry==null){ - return null; - } - return AttributeBag.create(mapEntry.getValue()); - } - private static ResTableMapEntry getAttributeValueBag(EntryStore store, int resourceId){ - if(store==null){ - return null; - } - Collection foundGroups = store.getEntryGroups(resourceId); - ResTableMapEntry best=null; - for(EntryGroup group:foundGroups){ - ResTableMapEntry valueBag = getAttributeValueBag(group); - best=chooseBest(best, valueBag); - } - return best; - } - private static ResTableMapEntry getAttributeValueBag(EntryGroup entryGroup){ - if(entryGroup==null){ - return null; - } - ResTableMapEntry best=null; - Iterator iterator=entryGroup.iterator(true); - while (iterator.hasNext()){ - Entry entry =iterator.next(); - ResTableMapEntry valueBag = getAttributeValueBag(entry); - best=chooseBest(best, valueBag); - } - return best; - } - private static ResTableMapEntry getAttributeValueBag(Entry entry){ - if(entry ==null){ - return null; - } - TableEntry tableEntry = entry.getTableEntry(); - if(tableEntry instanceof ResTableMapEntry){ - return (ResTableMapEntry) tableEntry; - } - return null; - } - private static ResTableMapEntry chooseBest(ResTableMapEntry entry1, ResTableMapEntry entry2){ - if(entry1==null){ - return entry2; - } - if(entry2==null){ - return entry1; - } - if(entry2.getValue().childesCount()>entry1.getValue().childesCount()){ - return entry2; - } - return entry1; - } - private static boolean isEqualString(String str1, String str2){ - if(isEmpty(str1)){ - return isEmpty(str2); - } - return str1.equals(str2); - } - private static boolean isEmpty(String str){ - if(str==null){ - return true; - } - str=str.trim(); - return str.length()==0; - } -} diff --git a/src/main/java/com/reandroid/arsc/coder/XmlSanitizer.java b/src/main/java/com/reandroid/arsc/coder/XmlSanitizer.java new file mode 100755 index 00000000..61d0ad0c --- /dev/null +++ b/src/main/java/com/reandroid/arsc/coder/XmlSanitizer.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.coder; + +public class XmlSanitizer { + public static String escapeSpecialCharacter(String text){ + if(text==null || text.length()==0){ + return text; + } + if(isSpecialCharacter(text.charAt(0))){ + return '\\' +text; + } + return text; + } + public static String unEscapeUnQuote(String text){ + if(text==null || text.length()<2){ + return text; + } + char first = text.charAt(0); + if(first == '"'){ + return unQuoteWhitespace(text); + } + if(first != '\\' || !isSpecialCharacter(text.charAt(1))){ + return text; + } + return text.substring(1); + } + public static String quoteWhitespace(String text){ + if(!isWhiteSpace(text)){ + return text; + } + return "\"" + text + "\""; + } + public static String unQuoteWhitespace(String text){ + if(text == null || text.length() < 3){ + return text; + } + if(text.charAt(0) != '"' || text.charAt(text.length()-1) != '"'){ + return text; + } + String unQuoted = text.substring(1, text.length()-1); + if(!isWhiteSpace(unQuoted)){ + return text; + } + return unQuoted; + } + private static boolean isWhiteSpace(String text){ + if(text == null || text.length() == 0){ + return false; + } + char[] chars = text.toCharArray(); + for(int i = 0; i < chars.length; i++){ + if(!isWhiteSpace(chars[i])){ + return false; + } + } + return true; + } + private static boolean isWhiteSpace(char ch){ + switch (ch){ + case ' ': + case '\n': + case '\r': + case '\t': + return true; + default: + return false; + } + } + private static boolean isSpecialCharacter(char ch){ + switch (ch){ + case '@': + case '?': + case '#': + return true; + default: + return false; + } + } +} diff --git a/src/main/java/com/reandroid/arsc/container/BlockList.java b/src/main/java/com/reandroid/arsc/container/BlockList.java index 3370ad23..11453490 100755 --- a/src/main/java/com/reandroid/arsc/container/BlockList.java +++ b/src/main/java/com/reandroid/arsc/container/BlockList.java @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2022 github.com/REAndroid * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,12 +19,15 @@ import com.reandroid.arsc.base.BlockContainer; import com.reandroid.arsc.base.BlockCounter; import com.reandroid.arsc.io.BlockReader; +import com.reandroid.arsc.util.FilterIterator; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Comparator; +import java.util.Iterator; import java.util.List; +import java.util.function.Predicate; public class BlockList extends Block { private final List mItems; @@ -32,6 +35,12 @@ public BlockList(){ super(); mItems=new ArrayList<>(); } + public Iterator iterator(){ + return mItems.iterator(); + } + public Iterator iterator(Predicate filter){ + return new FilterIterator<>(mItems.iterator(), filter); + } public void clearChildes(){ ArrayList childList = new ArrayList<>(getChildes()); for(T child:childList){ diff --git a/src/main/java/com/reandroid/arsc/container/SpecTypePair.java b/src/main/java/com/reandroid/arsc/container/SpecTypePair.java index 3612d3df..448d6ad2 100755 --- a/src/main/java/com/reandroid/arsc/container/SpecTypePair.java +++ b/src/main/java/com/reandroid/arsc/container/SpecTypePair.java @@ -15,13 +15,11 @@ */ package com.reandroid.arsc.container; -import com.reandroid.arsc.array.EntryArray; import com.reandroid.arsc.chunk.*; import com.reandroid.arsc.array.TypeBlockArray; import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.BlockContainer; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.group.ResourceEntry; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.TypeHeader; import com.reandroid.arsc.io.BlockReader; @@ -39,6 +37,7 @@ import java.io.IOException; import java.util.*; +import java.util.function.Predicate; public class SpecTypePair extends BlockContainer implements JSONConvert, Comparable{ @@ -91,6 +90,31 @@ public Iterator getResources(){ if(packageBlock == null){ return EmptyIterator.of(); } + final int end = getHighestEntryId(); + final int id = (packageBlock.getId() << 24) | (getId() << 16); + return new Iterator() { + private int mIndex; + @Override + public boolean hasNext() { + return mIndex <= end; + } + @Override + public ResourceEntry next() { + if(mIndex > end){ + throw new NoSuchElementException(); + } + int resourceId = id | mIndex; + mIndex++; + return new ResourceEntry(packageBlock, resourceId); + } + }; + } + public Iterator getResourcesOld(){ + final PackageBlock packageBlock = getPackageBlock(); + if(packageBlock == null){ + return EmptyIterator.of(); + } + return new ComputeIterator(listDefaultEntries()) { @Override public ResourceEntry apply(Entry element) { @@ -99,7 +123,7 @@ public ResourceEntry apply(Entry element) { }; } private Iterator listDefaultEntries(){ - Iterator iterator = getTypeBlockArray().iterator(true); + Iterator iterator = getTypeBlockArray().iterator(false); if(!iterator.hasNext()){ return EmptyIterator.of(); } @@ -120,6 +144,15 @@ public Entry apply(TypeBlock element) { } }; } + public Iterator getTypeBlocks(){ + return getTypeBlockArray().iterator(); + } + public Iterator getTypeBlocks(Predicate filter){ + return getTypeBlockArray().iterator(filter); + } + public Iterator getTypeBlocks(ResConfig resConfig){ + return getTypeBlockArray().iterator(resConfig); + } public Boolean hasComplexEntry(){ return getTypeBlockArray().hasComplexEntry(); } @@ -133,57 +166,6 @@ public void linkSpecStringsInternal(SpecStringPool specStringPool){ typeBlock.linkSpecStringsInternal(specStringPool); } } - public Map createEntryGroups(){ - return createEntryGroups(false); - } - public Map createEntryGroups(boolean skipNullEntries){ - Map map = new LinkedHashMap<>(); - for(TypeBlock typeBlock : listTypeBlocks()){ - EntryArray entryArray = typeBlock.getEntryArray(); - for(Entry entry : entryArray.listItems(skipNullEntries)){ - if(entry == null){ - continue; - } - int id = entry.getResourceId(); - EntryGroup entryGroup = map.get(id); - if(entryGroup == null){ - entryGroup = new EntryGroup(id); - map.put(id, entryGroup); - } - entryGroup.add(entry); - } - } - return map; - } - public EntryGroup createEntryGroup(int id){ - id = 0xffff & id; - EntryGroup entryGroup = null; - for(TypeBlock typeBlock:listTypeBlocks()){ - Entry entry = typeBlock.getEntry(id); - if(entry == null){ - continue; - } - if(entryGroup == null){ - entryGroup = new EntryGroup(entry.getResourceId()); - } - entryGroup.add(entry); - } - return entryGroup; - } - public EntryGroup getEntryGroup(String entryName){ - EntryGroup entryGroup = null; - for(TypeBlock typeBlock:listTypeBlocks()){ - Entry entry = typeBlock.getEntry(entryName); - if(entry == null){ - continue; - } - if(entryGroup == null){ - entryGroup = new EntryGroup(entry.getResourceId()); - } - entryGroup.add(entry); - } - return entryGroup; - } public void destroy(){ getSpecBlock().destroy(); getTypeBlockArray().destroy(); @@ -268,7 +250,7 @@ public TypeBlock getTypeBlock(String qualifiers){ public TypeBlock getTypeBlock(ResConfig resConfig){ return getTypeBlockArray().getTypeBlock(resConfig); } - public List listResConfig(){ + public Set listResConfig(){ return mTypeBlockArray.listResConfig(); } diff --git a/src/main/java/com/reandroid/arsc/group/EntryGroup.java b/src/main/java/com/reandroid/arsc/group/EntryGroup.java deleted file mode 100755 index 96b2b0cc..00000000 --- a/src/main/java/com/reandroid/arsc/group/EntryGroup.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.group; - -import com.reandroid.arsc.base.BlockArrayCreator; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.TypeBlock; -import com.reandroid.arsc.item.SpecString; -import com.reandroid.arsc.item.TypeString; -import com.reandroid.arsc.pool.SpecStringPool; -import com.reandroid.arsc.util.HexUtil; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResConfig; - -import java.util.Iterator; - -public class EntryGroup extends ItemGroup { - private final int resourceId; - public EntryGroup(int resId) { - super(ARRAY_CREATOR, HexUtil.toHex8(resId)); - this.resourceId=resId; - } - public Entry getEntry(ResConfig resConfig){ - Entry[] items = getItems(); - if(items == null || resConfig == null){ - return null; - } - int length = items.length; - for(int i=0; i iterator = iterator(); - int firstSpec = -2; - while (iterator.hasNext()){ - Entry entry = iterator.next(); - if(firstSpec == -2){ - firstSpec = entry.getSpecReference(); - continue; - } - if(firstSpec != entry.getSpecReference()){ - return false; - } - } - return true; - } - public boolean renameSpec(int specReference){ - Entry[] items=getItems(); - if(items==null){ - return false; - } - boolean renameOk=false; - for(Entry block:items){ - if(block==null){ - continue; - } - if(block.getSpecReference()==specReference){ - continue; - } - block.setSpecReference(specReference); - renameOk=true; - } - return renameOk; - } - public Entry pickOne(){ - Entry[] items=getItems(); - if(items==null){ - return null; - } - Entry result = null; - for(Entry entry :items){ - if(entry == null){ - continue; - } - if(entry.isDefault() && !entry.isNull()){ - return entry; - } - if(result==null || result.isNull()){ - result = entry; - continue; - } - result = choose(result, entry); - } - return result; - } - private Entry choose(Entry entry1, Entry entry2){ - if(entry1 == null || entry1 == entry2){ - return entry2; - } - if(entry2 == null){ - return entry1; - } - if(entry1.isNull() && !entry2.isNull()){ - return entry2; - } - if(!entry1.isNull() && entry2.isNull()){ - return entry1; - } - if(entry1.isDefault()){ - return entry1; - } - if(entry2.isDefault()){ - return entry2; - } - ResConfig config1 = entry1.getResConfig(); - ResConfig config2 = entry2.getResConfig(); - if(config1 == null && config2==null){ - return entry1; - } - if(config1 == null){ - return entry2; - } - if(config2 == null){ - return entry1; - } - boolean lang1 = isDefaultLanguage(config1); - boolean lang2 = isDefaultLanguage(config2); - if((lang1 && !lang2) || (!lang1 && !lang2)){ - return entry1; - } - if(!lang1){ - return entry2; - } - String region1 = config1.getRegion(); - String region2 = config2.getRegion(); - if((region1!=null && region2==null) || (region1==null && region2==null)){ - return entry1; - } - if(region1==null){ - return entry2; - } - return entry1; - } - private boolean isDefaultLanguage(ResConfig resConfig){ - String lang = resConfig.getLanguage(); - if(lang == null){ - return true; - } - return "en".equals(lang); - } - public Entry getDefault(){ - return getDefault(true); - } - public Entry getDefault(boolean skipNull){ - Iterator itr=iterator(skipNull); - while (itr.hasNext()){ - Entry entry =itr.next(); - if(entry.isDefault()){ - return entry; - } - } - return null; - } - public TypeString getTypeString(){ - Entry entry =pickOne(); - if(entry !=null){ - return entry.getTypeString(); - } - return null; - } - public SpecString getSpecString(){ - Entry entry =pickOne(); - if(entry !=null){ - return entry.getSpecString(); - } - return null; - } - public String getTypeName(){ - TypeString typeString=getTypeString(); - if(typeString==null){ - return null; - } - return typeString.get(); - } - public String getSpecName(){ - SpecString specString=getSpecString(); - if(specString==null){ - return null; - } - return specString.get(); - } - private SpecStringPool getSpecStringPool(){ - Entry entry =get(0); - if(entry ==null){ - return null; - } - TypeBlock typeBlock= entry.getTypeBlock(); - if(typeBlock==null){ - return null; - } - PackageBlock packageBlock=typeBlock.getPackageBlock(); - if(packageBlock==null){ - return null; - } - return packageBlock.getSpecStringPool(); - } - @Override - public int hashCode(){ - return resourceId; - } - @Override - public String toString(){ - Entry entry =pickOne(); - if(entry ==null){ - return super.toString(); - } - return super.toString()+"{"+ entry.toString()+"}"; - } - - private static final BlockArrayCreator ARRAY_CREATOR = new BlockArrayCreator(){ - @Override - public Entry newInstance() { - return new Entry(); - } - - @Override - public Entry[] newInstance(int len) { - return new Entry[len]; - } - }; - -} diff --git a/src/main/java/com/reandroid/arsc/group/ResourceEntry.java b/src/main/java/com/reandroid/arsc/model/ResourceEntry.java similarity index 73% rename from src/main/java/com/reandroid/arsc/group/ResourceEntry.java rename to src/main/java/com/reandroid/arsc/model/ResourceEntry.java index 0d9ec255..051552da 100644 --- a/src/main/java/com/reandroid/arsc/group/ResourceEntry.java +++ b/src/main/java/com/reandroid/arsc/model/ResourceEntry.java @@ -13,20 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.group; +package com.reandroid.arsc.model; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.item.SpecString; +import com.reandroid.arsc.util.ComputeIterator; +import com.reandroid.arsc.util.FilterIterator; import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.ResConfig; +import com.reandroid.arsc.value.ValueType; +import com.reandroid.arsc.value.attribute.AttributeBag; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Iterator; import java.util.function.Predicate; -public class ResourceEntry implements Iterable, Predicate { +public class ResourceEntry implements Iterable{ private final int resourceId; private final PackageBlock packageBlock; @@ -97,11 +101,17 @@ public Entry any(){ public boolean isEmpty() { return !iterator(true).hasNext(); } + public boolean isDeclared() { + return getName() != null; + } + public PackageBlock getPackageBlock(){ + return packageBlock; + } public int getResourceId() { return resourceId; } - public String getPackage(){ - return packageBlock.getPackageBlock().getName(); + public String getPackageName(){ + return getPackageBlock().getName(); } public String getType(){ return packageBlock.typeNameOf((getResourceId() >> 16) & 0xff); @@ -143,25 +153,53 @@ public String getName(){ public Iterator iterator(){ return iterator(true); } - @Override - public boolean test(Entry entry) { - return entry != null && !entry.isNull(); - } public Iterator iterator(boolean skipNull){ return packageBlock.getEntries(getResourceId(), skipNull); } + public Iterator iterator(Predicate filter){ + return new FilterIterator<>(packageBlock.getEntries(getResourceId()), filter); + } + public Iterator getConfigs(){ + return new ComputeIterator(iterator(false)) { + @Override + public ResConfig apply(Entry element) { + return element.getResConfig(); + } + }; + } public String getHexId(){ return HexUtil.toHex8(getResourceId()); } - public int count(){ - int result = 0; - Iterator itr = iterator(true); - while (itr.hasNext()){ - if(!itr.next().isNull()){ - result ++; - } + + public String buildReference(PackageBlock context, ValueType referenceType){ + if(!referenceType.isReference()){ + throw new IllegalArgumentException("Not reference: " + referenceType); } - return result; + StringBuilder builder = new StringBuilder(); + if(referenceType == ValueType.REFERENCE){ + builder.append('@'); + }else { + builder.append('?'); + } + if(context != getPackageBlock()){ + builder.append(getPackageName()); + builder.append(':'); + } + builder.append(getType()); + builder.append('/'); + builder.append(getName()); + return builder.toString(); + } + public String decodeAttributeData(int data){ + Entry entry = get(); + if(entry == null){ + return null; + } + AttributeBag attributeBag = AttributeBag.create(entry.getResValueMapArray()); + if(attributeBag != null){ + return attributeBag.decodeAttributeValue(data); + } + return null; } public boolean serializePublicXml(XmlSerializer serializer) throws IOException { @@ -195,7 +233,7 @@ public boolean equals(Object obj) { @Override public String toString(){ - return getHexId() + " @" + getPackage() + return getHexId() + " @" + getPackageName() + ":" + getType() + "/" + getName(); } diff --git a/src/main/java/com/reandroid/arsc/model/ResourceLibrary.java b/src/main/java/com/reandroid/arsc/model/ResourceLibrary.java new file mode 100644 index 00000000..1fe77f99 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/model/ResourceLibrary.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.model; + +import com.reandroid.common.Namespace; + +public interface ResourceLibrary extends Namespace { + int getId(); + String getName(); + boolean packageNameMatches(String packageName); + + static String toPrefix(String name){ + if(name == null){ + return null; + } + int i = name.lastIndexOf('.'); + if(i > 0){ + name = name.substring(i + 1); + } + return name; + } + static boolean packageNameMatches(ResourceLibrary lib, String packageName){ + if(packageName == null){ + return false; + } + if(PREFIX_ANDROID.equals(lib.getName())){ + return PREFIX_ANDROID.equals(packageName); + } + return packageName.equals(lib.getName()) + || packageName.equals(lib.getPrefix()); + } + + String URI_ANDROID = "http://schemas.android.com/apk/res/android"; + String URI_RES_AUTO = "http://schemas.android.com/apk/res-auto"; + String PREFIX_ANDROID = "android"; + String PREFIX_APP = "app"; + +} diff --git a/src/main/java/com/reandroid/arsc/pool/TypeStringPool.java b/src/main/java/com/reandroid/arsc/pool/TypeStringPool.java index 67d226ef..7527363c 100755 --- a/src/main/java/com/reandroid/arsc/pool/TypeStringPool.java +++ b/src/main/java/com/reandroid/arsc/pool/TypeStringPool.java @@ -43,7 +43,7 @@ public int idOf(String typeName){ **/ public int idOf(TypeString typeString){ if(typeString == null){ - return toTypeId(0); + return 0; } return (toTypeId(typeString.getIndex())); } diff --git a/src/main/java/com/reandroid/arsc/util/FilterIterator.java b/src/main/java/com/reandroid/arsc/util/FilterIterator.java index 5207b24b..5de57704 100644 --- a/src/main/java/com/reandroid/arsc/util/FilterIterator.java +++ b/src/main/java/com/reandroid/arsc/util/FilterIterator.java @@ -68,4 +68,29 @@ private boolean testAll(T item){ return mSecondTester == null || mSecondTester.test(item); } + + public static final class Except extends FilterIterator{ + private final T1 excludeItem; + private final boolean useEquals; + + public Except(Iterator iterator, T1 excludeItem, boolean useEquals) { + super(iterator); + this.excludeItem = excludeItem; + this.useEquals = useEquals; + } + public Except(Iterator iterator, T1 excludeItem) { + this(iterator, excludeItem, false); + } + + @Override + public boolean test(T1 item){ + if(item == null || item == excludeItem){ + return false; + } + if(!useEquals){ + return true; + } + return item.equals(excludeItem); + } + } } diff --git a/src/main/java/com/reandroid/arsc/util/FrameworkTable.java b/src/main/java/com/reandroid/arsc/util/FrameworkTable.java index b2e048ee..32c5c678 100755 --- a/src/main/java/com/reandroid/arsc/util/FrameworkTable.java +++ b/src/main/java/com/reandroid/arsc/util/FrameworkTable.java @@ -22,7 +22,7 @@ import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.group.ResourceEntry; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.item.ReferenceItem; import com.reandroid.arsc.item.TableString; diff --git a/src/main/java/com/reandroid/arsc/util/IOUtil.java b/src/main/java/com/reandroid/arsc/util/IOUtil.java index 8b49b416..2a1ebc9b 100644 --- a/src/main/java/com/reandroid/arsc/util/IOUtil.java +++ b/src/main/java/com/reandroid/arsc/util/IOUtil.java @@ -16,9 +16,26 @@ package com.reandroid.arsc.util; import java.io.Closeable; +import java.io.File; import java.io.IOException; public class IOUtil { + public static String shortPath(File file, int depth){ + File tmp = file; + while (depth > 0){ + File dir = tmp.getParentFile(); + if(dir == null){ + break; + } + tmp = dir; + depth --; + } + if(file == tmp){ + return file.getName(); + } + int i = tmp.getAbsolutePath().length() + 1; + return file.getAbsolutePath().substring(i); + } public static void close(Object obj) throws IOException { if(obj instanceof Closeable){ ((Closeable)obj).close(); diff --git a/src/main/java/com/reandroid/apk/ApkJsonDecoder.java b/src/main/java/com/reandroid/arsc/util/SingleIterator.java similarity index 52% rename from src/main/java/com/reandroid/apk/ApkJsonDecoder.java rename to src/main/java/com/reandroid/arsc/util/SingleIterator.java index 3c5df32c..1559675f 100644 --- a/src/main/java/com/reandroid/apk/ApkJsonDecoder.java +++ b/src/main/java/com/reandroid/arsc/util/SingleIterator.java @@ -13,18 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.apk; +package com.reandroid.arsc.util; -/** - * Renamed, use com.reandroid.apk.ApkModuleJsonDecoder - * */ -@Deprecated -public class ApkJsonDecoder extends ApkModuleJsonDecoder{ - public ApkJsonDecoder(ApkModule apkModule, boolean splitTypes){ - super(apkModule, splitTypes); +import java.util.Iterator; + +public class SingleIterator implements Iterator { + private T mItem; + public SingleIterator(T item){ + this.mItem = item; + } + @Override + public boolean hasNext() { + return this.mItem != null; } - public ApkJsonDecoder(ApkModule apkModule){ - super(apkModule); + @Override + public T next() { + T item = this.mItem; + this.mItem = null; + return item; } + public static Iterator of(T1 item){ + if(item == null){ + return EmptyIterator.of(); + } + return new SingleIterator<>(item); + } } diff --git a/src/main/java/com/reandroid/arsc/util/StringsUtil.java b/src/main/java/com/reandroid/arsc/util/StringsUtil.java index 5d08aeef..01dbbccc 100644 --- a/src/main/java/com/reandroid/arsc/util/StringsUtil.java +++ b/src/main/java/com/reandroid/arsc/util/StringsUtil.java @@ -20,6 +20,33 @@ public class StringsUtil { + public static String toUpperCase(String str){ + if(str == null || str.length() == 0){ + return str; + } + char[] chars = str.toCharArray(); + boolean changed = false; + for(int i = 0; i < chars.length; i++){ + char ch = chars[i]; + char lower = toUpperCase(ch); + if(ch == lower){ + continue; + } + chars[i] = lower; + changed = true; + } + if(!changed){ + return str; + } + return new String(chars); + } + public static char toUpperCase(char ch){ + if(ch > 'z' || ch < 'a'){ + return ch; + } + int i = ch - 'a'; + return (char) (i + 'A'); + } public static String toLowercase(String str){ char[] chars = str.toCharArray(); boolean changed = false; diff --git a/src/main/java/com/reandroid/arsc/value/AttributeType.java b/src/main/java/com/reandroid/arsc/value/AttributeType.java index 6682ab8e..99ef408d 100644 --- a/src/main/java/com/reandroid/arsc/value/AttributeType.java +++ b/src/main/java/com/reandroid/arsc/value/AttributeType.java @@ -52,6 +52,9 @@ public String toString(){ } public static AttributeType valueOf(int value){ + if(value == 0){ + return null; + } for(AttributeType type:VALUES){ if(type.getId() == value){ return type; diff --git a/src/main/java/com/reandroid/arsc/value/AttributeValue.java b/src/main/java/com/reandroid/arsc/value/AttributeValue.java index 751ef9e8..be41d6fa 100644 --- a/src/main/java/com/reandroid/arsc/value/AttributeValue.java +++ b/src/main/java/com/reandroid/arsc/value/AttributeValue.java @@ -15,8 +15,45 @@ */ package com.reandroid.arsc.value; -public interface AttributeValue extends Value{ - int getNameResourceID(); - void setNameResourceID(int resourceId); - Entry resolveName(); +import com.reandroid.arsc.model.ResourceEntry; + +public abstract class AttributeValue extends ValueItem{ + public AttributeValue(int bytesLength, int sizeOffset) { + super(bytesLength, sizeOffset); + } + public abstract int getNameResourceID(); + public abstract void setNameResourceID(int resourceId); + public abstract String decodePrefix(); + public abstract String decodeName(boolean includePrefix); + + public String decodeName(){ + return decodeName(true); + } + public ResourceEntry resolveName(){ + return resolve(getNameResourceID()); + } + @Override + public String decodeValue(){ + if(AttributeDataFormat.INTEGER.contains(getValueType())){ + String value = decodeDataAsAttrFormats(); + if(value == null){ + value = decodeDataAsAttr(); + } + if(value != null){ + return value; + } + } + return super.decodeValue(); + } + private String decodeDataAsAttr(){ + ResourceEntry attr = resolveName(); + if(attr != null){ + return attr.decodeAttributeData(getData()); + } + return null; + } + String decodeDataAsAttrFormats(){ + return null; + } + } diff --git a/src/main/java/com/reandroid/arsc/value/CompoundEntry.java b/src/main/java/com/reandroid/arsc/value/CompoundEntry.java index 7ff54f1b..cadc93a5 100644 --- a/src/main/java/com/reandroid/arsc/value/CompoundEntry.java +++ b/src/main/java/com/reandroid/arsc/value/CompoundEntry.java @@ -19,10 +19,19 @@ import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.json.JSONObject; -public abstract class CompoundEntry> extends TableEntry { +import java.util.Iterator; + +public abstract class CompoundEntry> + extends TableEntry implements Iterable{ public CompoundEntry(ARRAY mapArray){ super(new EntryHeaderMap(), mapArray); } + + @Override + public Iterator iterator(){ + return getValue().iterator(); + } + // Valid for type attr public AttributeDataFormat[] getAttributeTypeFormats(){ ITEM item = getByType(AttributeType.FORMATS); diff --git a/src/main/java/com/reandroid/arsc/value/Entry.java b/src/main/java/com/reandroid/arsc/value/Entry.java index a9204a47..2b87eb9d 100755 --- a/src/main/java/com/reandroid/arsc/value/Entry.java +++ b/src/main/java/com/reandroid/arsc/value/Entry.java @@ -21,10 +21,8 @@ import com.reandroid.arsc.base.BlockCounter; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.SpecBlock; -import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.*; import com.reandroid.arsc.pool.SpecStringPool; @@ -33,11 +31,9 @@ import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONObject; - import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; -import java.util.NoSuchElementException; public class Entry extends Block implements JSONConvert { private TableEntry mTableEntry; @@ -64,9 +60,16 @@ public ResValue getResValue(){ return null; } public ResValueMapArray getResValueMapArray(){ + ResTableMapEntry resTableMapEntry = getResTableMapEntry(); + if(resTableMapEntry != null){ + return resTableMapEntry.getValue(); + } + return null; + } + public ResTableMapEntry getResTableMapEntry(){ TableEntry tableEntry = getTableEntry(); if(tableEntry instanceof ResTableMapEntry){ - return ((ResTableMapEntry)tableEntry).getValue(); + return ((ResTableMapEntry)tableEntry); } return null; } @@ -223,7 +226,6 @@ public void updateSpecReference(SpecString specString){ if(tableEntry != null){ tableEntry.getHeader().setKey(specString); unlinkNullSpecString(); - return; }else if(mNullSpecReference != null){ linkNullSpecString(specString); }else if(specString == null){ @@ -302,24 +304,6 @@ private SpecStringPool getSpecStringPool(){ } return null; } - private Entry searchEntry(int resourceId){ - if(resourceId==getResourceId()){ - return this; - } - PackageBlock packageBlock= getPackageBlock(); - if(packageBlock==null){ - return null; - } - TableBlock tableBlock = packageBlock.getTableBlock(); - if(tableBlock==null){ - return null; - } - EntryGroup entryGroup = tableBlock.search(resourceId); - if(entryGroup!=null){ - return entryGroup.pickOne(); - } - return null; - } public ResValue setValueAsRaw(ValueType valueType, int data){ TableEntry tableEntry = ensureTableEntry(false); ResValue resValue = (ResValue) tableEntry.getValue(); @@ -474,7 +458,6 @@ public void setTableEntry(TableEntry tableEntry){ tableEntry.setParent(this); this.mTableEntry = tableEntry; transferSpecReference(tableEntry); - onTableEntryAdded(); } private void transferSpecReference(TableEntry tableEntry){ IntegerItem nullSpecReference = this.mNullSpecReference; @@ -488,21 +471,11 @@ private void transferSpecReference(TableEntry tableEntry){ valueHeader.setKey(ref); } } - private void onTableEntryAdded(){ - PackageBlock packageBlock = getPackageBlock(); - if(packageBlock!=null){ - packageBlock.onEntryAdded(this); - } - } private void onTableEntryRemoved(){ TableEntry exist = this.mTableEntry; if(exist == null){ return; } - PackageBlock packageBlock = getPackageBlock(); - if(packageBlock!=null){ - packageBlock.removeEntryGroup(this); - } exist.onRemoved(); exist.setIndex(-1); exist.setParent(null); @@ -559,45 +532,6 @@ private boolean shouldMerge(Entry coming){ } return getTableEntry().shouldMerge(coming.getTableEntry()); } - - public String buildResourceName(int resourceId, char prefix, boolean includeType){ - if(resourceId==0){ - return null; - } - Entry entry=searchEntry(resourceId); - return buildResourceName(entry, prefix, includeType); - } - public String buildResourceName(Entry entry, char prefix, boolean includeType){ - if(entry==null){ - return null; - } - String pkgName=entry.getPackageName(); - if(getResourceId()==entry.getResourceId()){ - pkgName=null; - }else if(pkgName!=null){ - if(pkgName.equals(this.getPackageName())){ - pkgName=null; - } - } - String type=null; - if(includeType){ - type=entry.getTypeName(); - } - String name=entry.getName(); - return buildResourceName(prefix, pkgName, type, name); - } - public String getResourceName(){ - return buildResourceName('@',null, getTypeName(), getName()); - } - public String getResourceName(char prefix){ - return getResourceName(prefix, false, true); - } - public String getResourceName(char prefix, boolean includePackage, boolean includeType){ - String pkg=includePackage?getPackageName():null; - String type=includeType?getTypeName():null; - return buildResourceName(prefix,pkg, type, getName()); - } - @Override public String toString(){ StringBuilder builder = new StringBuilder(); @@ -622,26 +556,5 @@ public String toString(){ builder.append(getName()); return builder.toString(); } - - public static String buildResourceName(char prefix, String packageName, String type, String name){ - if(name==null){ - return null; - } - StringBuilder builder=new StringBuilder(); - if(prefix!=0){ - builder.append(prefix); - } - if(packageName!=null){ - builder.append(packageName); - builder.append(':'); - } - if(type!=null){ - builder.append(type); - builder.append('/'); - } - builder.append(name); - return builder.toString(); - } - public static final String NAME_id = "id"; } diff --git a/src/main/java/com/reandroid/arsc/value/LibraryInfo.java b/src/main/java/com/reandroid/arsc/value/LibraryInfo.java index 92bacd04..ebba9789 100755 --- a/src/main/java/com/reandroid/arsc/value/LibraryInfo.java +++ b/src/main/java/com/reandroid/arsc/value/LibraryInfo.java @@ -17,6 +17,7 @@ import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.BlockCounter; +import com.reandroid.arsc.model.ResourceLibrary; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.FixedLengthString; import com.reandroid.arsc.item.IntegerItem; @@ -27,7 +28,7 @@ import java.io.IOException; import java.io.OutputStream; -public class LibraryInfo extends Block implements JSONConvert { +public class LibraryInfo extends Block implements JSONConvert, ResourceLibrary { private final IntegerItem mPackageId; private final FixedLengthString mPackageName; @@ -41,18 +42,35 @@ public LibraryInfo(){ mPackageName.setParent(this); } - public int getPackageId(){ + @Override + public int getId(){ return mPackageId.get(); } - public void setPackageId(int id){ + public void setId(int id){ mPackageId.set(id); } - public String getPackageName(){ + @Override + public String getName(){ return mPackageName.get(); } - public void setPackageName(String packageName){ + public void setName(String packageName){ mPackageName.set(packageName); } + @Override + public String getPrefix(){ + return ResourceLibrary.toPrefix(getName()); + } + @Override + public String getUri(){ + if(getId() == 0x01 && ResourceLibrary.PREFIX_ANDROID.equals(getName())){ + return ResourceLibrary.URI_ANDROID; + } + return ResourceLibrary.URI_RES_AUTO; + } + @Override + public boolean packageNameMatches(String packageName) { + return ResourceLibrary.packageNameMatches(this, packageName); + } @Override public byte[] getBytes() { @@ -95,32 +113,32 @@ public void onReadBytes(BlockReader reader) throws IOException{ @Override public JSONObject toJson() { JSONObject jsonObject=new JSONObject(); - jsonObject.put("id", getPackageId()); - jsonObject.put("name", getPackageName()); + jsonObject.put("id", getId()); + jsonObject.put("name", getName()); return jsonObject; } @Override public void fromJson(JSONObject json) { - setPackageId(json.getInt("id")); - setPackageName(json.getString("name")); + setId(json.getInt("id")); + setName(json.getString("name")); } public void merge(LibraryInfo info){ if(info==null||info==this){ return; } - if(getPackageId()!=info.getPackageId()){ + if(getId()!=info.getId()){ throw new IllegalArgumentException("Can not add different id libraries: " - +getPackageId()+"!="+info.getPackageId()); + + getId()+"!="+info.getId()); } - setPackageName(info.getPackageName()); + setName(info.getName()); } @Override public String toString(){ StringBuilder builder=new StringBuilder(); builder.append("LIBRARY{"); - builder.append(HexUtil.toHex2((byte) getPackageId())); + builder.append(HexUtil.toHex2((byte) getId())); builder.append(':'); - String name=getPackageName(); + String name= getName(); if(name==null){ name="NULL"; } diff --git a/src/main/java/com/reandroid/arsc/value/ResTableMapEntry.java b/src/main/java/com/reandroid/arsc/value/ResTableMapEntry.java index ef3969a4..525f2866 100644 --- a/src/main/java/com/reandroid/arsc/value/ResTableMapEntry.java +++ b/src/main/java/com/reandroid/arsc/value/ResTableMapEntry.java @@ -16,11 +16,92 @@ package com.reandroid.arsc.value; import com.reandroid.arsc.array.ResValueMapArray; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.model.ResourceEntry; public class ResTableMapEntry extends CompoundEntry { public ResTableMapEntry(){ super(new ResValueMapArray()); } + + public String decodeParentId(){ + ResourceEntry resourceEntry = resolveParentId(); + if(resourceEntry == null){ + return null; + } + PackageBlock packageBlock = getPackageBlock(); + if(packageBlock == null){ + return null; + } + return resourceEntry.buildReference(packageBlock, ValueType.REFERENCE); + } + public ResourceEntry resolveParentId(){ + int id = getParentId(); + if(id == 0){ + return null; + } + PackageBlock packageBlock = getPackageBlock(); + if(packageBlock == null){ + return null; + } + TableBlock tableBlock = packageBlock.getTableBlock(); + if(tableBlock == null){ + return null; + } + return tableBlock.getResource(packageBlock, id); + } + private PackageBlock getPackageBlock(){ + Entry entry = getParentEntry(); + if(entry != null){ + return entry.getPackageBlock(); + } + return null; + } + public boolean isAttr(){ + boolean hasFormats = false; + for(ResValueMap valueMap : this){ + AttributeType attributeType = valueMap.getAttributeType(); + if(attributeType == null || attributeType.isPlural()){ + return false; + } + if(attributeType == AttributeType.FORMATS){ + if(hasFormats){ + return false; + } + hasFormats = true; + } + } + return hasFormats; + } + public boolean isPlural(){ + for(ResValueMap valueMap : this){ + AttributeType attributeType = valueMap.getAttributeType(); + if(attributeType == null || !attributeType.isPlural()){ + return false; + } + } + return false; + } + public boolean isArray(){ + int size = getValue().childesCount(); + for(ResValueMap valueMap : this){ + int id = valueMap.getNameResourceID(); + if(id >= 0 && id <= size){ + continue; + } + return false; + } + if(size != 0){ + return true; + } + Entry entry = getParentEntry(); + if(entry == null){ + return false; + } + String type = entry.getTypeName(); + return type != null && type.contains("array"); + } @Override boolean shouldMerge(TableEntry tableEntry){ if(tableEntry == this || !(tableEntry instanceof ResTableMapEntry)){ diff --git a/src/main/java/com/reandroid/arsc/value/ResValueMap.java b/src/main/java/com/reandroid/arsc/value/ResValueMap.java index 1a58f95b..a3146a68 100755 --- a/src/main/java/com/reandroid/arsc/value/ResValueMap.java +++ b/src/main/java/com/reandroid/arsc/value/ResValueMap.java @@ -16,53 +16,54 @@ package com.reandroid.arsc.value; import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.ValueCoder; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.util.HexUtil; import com.reandroid.json.JSONObject; -public class ResValueMap extends ValueItem implements AttributeValue{ +public class ResValueMap extends AttributeValue{ public ResValueMap() { super(12, OFFSET_SIZE); } - - public String decodeData(){ - String value = decodeDataAsAttrFormats(); - if(value != null){ - return value; + @Override + public String decodeName(boolean includePrefix){ + int resourceId = getNameResourceID(); + if(!PackageBlock.isResourceId(resourceId)){ + AttributeType attributeType = getAttributeType(); + if(attributeType != null){ + return attributeType.getName(); + } + return null; } - ValueType valueType = getValueType(); - if(valueType == ValueType.STRING){ - return getValueAsString(); + ResourceEntry resourceEntry = resolve(resourceId); + if(resourceEntry == null || !resourceEntry.isDeclared()){ + return ValueCoder.decodeUnknownResourceId(false, resourceId); } - int data = getData(); - if(AttributeDataFormat.REFERENCE.contains(valueType)){ - Entry entry = resolve(data); - if(entry == null){ - return HexUtil.toHex8("@0x", data); + String name = resourceEntry.getName(); + if(includePrefix && resourceEntry.getPackageBlock() != getPackageBlock()){ + String prefix = resourceEntry.getPackageName(); + if(prefix != null){ + name = prefix + ":" + name; } - return buildReference(entry, valueType, true); } - return ValueDecoder.decode(valueType, data); + return name; } - private String decodeDataAsAttrFormats(){ + @Override + String decodeDataAsAttrFormats(){ AttributeType attributeType = getAttributeType(); if(attributeType != AttributeType.FORMATS){ return null; } return AttributeDataFormat.toString(AttributeDataFormat.decodeValueTypes(getData())); } - public String decodeName(){ - AttributeType attributeType = getAttributeType(); - if(attributeType != null){ - return attributeType.getName(); - } - Entry entry = resolveName(); - return buildReference(entry, null, false); - } @Override - public Entry resolveName(){ - return resolve(getNameResourceID()); + public String decodePrefix(){ + ResourceEntry resourceEntry = resolveName(); + if(resourceEntry == null || getPackageBlock() == resourceEntry.getPackageBlock()){ + return null; + } + return resourceEntry.getPackageName(); } public AttributeType getAttributeType(){ return AttributeType.valueOf(getNameResourceID()); @@ -106,6 +107,9 @@ public PackageBlock getParentChunk(){ public ResTableMapEntry getParentMapEntry(){ return getParentInstance(ResTableMapEntry.class); } + public Entry getParentEntry(){ + return getParentInstance(Entry.class); + } public int getName(){ return getInteger(getBytesInternal(), OFFSET_NAME); @@ -170,12 +174,12 @@ public void merge(ValueItem valueItem){ @Override public String toString(){ String name = decodeName(); - String data = decodeData(); + String data = decodeValue(); if(name != null && data != null){ return name + "=\"" + data + "\""; } return "name=" + HexUtil.toHex8(getName()) - +", "+super.toString(); + +", " + super.toString(); } private static final int OFFSET_NAME = 0; diff --git a/src/main/java/com/reandroid/arsc/value/ValueItem.java b/src/main/java/com/reandroid/arsc/value/ValueItem.java index 9cc889f0..62d72126 100755 --- a/src/main/java/com/reandroid/arsc/value/ValueItem.java +++ b/src/main/java/com/reandroid/arsc/value/ValueItem.java @@ -20,6 +20,8 @@ import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.ParentChunk; import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.coder.ValueCoder; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.BlockItem; import com.reandroid.arsc.item.ReferenceBlock; @@ -41,58 +43,21 @@ public abstract class ValueItem extends BlockItem implements Value, public ValueItem(int bytesLength, int sizeOffset) { super(bytesLength); this.sizeOffset = sizeOffset; - writeSize(); } public boolean isUndefined(){ return getValueType() == ValueType.NULL && getData() == 0; } - public Entry resolve(int resourceId){ - PackageBlock packageBlock = getPackageBlock(); - if(packageBlock == null){ + public ResourceEntry resolve(int resourceId){ + PackageBlock context = getPackageBlock(); + if(context == null){ return null; } - Entry entry = packageBlock.getAnyEntry(resourceId); - if(entry != null){ - return entry; - } - TableBlock tableBlock = packageBlock.getTableBlock(); + TableBlock tableBlock = context.getTableBlock(); if(tableBlock == null){ return null; } - return tableBlock.getAnyEntry(resourceId); - } - - public String buildReference(Entry entry, ValueType referenceType, boolean addType){ - if(entry == null){ - return null; - } - PackageBlock packageBlock = entry.getPackageBlock(); - PackageBlock myPackageBlock = getPackageBlock(); - StringBuilder builder = new StringBuilder(); - if(referenceType == ValueType.REFERENCE - || referenceType == ValueType.DYNAMIC_REFERENCE){ - builder.append('@'); - }else if(referenceType == ValueType.ATTRIBUTE - || referenceType == ValueType.DYNAMIC_ATTRIBUTE){ - builder.append('?'); - } - if(packageBlock != myPackageBlock && packageBlock != null && myPackageBlock != null){ - String packageName = packageBlock.getName(); - - if(!packageName.equals(myPackageBlock.getName()) - || packageBlock.getId() != myPackageBlock.getId()){ - builder.append(packageName); - builder.append(':'); - } - - } - if(addType){ - builder.append(entry.getTypeName()); - builder.append('/'); - } - builder.append(entry.getName()); - return builder.toString(); + return tableBlock.getResource(context, resourceId); } public PackageBlock getPackageBlock(){ ParentChunk parentChunk = getParentChunk(); @@ -321,6 +286,41 @@ public void merge(ValueItem valueItem){ setTypeAndData(coming, valueItem.getData()); } } + public String decodeValue(){ + ValueType valueType = getValueType(); + if(valueType == null){ + return null; + } + if(valueType.isReference()){ + return decodeAsReferenceString(valueType); + } + if(valueType == ValueType.STRING){ + return getValueAsString(); + } + return ValueCoder.decode(valueType, getData()); + } + private String decodeAsReferenceString(ValueType valueType){ + int data = getData(); + if(data == 0){ + if(valueType == ValueType.ATTRIBUTE){ + return "?null"; + } + return "@null"; + } + PackageBlock packageBlock = getPackageBlock(); + if(packageBlock == null){ + throw new NullPointerException("Parent package block is null"); + } + TableBlock tableBlock = packageBlock.getTableBlock(); + if(tableBlock == null){ + throw new NullPointerException("Parent table block is null"); + } + ResourceEntry resourceEntry = tableBlock.getResource(packageBlock, data); + if(resourceEntry == null || !resourceEntry.isDeclared()){ + return ValueCoder.decodeUnknownResourceId(valueType.isReference(), data); + } + return resourceEntry.buildReference(packageBlock, valueType); + } @Override public JSONObject toJson() { if(isNull()){ @@ -353,6 +353,9 @@ public void fromJson(JSONObject json) { @Override public String toString(){ + if(getPackageBlock() != null){ + return decodeValue(); + } StringBuilder builder = new StringBuilder(); int size = getSize(); if(size!=8){ diff --git a/src/main/java/com/reandroid/arsc/value/ValueType.java b/src/main/java/com/reandroid/arsc/value/ValueType.java index 71e37fe3..204de010 100755 --- a/src/main/java/com/reandroid/arsc/value/ValueType.java +++ b/src/main/java/com/reandroid/arsc/value/ValueType.java @@ -15,6 +15,11 @@ */ package com.reandroid.arsc.value; +import com.reandroid.arsc.util.StringsUtil; + +import java.util.HashMap; +import java.util.Map; + public enum ValueType { NULL((byte) 0x00, ""), @@ -64,25 +69,49 @@ public boolean isReference(){ } public static ValueType valueOf(byte b){ - ValueType[] all=values(); - for(ValueType vt:all){ - if(vt.mByte==b){ - return vt; - } + return valueOf((b & 0x00ff)); + } + public static ValueType valueOf(int i){ + if(i < 0){ + return null; + } + ValueType[] sorted = getSortedValues(); + if(i < sorted.length){ + return sorted[i]; } return null; } public static ValueType fromName(String name){ - if(name==null){ - return null; + return getValueTypeMap().get(StringsUtil.toUpperCase(name)); + } + private static ValueType[] getSortedValues(){ + if(sortedValues != null){ + return sortedValues; } - name=name.toUpperCase(); - ValueType[] all=values(); - for(ValueType vt:all){ - if(name.equals(vt.name())){ - return vt; + synchronized (ValueType.class){ + ValueType[] sorted = new ValueType[0x1f + 1]; + ValueType[] values = values(); + for(ValueType valueType : values){ + sorted[valueType.getByte() & 0xff] = valueType; } + sortedValues = sorted; + return sorted; + } + } + private static Map getValueTypeMap(){ + if(valueTypeMap != null){ + return valueTypeMap; + } + synchronized (ValueType.class){ + Map map = new HashMap<>(); + ValueType[] values = values(); + for(ValueType valueType : values){ + map.put(valueType.name(), valueType); + } + valueTypeMap = map; + return map; } - return null; } + private static ValueType[] sortedValues; + private static Map valueTypeMap; } diff --git a/src/main/java/com/reandroid/arsc/value/attribute/AttributeBag.java b/src/main/java/com/reandroid/arsc/value/attribute/AttributeBag.java index a9445779..16d4fca1 100755 --- a/src/main/java/com/reandroid/arsc/value/attribute/AttributeBag.java +++ b/src/main/java/com/reandroid/arsc/value/attribute/AttributeBag.java @@ -18,8 +18,6 @@ import com.reandroid.arsc.array.ResValueMapArray; import com.reandroid.arsc.coder.EncodeResult; import com.reandroid.arsc.value.*; -import com.reandroid.common.EntryStore; - public class AttributeBag { private final AttributeBagItem[] mBagItems; @@ -54,9 +52,9 @@ public EncodeResult encodeEnumOrFlagValue(String valueString){ ValueType valueType = isFlag()?ValueType.HEX :ValueType.DEC; return new EncodeResult(valueType, value); } - public String decodeAttributeValue(EntryStore entryStore, int attrValue){ + public String decodeAttributeValue(int attrValue){ AttributeBagItem[] bagItems=searchValue(attrValue); - return AttributeBagItem.toString(entryStore, bagItems, false); + return AttributeBagItem.toString(bagItems, false); } public AttributeBagItem searchByName(String entryName){ AttributeBagItem[] bagItems= getBagItems(); diff --git a/src/main/java/com/reandroid/arsc/value/attribute/AttributeBagItem.java b/src/main/java/com/reandroid/arsc/value/attribute/AttributeBagItem.java index a3286c1f..74d23cdc 100755 --- a/src/main/java/com/reandroid/arsc/value/attribute/AttributeBagItem.java +++ b/src/main/java/com/reandroid/arsc/value/attribute/AttributeBagItem.java @@ -15,15 +15,12 @@ */ package com.reandroid.arsc.value.attribute; -import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.coder.ValueCoder; -import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.value.AttributeType; import com.reandroid.arsc.value.AttributeDataFormat; -import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.ResValueMap; -import com.reandroid.common.EntryStore; public class AttributeBagItem { @@ -35,10 +32,7 @@ public int getData(){ return getBagItem().getData(); } public String getNameOrHex(){ - return getNameOrHex(null); - } - public String getNameOrHex(EntryStore entryStore){ - String name = getName(entryStore); + String name = getName(); if(name == null){ name = ValueCoder.decodeUnknownResourceId(false, getBagItem().getNameResourceID()); @@ -46,32 +40,12 @@ public String getNameOrHex(EntryStore entryStore){ return name; } public String getName(){ - return getName(null); - } - public String getName(EntryStore entryStore){ if(isType()){ return null; } - ResValueMap item = getBagItem(); - int id=item.getName(); - Entry parentEntry=item.getEntry(); - if(parentEntry!=null){ - PackageBlock packageBlock=parentEntry.getPackageBlock(); - if(packageBlock!=null){ - EntryGroup entryGroup=packageBlock.getEntryGroup(id); - if(entryGroup!=null){ - String name=entryGroup.getSpecName(); - if(name!=null){ - return name; - } - } - } - } - if(entryStore!=null){ - EntryGroup entryGroup=entryStore.getEntryGroup(id); - if(entryGroup!=null){ - return entryGroup.getSpecName(); - } + ResourceEntry resourceEntry = getBagItem().resolveName(); + if(resourceEntry != null){ + return resourceEntry.getName(); } return null; } @@ -126,10 +100,10 @@ public String toString(){ return builder.toString(); } - public static String toString(EntryStore entryStore, AttributeBagItem[] bagItems) { - return toString(entryStore, bagItems, false); + public static String toString(AttributeBagItem[] bagItems) { + return toString(bagItems, false); } - public static String toString(EntryStore entryStore, AttributeBagItem[] bagItems, boolean use_hex) { + public static String toString(AttributeBagItem[] bagItems, boolean use_hex) { if(bagItems==null){ return null; } @@ -149,9 +123,9 @@ public static String toString(EntryStore entryStore, AttributeBagItem[] bagItems } String name; if(use_hex){ - name = item.getNameOrHex(entryStore); + name = item.getNameOrHex(); }else { - name = item.getName(entryStore); + name = item.getName(); } if(name == null){ return null; diff --git a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java index 4c03194c..d80b8bb0 100644 --- a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java +++ b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java @@ -1,126 +1,138 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.value.plurals; +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.value.plurals; - import com.reandroid.arsc.value.ResConfig; - import com.reandroid.arsc.value.ResValueMap; - import com.reandroid.arsc.value.ValueType; - import com.reandroid.arsc.value.bag.MapBag; +import com.reandroid.arsc.array.ResValueMapArray; +import com.reandroid.arsc.value.AttributeType; +import com.reandroid.arsc.value.ResConfig; +import com.reandroid.arsc.value.ResValueMap; +import com.reandroid.arsc.value.ValueType; +import com.reandroid.arsc.value.bag.MapBag; - import java.util.Arrays; - import java.util.HashSet; - import java.util.Set; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; - public class PluralsBag extends MapBag { - private PluralsBag(com.reandroid.arsc.value.Entry entry) { - super(entry); - } +public class PluralsBag extends MapBag { + private PluralsBag(com.reandroid.arsc.value.Entry entry) { + super(entry); + } - @Override - protected PluralsBagItem createBagItem(ResValueMap valueMap, boolean copied) { - if (copied) { - return PluralsBagItem.copyOf(valueMap); - } - return PluralsBagItem.create(valueMap); - } + @Override + protected PluralsBagItem createBagItem(ResValueMap valueMap, boolean copied) { + if (copied) { + return PluralsBagItem.copyOf(valueMap); + } + return PluralsBagItem.create(valueMap); + } - @Override - protected ResValueMap newKey(PluralsQuantity key) { - ResValueMap valueMap = new ResValueMap(); - valueMap.setParent(getMapArray()); - valueMap.setNameHigh((short) 0x0100); - valueMap.setNameLow(key.getId()); - return valueMap; - } + @Override + protected ResValueMap newKey(AttributeType key) { + ResValueMap valueMap; + ResValueMapArray mapArray = getMapArray(); + if(mapArray != null){ + valueMap = mapArray.createNext(); + }else { + valueMap = new ResValueMap(); + } + valueMap.setAttributeType(key); + return valueMap; + } - @Override - protected PluralsQuantity getKeyFor(ResValueMap valueMap) { - return PluralsQuantity.valueOf(valueMap); - } + @Override + protected AttributeType getKeyFor(ResValueMap valueMap) { + if(valueMap == null){ + return null; + } + AttributeType attributeType = valueMap.getAttributeType(); + if(attributeType != null && attributeType.isPlural()){ + return attributeType; + } + return null; + } - public String getQuantityString(PluralsQuantity quantity, ResConfig resConfig) { - PluralsBagItem item = get(quantity); - if (item == null) { - return null; - } - return item.getQualityString(resConfig); - } - public String getQuantityString(PluralsQuantity quantity) { - return getQuantityString(quantity, null); - } + public String getQuantityString(AttributeType quantity, ResConfig resConfig) { + PluralsBagItem item = get(quantity); + if (item == null) { + return null; + } + return item.getQualityString(resConfig); + } + public String getQuantityString(AttributeType quantity) { + return getQuantityString(quantity, null); + } - public void setQuantityString(PluralsQuantity quantity, String str) { - if (quantity == null || str == null) { - return; - } - put(quantity, PluralsBagItem.string(getStringPool().getOrCreate(str))); - } + public void setQuantityString(AttributeType quantity, String str) { + if (quantity == null || str == null) { + return; + } + put(quantity, PluralsBagItem.string(getStringPool().getOrCreate(str))); + } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("<"); - String type = getTypeName(); - builder.append(type); - builder.append(" name=\""); - builder.append(getName()); - builder.append("\">"); - for (PluralsBagItem pluralsBagItem : values()) { - builder.append("\n "); - builder.append(pluralsBagItem.toString()); - } - builder.append("\n"); - return builder.toString(); - } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("<"); + String type = getTypeName(); + builder.append(type); + builder.append(" name=\""); + builder.append(getName()); + builder.append("\">"); + for (PluralsBagItem pluralsBagItem : values()) { + builder.append("\n "); + builder.append(pluralsBagItem.toString()); + } + builder.append("\n"); + return builder.toString(); + } - private final static Set validTypes = new HashSet<>(Arrays.asList(ValueType.NULL, ValueType.STRING, ValueType.REFERENCE)); + private final static Set validTypes = new HashSet<>(Arrays.asList(ValueType.NULL, ValueType.STRING, ValueType.REFERENCE)); - /** - * The result of this is not always 100% accurate, - * in addition to this use your methods to cross check like type-name == "plurals" - **/ - public static boolean isPlurals(com.reandroid.arsc.value.Entry entry) { - PluralsBag plurals = create(entry); - if (plurals == null) { - return false; - } - ResValueMap[] items = plurals.getMapArray().getChildes(); - if (items.length == 0) { - return false; - } + /** + * The result of this is not always 100% accurate, + * in addition to this use your methods to cross check like type-name == "plurals" + **/ + public static boolean isPlurals(com.reandroid.arsc.value.Entry entry) { + PluralsBag plurals = create(entry); + if (plurals == null) { + return false; + } + ResValueMap[] items = plurals.getMapArray().getChildes(); + if (items.length == 0) { + return false; + } - for (ResValueMap item : items) { - if (item == null || !validTypes.contains(item.getValueType())) { - return false; - } - int name = item.getName(); - int high = (name >> 16) & 0xffff; - if (PluralsQuantity.valueOf(item) == null || high != 0x0100) { - return false; - } - } - return true; - } + for (ResValueMap item : items) { + if (item == null || !validTypes.contains(item.getValueType())) { + return false; + } + AttributeType attributeType = item.getAttributeType(); + if(attributeType == null || !attributeType.isPlural()){ + return false; + } + } + return true; + } - public static PluralsBag create(com.reandroid.arsc.value.Entry entry) { - if (entry == null || !entry.isComplex()) { - return null; - } - return new PluralsBag(entry); - } - } + public static PluralsBag create(com.reandroid.arsc.value.Entry entry) { + if (entry == null || !entry.isComplex()) { + return null; + } + return new PluralsBag(entry); + } +} diff --git a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java index 34b5ba79..8715cbd5 100644 --- a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java +++ b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java @@ -1,131 +1,135 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.arsc.value.plurals; - - import com.reandroid.arsc.chunk.TableBlock; - import com.reandroid.arsc.item.StringItem; - import com.reandroid.arsc.item.TableString; - import com.reandroid.arsc.util.HexUtil; - import com.reandroid.arsc.value.*; - import com.reandroid.arsc.value.bag.BagItem; - - import java.util.List; - - public class PluralsBagItem extends BagItem { - private PluralsBagItem(ResValueMap bagItem) { - super(bagItem); - } - - private PluralsBagItem(StringItem str) { - super(str); - } - - private PluralsBagItem(ValueType valueType, int data) { - super(valueType, data); - } - - public PluralsQuantity getQuantity() { - if (mBagItem == null || mBagItem.getName() == 0) { - return null; - } - return PluralsQuantity.valueOf(mBagItem); - } - - public String getQualityString(ResConfig resConfig) { - switch (getValueType()) { - case STRING: - return getStringValue(); - case REFERENCE: - Entry entry = null; - if (mBagItem != null) { - entry = mBagItem.getEntry(); - } - if (entry == null) { - return null; - } - - if (resConfig == null) { - resConfig = entry.getResConfig(); - } - - Entry stringRes = null; - if (resConfig != null) { - TableBlock tableBlock = entry.getPackageBlock().getTableBlock(); - List resolvedList = tableBlock.resolveReferenceWithConfig(getValue(), resConfig); - if (resolvedList.size() > 0) { - stringRes = resolvedList.get(0); - } - } - - if (stringRes == null) { - return null; - } - ResValue resValue = stringRes.getResValue(); - if (resValue == null || resValue.getValueType() != ValueType.STRING) { - throw new IllegalArgumentException("Not a STR reference: " + formattedRefValue()); - } - return resValue.getValueAsString(); - default: - throw new IllegalArgumentException("Not STR/REFERENCE ValueType=" + getValueType()); - } - } - - private String formattedRefValue() { - return HexUtil.toHex8("@0x", getValue()); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(""); - if (hasStringValue()) { - builder.append(getStringValue()); - } else { - builder.append(formattedRefValue()); - } - builder.append(""); - return builder.toString(); - } - - protected static PluralsBagItem create(ResValueMap resValueMap) { - if (resValueMap == null) { - return null; - } - return new PluralsBagItem(resValueMap); - } - - protected static PluralsBagItem copyOf(ResValueMap resValueMap) { - ValueType valueType = resValueMap.getValueType(); - if (valueType == ValueType.STRING) { - return new PluralsBagItem(resValueMap.getDataAsPoolString()); - } else { - return new PluralsBagItem(valueType, resValueMap.getData()); - } - } - - public static PluralsBagItem string(TableString str) { - if (str == null) { - return null; - } - return new PluralsBagItem(str); - } - - public static PluralsBagItem reference(int resourceId) { - return new PluralsBagItem(ValueType.REFERENCE, resourceId); - } - } +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.arsc.value.plurals; + +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.item.StringItem; +import com.reandroid.arsc.item.TableString; +import com.reandroid.arsc.util.HexUtil; +import com.reandroid.arsc.value.*; +import com.reandroid.arsc.value.bag.BagItem; + +import java.util.List; + +public class PluralsBagItem extends BagItem { + private PluralsBagItem(ResValueMap bagItem) { + super(bagItem); + } + + private PluralsBagItem(StringItem str) { + super(str); + } + + private PluralsBagItem(ValueType valueType, int data) { + super(valueType, data); + } + + public AttributeType getQuantity() { + if (mBagItem == null) { + return null; + } + AttributeType attributeType = mBagItem.getAttributeType(); + if(attributeType != null && attributeType.isPlural()){ + return attributeType; + } + return null; + } + + public String getQualityString(ResConfig resConfig) { + switch (getValueType()) { + case STRING: + return getStringValue(); + case REFERENCE: + Entry entry = null; + if (mBagItem != null) { + entry = mBagItem.getEntry(); + } + if (entry == null) { + return null; + } + + if (resConfig == null) { + resConfig = entry.getResConfig(); + } + + Entry stringRes = null; + if (resConfig != null) { + TableBlock tableBlock = entry.getPackageBlock().getTableBlock(); + List resolvedList = tableBlock.resolveReferenceWithConfig(getValue(), resConfig); + if (resolvedList.size() > 0) { + stringRes = resolvedList.get(0); + } + } + + if (stringRes == null) { + return null; + } + ResValue resValue = stringRes.getResValue(); + if (resValue == null || resValue.getValueType() != ValueType.STRING) { + throw new IllegalArgumentException("Not a STR reference: " + formattedRefValue()); + } + return resValue.getValueAsString(); + default: + throw new IllegalArgumentException("Not STR/REFERENCE ValueType=" + getValueType()); + } + } + + private String formattedRefValue() { + return HexUtil.toHex8("@0x", getValue()); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(""); + if (hasStringValue()) { + builder.append(getStringValue()); + } else { + builder.append(formattedRefValue()); + } + builder.append(""); + return builder.toString(); + } + + protected static PluralsBagItem create(ResValueMap resValueMap) { + if (resValueMap == null) { + return null; + } + return new PluralsBagItem(resValueMap); + } + + protected static PluralsBagItem copyOf(ResValueMap resValueMap) { + ValueType valueType = resValueMap.getValueType(); + if (valueType == ValueType.STRING) { + return new PluralsBagItem(resValueMap.getDataAsPoolString()); + } else { + return new PluralsBagItem(valueType, resValueMap.getData()); + } + } + + public static PluralsBagItem string(TableString str) { + if (str == null) { + return null; + } + return new PluralsBagItem(str); + } + + public static PluralsBagItem reference(int resourceId) { + return new PluralsBagItem(ValueType.REFERENCE, resourceId); + } +} diff --git a/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java b/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java index d02f05e4..d17f8942 100755 --- a/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java +++ b/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java @@ -18,6 +18,10 @@ import com.reandroid.arsc.value.ResValueMap; + /** + * Replaced by com.reandroid.arsc.value.AttributeType + * */ + @Deprecated public enum PluralsQuantity { OTHER((short) 0x0004), ZERO((short) 0x0005), diff --git a/src/main/java/com/reandroid/arsc/value/style/StyleBag.java b/src/main/java/com/reandroid/arsc/value/style/StyleBag.java index 752749b8..0807f73b 100644 --- a/src/main/java/com/reandroid/arsc/value/style/StyleBag.java +++ b/src/main/java/com/reandroid/arsc/value/style/StyleBag.java @@ -16,7 +16,9 @@ package com.reandroid.arsc.value.style; import com.reandroid.apk.xmlencoder.EncodeMaterials; + import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; + import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.bag.MapBag; @@ -34,7 +36,11 @@ public String getParentResourceName() { if (entry == null) { return null; } - return entry.buildResourceName(id, '@', true); + ResTableMapEntry tableMapEntry = entry.getResTableMapEntry(); + if(tableMapEntry != null){ + return tableMapEntry.decodeParentId(); + } + return null; } public int getParentId() { @@ -123,7 +129,7 @@ public static boolean isStyle(com.reandroid.arsc.value.Entry entry) { } for (ResValueMap item : items) { - if (item == null || tableBlock.search(item.getNameResourceID()) == null) { + if (item == null || tableBlock.getResource(item.getNameResourceID()) == null) { return false; } } diff --git a/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java b/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java index 76e6ea0d..f8c48779 100644 --- a/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java +++ b/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java @@ -15,8 +15,9 @@ */ package com.reandroid.arsc.value.style; +import com.reandroid.arsc.coder.CommonType; import com.reandroid.arsc.coder.EncodeResult; -import com.reandroid.arsc.coder.ValueDecoder; +import com.reandroid.arsc.coder.ValueCoder; import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.util.HexUtil; @@ -26,7 +27,6 @@ import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.ValueType; -import com.reandroid.common.EntryStore; public class StyleBagItem extends BagItem { private StyleBagItem(ResValueMap bagItem) { @@ -45,18 +45,13 @@ public String getName() { if (mBagItem == null) { return null; } - Entry block = mBagItem.getEntry(); - if (block == null) { - return null; - } - char prefix = 0; - return block.buildResourceName(mBagItem.getName(), prefix, false); + return mBagItem.decodeName(true); } - public Entry getAttributeEntry(EntryStore entryStore) { + public Entry getAttributeEntry() { if (mBagItem == null) { return null; } - return entryStore.getEntryGroup(mBagItem.getName()).pickOne(); + return mBagItem.resolveName().get(); } public int getNameId() { @@ -79,24 +74,13 @@ public String getValueAsReference() { if (valueType != ValueType.REFERENCE && valueType != ValueType.ATTRIBUTE) { throw new IllegalArgumentException("Not REF ValueType=" + valueType); } - Entry entry = getBagItem().getEntry(); - if (entry == null) { - return null; - } - char prefix = '@'; - boolean includeType = true; - if (valueType == ValueType.ATTRIBUTE) { - prefix = '?'; - includeType = false; - } - int id = getValue(); - return entry.buildResourceName(id, prefix, includeType); + return getBagItem().decodeValue(); } - public String decodeAttributeValue(AttributeBag attr, EntryStore entryStore) { + public String decodeAttributeValue(AttributeBag attr) { if (!hasIntValue()) { return null; } - return attr.decodeAttributeValue(entryStore, getValue()); + return attr.decodeAttributeValue(getValue()); } public AttributeBagItem[] getFlagsOrEnum(AttributeBag attr) { if (!hasIntValue()) { @@ -177,10 +161,15 @@ public static StyleBagItem encoded(EncodeResult encodeResult) { return create(encodeResult.valueType, encodeResult.value); } public static StyleBagItem color(String color) { - return encoded(ValueDecoder.encodeColor(color)); + EncodeResult encodeResult = ValueCoder.encode(color, CommonType.COLOR.valueTypes()); + return encoded(encodeResult); } public static StyleBagItem dimensionOrFraction(String str) { - return encoded(ValueDecoder.encodeDimensionOrFraction(str)); + EncodeResult encodeResult = ValueCoder.encode(str, ValueType.DIMENSION); + if(encodeResult == null){ + encodeResult = ValueCoder.encode(str, ValueType.FRACTION); + } + return encoded(encodeResult); } public static StyleBagItem createFloat(float n) { return new StyleBagItem(ValueType.FLOAT, Float.floatToIntBits(n)); diff --git a/src/main/java/com/reandroid/common/EntryStore.java b/src/main/java/com/reandroid/common/EntryStore.java deleted file mode 100755 index 4d6e24de..00000000 --- a/src/main/java/com/reandroid/common/EntryStore.java +++ /dev/null @@ -1,28 +0,0 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.common; - -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.item.TableString; - -import java.util.Collection; - -public interface EntryStore { - Collection getEntryGroups(int resourceId); - EntryGroup getEntryGroup(int resourceId); - Collection getPackageBlocks(int packageId); -} diff --git a/src/main/java/com/reandroid/common/Frameworks.java b/src/main/java/com/reandroid/common/Frameworks.java deleted file mode 100755 index f3fd9c24..00000000 --- a/src/main/java/com/reandroid/common/Frameworks.java +++ /dev/null @@ -1,43 +0,0 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.common; - -import com.reandroid.apk.AndroidFrameworks; -import com.reandroid.arsc.util.FrameworkTable; - -import java.io.IOException; -import java.io.InputStream; - -/**Use {@link AndroidFrameworks} */ -@Deprecated -public class Frameworks { - private static FrameworkTable android_table; - private static boolean load_once; - @Deprecated - public static FrameworkTable getAndroid(){ - if(android_table!=null || load_once){ - return android_table; - } - load_once=true; - FrameworkTable frameworkTable=null; - try { - frameworkTable = AndroidFrameworks.getLatest().getTableBlock(); - } catch (IOException e) { - } - android_table=frameworkTable; - return android_table; - } -} diff --git a/src/main/java/com/reandroid/common/Namespace.java b/src/main/java/com/reandroid/common/Namespace.java new file mode 100644 index 00000000..da067ef9 --- /dev/null +++ b/src/main/java/com/reandroid/common/Namespace.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.common; + +public interface Namespace { + String getPrefix(); + String getUri(); + static boolean isValidPrefix(String prefix){ + if(prefix == null || prefix.length() == 0){ + return false; + } + char[] chars = prefix.toCharArray(); + if(!isValidPrefixChar(chars[0])){ + return false; + } + for(int i = 1; i < chars.length; i++){ + char ch = chars[i]; + if(isValidPrefixChar(ch) || isValidPrefixSymbol(ch)){ + continue; + } + return false; + } + return true; + } + static boolean isValidPrefixChar(char ch){ + return (ch <= 'Z' && ch >= 'A') + || (ch <= 'z' && ch >= 'a'); + } + static boolean isValidPrefixSymbol(char ch){ + return (ch <= '9' && ch >= '0') + || ch == '_'; + } +} diff --git a/src/main/java/com/reandroid/common/ReferenceResolver.java b/src/main/java/com/reandroid/common/ReferenceResolver.java index a82a32bd..3d662cd6 100644 --- a/src/main/java/com/reandroid/common/ReferenceResolver.java +++ b/src/main/java/com/reandroid/common/ReferenceResolver.java @@ -1,21 +1,22 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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. - */ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.common; -import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.ResConfig; import com.reandroid.arsc.value.ResValue; @@ -25,11 +26,11 @@ import java.util.function.Predicate; public class ReferenceResolver{ - private final EntryStore entryStore; + private final TableBlock entryStore; private final List results; private final Set resolvedIds; private int limit; - public ReferenceResolver(EntryStore entryStore){ + public ReferenceResolver(TableBlock entryStore){ this.entryStore = entryStore; this.results = new ArrayList<>(); this.resolvedIds = new HashSet<>(); @@ -104,11 +105,11 @@ private void addResult(Predicate filter, Entry entry){ } private List listNonNullEntries(int resourceId){ List results = new ArrayList<>(); - EntryGroup entryGroup = this.entryStore.getEntryGroup(resourceId); - if(entryGroup==null){ + ResourceEntry resourceEntry = this.entryStore.getResource(resourceId); + if(resourceEntry == null){ return results; } - Iterator itr = entryGroup.iterator(true); + Iterator itr = resourceEntry.iterator(true); while (itr.hasNext()){ results.add(itr.next()); } @@ -131,7 +132,7 @@ public boolean test(Entry entry) { @Override public int compare(Entry entry1, Entry entry2) { ResConfig config1 = entry1.getResConfig(); - ResConfig config2 = entry1.getResConfig(); + ResConfig config2 = entry2.getResConfig(); if (config.equals(config1)){ return -1; } diff --git a/src/main/java/com/reandroid/common/TableEntryStore.java b/src/main/java/com/reandroid/common/TableEntryStore.java deleted file mode 100755 index 8404410f..00000000 --- a/src/main/java/com/reandroid/common/TableEntryStore.java +++ /dev/null @@ -1,224 +0,0 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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 com.reandroid.common; - -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.TableBlock; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.util.FrameworkTable; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.StagedAliasEntry; - -import java.util.*; - -public class TableEntryStore implements EntryStore{ - private final Map> mLocalPackagesMap; - private final Map> mFrameworkPackagesMap; - public TableEntryStore(){ - this.mLocalPackagesMap = new HashMap<>(); - this.mFrameworkPackagesMap = new HashMap<>(); - } - - public String getEntryName(int resourceId){ - Entry entry = getEntry(resourceId); - if(entry ==null){ - return null; - } - return entry.getName(); - } - public Entry getEntry(int resourceId){ - if(resourceId==0){ - return null; - } - EntryGroup entryGroup=getEntryGroup(resourceId); - if(entryGroup==null){ - return null; - } - return entryGroup.pickOne(); - } - public void add(TableBlock tableBlock){ - if(tableBlock==null){ - return; - } - for(PackageBlock packageBlock:tableBlock.listPackages()){ - add(packageBlock); - } - } - public void add(PackageBlock packageBlock){ - if(packageBlock==null){ - return; - } - Set packageBlockSet; - if(packageBlock.getTableBlock() instanceof FrameworkTable){ - packageBlockSet = getOrCreateFrameworks(packageBlock.getId()); - }else { - packageBlockSet = getOrCreateLocal(packageBlock.getId()); - } - if(packageBlockSet.contains(packageBlock)){ - return; - } - packageBlockSet.add(packageBlock); - } - private Set getOrCreateLocal(int packageId){ - Integer id=packageId; - Set packageBlockSet = mLocalPackagesMap.get(id); - if(packageBlockSet==null){ - packageBlockSet=new HashSet<>(); - mLocalPackagesMap.put(id, packageBlockSet); - } - return packageBlockSet; - } - private Set getOrCreateFrameworks(int packageId){ - Integer id=packageId; - Set packageBlockSet = mFrameworkPackagesMap.get(id); - if(packageBlockSet==null){ - packageBlockSet=new HashSet<>(); - mFrameworkPackagesMap.put(id, packageBlockSet); - } - return packageBlockSet; - } - @Override - public List getEntryGroups(int resourceId) { - List results = searchEntryGroupsLocal(resourceId); - if(results.size()>0){ - return results; - } - int alias = searchIdAliasLocal(resourceId); - results = searchEntryGroupsLocal(alias); - if(results.size()>0){ - return results; - } - results = searchEntryGroupsFramework(resourceId); - if(results.size()>0){ - return results; - } - alias = searchIdAliasFramework(resourceId); - return searchEntryGroupsFramework(alias); - } - @Override - public EntryGroup getEntryGroup(int resourceId) { - EntryGroup entryGroup = searchEntryLocal(resourceId); - if(entryGroup==null){ - entryGroup = searchEntryLocal(searchIdAliasLocal(resourceId)); - } - if(entryGroup==null){ - entryGroup = searchEntryFramework(resourceId); - } - if(entryGroup==null){ - entryGroup = searchEntryFramework(searchIdAliasFramework(resourceId)); - } - return entryGroup; - } - @Override - public List getPackageBlocks(int packageId) { - List results=new ArrayList<>(); - packageId = 0xff & packageId; - Set packageBlockSet = mLocalPackagesMap.get(packageId); - if(packageBlockSet==null){ - packageBlockSet = mFrameworkPackagesMap.get(packageId); - } - if(packageBlockSet!=null){ - results.addAll(packageBlockSet); - } - return results; - } - private List searchEntryGroupsLocal(int resourceId) { - if(resourceId==0){ - return new ArrayList<>(); - } - List results=new ArrayList<>(); - int pkgId = (resourceId>>24)&0xff; - Set packageBlockSet = mLocalPackagesMap.get(pkgId); - if(packageBlockSet==null){ - return results; - } - for(PackageBlock packageBlock: packageBlockSet){ - EntryGroup group=packageBlock.getEntryGroup(resourceId); - if(group!=null){ - results.add(group); - } - } - return results; - } - private List searchEntryGroupsFramework(int resourceId) { - if(resourceId==0){ - return new ArrayList<>(); - } - List results=new ArrayList<>(); - int pkgId = (resourceId>>24)&0xff; - Set packageBlockSet = mFrameworkPackagesMap.get(pkgId); - if(packageBlockSet==null){ - return results; - } - for(PackageBlock packageBlock: packageBlockSet){ - EntryGroup group=packageBlock.getEntryGroup(resourceId); - if(group!=null){ - results.add(group); - } - } - return results; - } - private EntryGroup searchEntryLocal(int resourceId) { - int packageId = (resourceId>>24)&0xff; - Set packageBlockSet = mLocalPackagesMap.get(packageId); - if(packageBlockSet==null){ - return null; - } - for(PackageBlock packageBlock: packageBlockSet){ - EntryGroup group=packageBlock.getEntryGroup(resourceId); - if(group!=null && group.pickOne()!=null){ - return group; - } - } - return null; - } - private EntryGroup searchEntryFramework(int resourceId) { - int packageId = (resourceId>>24)&0xff; - Set packageBlockSet = mFrameworkPackagesMap.get(packageId); - if(packageBlockSet==null){ - return null; - } - for(PackageBlock packageBlock: packageBlockSet){ - EntryGroup group=packageBlock.getEntryGroup(resourceId); - if(group!=null && group.pickOne()!=null){ - return group; - } - } - return null; - } - private int searchIdAliasLocal(int resourceId) { - for(Set packageBlockSet : mLocalPackagesMap.values()){ - for(PackageBlock packageBlock:packageBlockSet){ - StagedAliasEntry stagedAliasEntry = packageBlock.searchByStagedResId(resourceId); - if(stagedAliasEntry!=null){ - return stagedAliasEntry.getFinalizedResId(); - } - } - } - return 0; - } - private int searchIdAliasFramework(int resourceId) { - for(Set packageBlockSet : mLocalPackagesMap.values()){ - for(PackageBlock packageBlock:packageBlockSet){ - StagedAliasEntry stagedAliasEntry = packageBlock.searchByStagedResId(resourceId); - if(stagedAliasEntry!=null){ - return stagedAliasEntry.getFinalizedResId(); - } - } - } - return 0; - } -} diff --git a/src/main/java/com/reandroid/identifiers/PackageIdentifier.java b/src/main/java/com/reandroid/identifiers/PackageIdentifier.java index ade5669c..542f8312 100644 --- a/src/main/java/com/reandroid/identifiers/PackageIdentifier.java +++ b/src/main/java/com/reandroid/identifiers/PackageIdentifier.java @@ -17,18 +17,12 @@ import com.android.org.kxml2.io.KXmlParser; import com.android.org.kxml2.io.KXmlSerializer; -import com.reandroid.arsc.array.EntryArray; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.container.SpecTypePair; -import com.reandroid.arsc.group.EntryGroup; -import com.reandroid.arsc.group.ResourceEntry; -import com.reandroid.arsc.item.SpecString; -import com.reandroid.arsc.pool.SpecStringPool; -import com.reandroid.arsc.pool.TypeStringPool; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ValueHeader; import com.reandroid.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -50,84 +44,6 @@ public PackageIdentifier(){ this(0, null); } - public void initialize(PackageBlock packageBlock){ - initialize(packageBlock, true); - } - public void initialize(PackageBlock packageBlock, boolean initialize_ids){ - packageBlock.setId(getId()); - String name = getName(); - if(name != null){ - packageBlock.setName(name); - } - initializeTypeName(packageBlock.getTypeStringPool()); - initializeSpecNames(packageBlock.getSpecStringPool()); - if(initialize_ids){ - initializeIds(packageBlock); - } - initializeEntries(packageBlock); - initializePackageJson(packageBlock); - this.mPackageLoadStamp = packageBlock.getEntryGroupCount(); - setPackageBlock(packageBlock); - } - private void initializeTypeName(TypeStringPool typeStringPool){ - for(TypeIdentifier ti : list()){ - typeStringPool.getOrCreate(ti.getId(), ti.getName()); - } - } - private void initializeSpecNames(SpecStringPool specStringPool){ - List nameList = new ArrayList<>(getResourcesCount()); - for(TypeIdentifier ti : list()){ - nameList.addAll(ti.listNames()); - } - //specStringPool.addStrings(nameList); - } - private void initializeIds(PackageBlock packageBlock){ - TypeIdentifier identifierID = get("id"); - if(identifierID == null){ - return; - } - TypeBlock typeBlock = packageBlock - .getOrCreateTypeBlock("", "id"); - EntryArray entryArray = typeBlock.getEntryArray(); - entryArray.ensureSize(identifierID.size()); - SpecStringPool specStringPool = packageBlock.getSpecStringPool(); - for(ResourceIdentifier ri : identifierID.list()){ - Entry entry = entryArray.getOrCreate((short) ri.getId()); - SpecString specString = specStringPool.getOrCreate(ri.getName()); - if(!entry.isNull() && !entry.isComplex()){ - entry.setSpecReference(specString); - continue; - } - entry.setValueAsBoolean(false); - entry.setSpecReference(specString); - setIdEntryVisibility(entry); - } - } - private void initializeEntries(PackageBlock packageBlock){ - for(TypeIdentifier ti : list()){ - - TypeBlock typeBlock = packageBlock - .getOrCreateTypeBlock("", ti.getName()); - int size = ti.size(); - int idMax = ti.getMaxId(); - int count; - if(size > idMax){ - count = size; - }else { - count = idMax + 1; - } - typeBlock.ensureEntriesCount(count); - for(ResourceIdentifier ri : ti.list()){ - Entry entry = typeBlock.getOrCreateEntry((short) ri.getId()); - entry.setName(ri.getName(), true); - } - } - } - private void setIdEntryVisibility(Entry entry){ - ValueHeader valueHeader = entry.getHeader(); - valueHeader.setWeak(true); - valueHeader.setPublic(true); - } private void initializePackageJson(PackageBlock packageBlock){ File jsonFile = searchPackageJsonFromTag(); if(jsonFile == null){ @@ -198,16 +114,19 @@ public void setResourceNamesToPackage(PackageBlock packageBlock){ if(packageBlock == null){ return; } - for(EntryGroup entryGroup : packageBlock.listEntryGroup()){ - setResourceNamesToEntry(entryGroup); + for(SpecTypePair specTypePair:packageBlock.listSpecTypePairs()){ + Iterator itr = specTypePair.getResources(); + while (itr.hasNext()){ + setResourceNamesToEntry(itr.next()); + } } } - public void setResourceNamesToEntry(EntryGroup entryGroup){ - ResourceIdentifier ri = getResourceIdentifier(entryGroup.getResourceId()); + public void setResourceNamesToEntry(ResourceEntry resourceEntry){ + ResourceIdentifier ri = getResourceIdentifier(resourceEntry.getResourceId()); if(ri == null){ return; } - entryGroup.renameSpec(ri.getName()); + resourceEntry.setName(ri.getName()); } public ResourceIdentifier getResourceIdentifier(int resourceId){ TypeIdentifier typeIdentifier = get((resourceId >> 16) & 0xff); @@ -246,7 +165,6 @@ public void writePublicXml(OutputStream outputStream) throws IOException { write(serializer); } public void write(XmlSerializer serializer) throws IOException { - updateChangedPackageBlock(); serializer.startDocument("utf-8", null); serializer.text("\n"); serializer.startTag(null, XML_TAG_RESOURCES); @@ -278,16 +196,6 @@ public void load(PackageBlock packageBlock){ loadEntryGroups(packageBlock); setPackageBlock(packageBlock); } - private void updateChangedPackageBlock(){ - PackageBlock packageBlock = getPackageBlock(); - if(packageBlock == null){ - return; - } - if(packageBlock.getEntryGroupCount() == this.mPackageLoadStamp){ - return; - } - loadEntryGroups(packageBlock); - } private void loadEntryGroups(PackageBlock packageBlock){ for(SpecTypePair specTypePair:packageBlock.listSpecTypePairs()){ Iterator itr = specTypePair.getResources(); @@ -295,7 +203,6 @@ private void loadEntryGroups(PackageBlock packageBlock){ add(itr.next()); } } - this.mPackageLoadStamp = packageBlock.getEntryGroupCount(); } public void loadPublicXml(File file) throws IOException, XmlPullParserException { FileInputStream fileInputStream = new FileInputStream(file); @@ -418,9 +325,6 @@ public void setPackageBlock(PackageBlock packageBlock) { this.mPackageBlock = packageBlock; } - public void add(EntryGroup entryGroup){ - add(entryGroup.pickOne()); - } public void add(ResourceEntry resourceEntry){ add(resourceEntry.get()); } diff --git a/src/main/java/com/reandroid/identifiers/ResourceIdentifier.java b/src/main/java/com/reandroid/identifiers/ResourceIdentifier.java index 6c056f7f..c3de6d9e 100644 --- a/src/main/java/com/reandroid/identifiers/ResourceIdentifier.java +++ b/src/main/java/com/reandroid/identifiers/ResourceIdentifier.java @@ -16,12 +16,11 @@ package com.reandroid.identifiers; import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.model.ResourceEntry; import com.reandroid.arsc.util.HexUtil; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; -import java.util.Objects; public class ResourceIdentifier extends Identifier{ private Boolean mHasGoodName; @@ -164,18 +163,19 @@ public boolean renameSpec(){ if(packageBlock == null){ return false; } - EntryGroup entryGroup = packageBlock.getEntryGroup(getResourceId()); - if(entryGroup == null){ + ResourceEntry resourceEntry = packageBlock.getResource(getResourceId()); + if(resourceEntry == null){ return false; } String name = getName(); if(name == null){ return false; } - if(name.equals(entryGroup.getSpecName())){ + if(name.equals(resourceEntry.getName())){ return false; } - return entryGroup.renameSpec(name); + resourceEntry.setName(name); + return true; } private PackageBlock getPackageBlock(){ PackageIdentifier pi = getPackageIdentifier(); diff --git a/src/main/java/com/reandroid/identifiers/TableIdentifier.java b/src/main/java/com/reandroid/identifiers/TableIdentifier.java index 7612fb77..1e02755c 100644 --- a/src/main/java/com/reandroid/identifiers/TableIdentifier.java +++ b/src/main/java/com/reandroid/identifiers/TableIdentifier.java @@ -15,7 +15,6 @@ */ package com.reandroid.identifiers; -import com.reandroid.arsc.array.PackageArray; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.util.StringsUtil; @@ -35,17 +34,6 @@ public TableIdentifier() { this.mCaseInsensitive = Identifier.CASE_INSENSITIVE_FS; } - public void initialize(TableBlock tableBlock){ - initialize(tableBlock, true); - } - public void initialize(TableBlock tableBlock, boolean initialize_ids){ - PackageArray packageArray = tableBlock.getPackageArray(); - for(PackageIdentifier pi : getPackages()){ - PackageBlock packageBlock = packageArray.createNext(); - pi.initialize(packageBlock, initialize_ids); - } - } - public void load(TableBlock tableBlock){ for(PackageBlock packageBlock : tableBlock.listPackages()){ load(packageBlock); diff --git a/src/main/java/com/reandroid/xml/parser/MXParser.java b/src/main/java/com/reandroid/xml/parser/MXParser.java deleted file mode 100644 index 1d735629..00000000 --- a/src/main/java/com/reandroid/xml/parser/MXParser.java +++ /dev/null @@ -1,2718 +0,0 @@ -/* - * This class is taken from org.xmlpull.* - * - * Check license: http://xmlpull.org - * - */ - -/*This package is renamed from org.xmlpull.* to avoid conflicts*/ -package com.reandroid.xml.parser; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; - -public class MXParser implements XmlPullParser -{ - protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace"; - protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; - protected final static String FEATURE_XML_ROUNDTRIP= - //"http://xmlpull.org/v1/doc/features.html#xml-roundtrip"; - "http://xmlpull.org/v1/doc/features.html#xml-roundtrip"; - protected final static String FEATURE_NAMES_INTERNED = - "http://xmlpull.org/v1/doc/features.html#names-interned"; - protected final static String PROPERTY_XMLDECL_VERSION = - "http://xmlpull.org/v1/doc/properties.html#xmldecl-version"; - protected final static String PROPERTY_XMLDECL_STANDALONE = - "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone"; - protected final static String PROPERTY_XMLDECL_CONTENT = - "http://xmlpull.org/v1/doc/properties.html#xmldecl-content"; - protected final static String PROPERTY_LOCATION = - "http://xmlpull.org/v1/doc/properties.html#location"; - - protected boolean allStringsInterned; - - protected void resetStringCache() { - } - - protected String newString(char[] cbuf, int off, int len) { - return new String(cbuf, off, len); - } - - protected String newStringIntern(char[] cbuf, int off, int len) { - return (new String(cbuf, off, len)).intern(); - } - - private static final boolean TRACE_SIZING = false; - - protected boolean processNamespaces; - protected boolean roundtripSupported; - - protected String location; - protected int lineNumber; - protected int columnNumber; - protected boolean seenRoot; - protected boolean reachedEnd; - protected int eventType; - protected boolean emptyElementTag; - - protected int depth; - protected char[][] elRawName; - protected int[] elRawNameEnd; - protected int[] elRawNameLine; - - protected String[] elName; - protected String[] elPrefix; - protected String[] elUri; - //protected String elValue[]; - protected int[] elNamespaceCount; - - protected void ensureElementsCapacity() { - final int elStackSize = elName != null ? elName.length : 0; - if( (depth + 1) >= elStackSize) { - // we add at least one extra slot ... - final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25 - final boolean needsCopying = elStackSize > 0; - String[] arr = null; - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize); - elName = arr; - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize); - elPrefix = arr; - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elUri, 0, arr, 0, elStackSize); - elUri = arr; - - int[] iarr = new int[newSize]; - if(needsCopying) { - System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize); - } else { - iarr[0] = 0; - } - elNamespaceCount = iarr; - iarr = new int[newSize]; - if(needsCopying) { - System.arraycopy(elRawNameEnd, 0, iarr, 0, elStackSize); - } - elRawNameEnd = iarr; - - iarr = new int[newSize]; - if(needsCopying) { - System.arraycopy(elRawNameLine, 0, iarr, 0, elStackSize); - } - elRawNameLine = iarr; - - final char[][] carr = new char[newSize][]; - if(needsCopying) { - System.arraycopy(elRawName, 0, carr, 0, elStackSize); - } - elRawName = carr; - } - } - protected int attributeCount; - protected String[] attributeName; - protected int[] attributeNameHash; - protected String[] attributePrefix; - protected String[] attributeUri; - protected String[] attributeValue; - - /** - * Make sure that in attributes temporary array is enough space. - */ - protected void ensureAttributesCapacity(int size) { - final int attrPosSize = attributeName != null ? attributeName.length : 0; - if(size >= attrPosSize) { - final int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25 - - final boolean needsCopying = attrPosSize > 0; - String[] arr = null; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(attributeName, 0, arr, 0, attrPosSize); - attributeName = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(attributePrefix, 0, arr, 0, attrPosSize); - attributePrefix = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(attributeUri, 0, arr, 0, attrPosSize); - attributeUri = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(attributeValue, 0, arr, 0, attrPosSize); - attributeValue = arr; - - if( ! allStringsInterned ) { - final int[] iarr = new int[newSize]; - if(needsCopying) System.arraycopy(attributeNameHash, 0, iarr, 0, attrPosSize); - attributeNameHash = iarr; - } - - arr = null; - } - } - protected int namespaceEnd; - protected String namespacePrefix[]; - protected int namespacePrefixHash[]; - protected String namespaceUri[]; - - protected void ensureNamespacesCapacity(int size) { - final int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0; - if(size >= namespaceSize) { - final int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25 - - final String[] newNamespacePrefix = new String[newSize]; - final String[] newNamespaceUri = new String[newSize]; - if(namespacePrefix != null) { - System.arraycopy( - namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd); - System.arraycopy( - namespaceUri, 0, newNamespaceUri, 0, namespaceEnd); - } - namespacePrefix = newNamespacePrefix; - namespaceUri = newNamespaceUri; - - - if( ! allStringsInterned ) { - final int[] newNamespacePrefixHash = new int[newSize]; - if(namespacePrefixHash != null) { - System.arraycopy( - namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd); - } - namespacePrefixHash = newNamespacePrefixHash; - } - } - } - protected static int fastHash(char ch[], int off, int len ) { - if(len == 0) { - return 0; - } - int hash = ch[off]; - hash = (hash << 7) + ch[ off + len - 1 ]; - if(len > 16) { - hash = (hash << 7) + ch[ off + (len / 4)]; - } - if(len > 8) { - hash = (hash << 7) + ch[ off + (len / 2)]; - } - return hash; - } - protected int entityEnd; - - protected String entityName[]; - protected char[] entityNameBuf[]; - protected String entityReplacement[]; - protected char[] entityReplacementBuf[]; - - protected int entityNameHash[]; - - protected void ensureEntityCapacity() { - final int entitySize = entityReplacementBuf != null ? entityReplacementBuf.length : 0; - if(entityEnd >= entitySize) { - final int newSize = entityEnd > 7 ? 2 * entityEnd : 8; // = lucky 7 + 1 //25 - final String[] newEntityName = new String[newSize]; - final char[][] newEntityNameBuf = new char[newSize][]; - final String[] newEntityReplacement = new String[newSize]; - final char[][] newEntityReplacementBuf = new char[newSize][]; - if(entityName != null) { - System.arraycopy(entityName, 0, newEntityName, 0, entityEnd); - System.arraycopy(entityNameBuf, 0, newEntityNameBuf, 0, entityEnd); - System.arraycopy(entityReplacement, 0, newEntityReplacement, 0, entityEnd); - System.arraycopy(entityReplacementBuf, 0, newEntityReplacementBuf, 0, entityEnd); - } - entityName = newEntityName; - entityNameBuf = newEntityNameBuf; - entityReplacement = newEntityReplacement; - entityReplacementBuf = newEntityReplacementBuf; - - if( ! allStringsInterned ) { - final int[] newEntityNameHash = new int[newSize]; - if(entityNameHash != null) { - System.arraycopy(entityNameHash, 0, newEntityNameHash, 0, entityEnd); - } - entityNameHash = newEntityNameHash; - } - } - } - protected static final int READ_CHUNK_SIZE = 8*1024; - protected Reader reader; - protected String inputEncoding; - protected InputStream inputStream; - - - protected int bufLoadFactor = 95; - - protected char buf[] = new char[READ_CHUNK_SIZE]; - protected int bufSoftLimit = ( bufLoadFactor * buf.length ) /100; - protected boolean preventBufferCompaction; - - protected int bufAbsoluteStart; // this is buf - protected int bufStart; - protected int bufEnd; - protected int pos; - protected int posStart; - protected int posEnd; - - protected char[] pc = new char[ - Runtime.getRuntime().freeMemory() > 1000000L ? READ_CHUNK_SIZE : 64 ]; - protected int pcStart; - protected int pcEnd; - - protected boolean usePC; - - - protected boolean seenStartTag; - protected boolean seenEndTag; - protected boolean pastEndTag; - protected boolean seenAmpersand; - protected boolean seenMarkup; - protected boolean seenDocdecl; - - protected boolean tokenize; - protected String text; - protected String entityRefName; - - protected String xmlDeclVersion; - protected Boolean xmlDeclStandalone; - protected String xmlDeclContent; - - protected void reset() { - location = null; - lineNumber = 1; - columnNumber = 0; - seenRoot = false; - reachedEnd = false; - eventType = START_DOCUMENT; - emptyElementTag = false; - - depth = 0; - - attributeCount = 0; - - namespaceEnd = 0; - - entityEnd = 0; - - reader = null; - inputEncoding = null; - - preventBufferCompaction = false; - bufAbsoluteStart = 0; - bufEnd = bufStart = 0; - pos = posStart = posEnd = 0; - - pcEnd = pcStart = 0; - - usePC = false; - - seenStartTag = false; - seenEndTag = false; - pastEndTag = false; - seenAmpersand = false; - seenMarkup = false; - seenDocdecl = false; - - xmlDeclVersion = null; - xmlDeclStandalone = null; - xmlDeclContent = null; - - resetStringCache(); - } - - public MXParser() { - } - public void setFeature(String name, boolean state) throws XmlPullParserException - { - if(name == null) throw new IllegalArgumentException("feature name should not be null"); - if(FEATURE_PROCESS_NAMESPACES.equals(name)) { - if(eventType != START_DOCUMENT) throw new XmlPullParserException( - "namespace processing feature can only be changed before parsing", this, null); - processNamespaces = state; - } else if(FEATURE_NAMES_INTERNED.equals(name)) { - if(state != false) { - throw new XmlPullParserException( - "interning names in this implementation is not supported"); - } - } else if(FEATURE_PROCESS_DOCDECL.equals(name)) { - if(state != false) { - throw new XmlPullParserException( - "processing DOCDECL is not supported"); - } - } else if(FEATURE_XML_ROUNDTRIP.equals(name)) { - roundtripSupported = state; - } else { - throw new XmlPullParserException("unsupported feature "+name); - } - } - - public boolean getFeature(String name) - { - if(name == null) throw new IllegalArgumentException("feature name should not be null"); - if(FEATURE_PROCESS_NAMESPACES.equals(name)) { - return processNamespaces; - } else if(FEATURE_NAMES_INTERNED.equals(name)) { - return false; - } else if(FEATURE_PROCESS_DOCDECL.equals(name)) { - return false; - } else if(FEATURE_XML_ROUNDTRIP.equals(name)) { - return roundtripSupported; - } - return false; - } - - public void setProperty(String name, - Object value) - throws XmlPullParserException - { - if(PROPERTY_LOCATION.equals(name)) { - location = (String) value; - } else { - throw new XmlPullParserException("unsupported property: '"+name+"'"); - } - } - - - public Object getProperty(String name) - { - if(name == null) throw new IllegalArgumentException("property name should not be null"); - if(PROPERTY_XMLDECL_VERSION.equals(name)) { - return xmlDeclVersion; - } else if(PROPERTY_XMLDECL_STANDALONE.equals(name)) { - return xmlDeclStandalone; - } else if(PROPERTY_XMLDECL_CONTENT.equals(name)) { - return xmlDeclContent; - } else if(PROPERTY_LOCATION.equals(name)) { - return location; - } - return null; - } - - - public void setInput(Reader in) throws XmlPullParserException - { - reset(); - reader = in; - } - - public void setInput(java.io.InputStream inputStream, String inputEncoding) - throws XmlPullParserException - { - if(inputStream == null) { - throw new IllegalArgumentException("input stream can not be null"); - } - this.inputStream = inputStream; - Reader reader; - try { - if(inputEncoding != null) { - reader = new InputStreamReader(inputStream, inputEncoding); - } else { - reader = new InputStreamReader(inputStream, "UTF-8"); - } - } catch (UnsupportedEncodingException une) { - throw new XmlPullParserException( - "could not create reader for encoding "+inputEncoding+" : "+une, this, une); - } - setInput(reader); - this.inputEncoding = inputEncoding; - } - public InputStream getInputStream(){ - return inputStream; - } - public Reader getReader(){ - reset(); - return reader; - } - - public String getInputEncoding() { - return inputEncoding; - } - - public void defineEntityReplacementText(String entityName, - String replacementText) - throws XmlPullParserException - { - ensureEntityCapacity(); - - // this is to make sure that if interning works we will take advantage of it ... - this.entityName[entityEnd] = newString(entityName.toCharArray(), 0, entityName.length()); - entityNameBuf[entityEnd] = entityName.toCharArray(); - - entityReplacement[entityEnd] = replacementText; - entityReplacementBuf[entityEnd] = replacementText.toCharArray(); - if(!allStringsInterned) { - entityNameHash[ entityEnd ] = - fastHash(entityNameBuf[entityEnd], 0, entityNameBuf[entityEnd].length); - } - ++entityEnd; - } - - public int getNamespaceCount(int depth) throws XmlPullParserException - { - if(!processNamespaces || depth == 0) { - return 0; - } - if(depth < 0 || depth > this.depth) { - throw new IllegalArgumentException( - "allowed namespace depth 0.."+this.depth+" not "+depth); - } - return elNamespaceCount[ depth ]; - } - - public String getNamespacePrefix(int pos) - throws XmlPullParserException - { - if(pos < namespaceEnd) { - return namespacePrefix[ pos ]; - } else { - throw new XmlPullParserException( - "position "+pos+" exceeded number of available namespaces "+namespaceEnd); - } - } - - public String getNamespaceUri(int pos) throws XmlPullParserException - { - if(pos < namespaceEnd) { - return namespaceUri[ pos ]; - } else { - throw new XmlPullParserException( - "position "+pos+" exceeded number of available namespaces "+namespaceEnd); - } - } - - public String getNamespace( String prefix ) - { - if(prefix != null) { - for( int i = namespaceEnd -1; i >= 0; i--) { - if( prefix.equals( namespacePrefix[ i ] ) ) { - return namespaceUri[ i ]; - } - } - if("xml".equals( prefix )) { - return XML_URI; - } else if("xmlns".equals( prefix )) { - return XMLNS_URI; - } - } else { - for( int i = namespaceEnd -1; i >= 0; i--) { - if( namespacePrefix[ i ] == null) { //"") { //null ) { //TODO check FIXME Alek - return namespaceUri[ i ]; - } - } - - } - return null; - } - - - public int getDepth() - { - return depth; - } - - - private static int findFragment(int bufMinPos, char[] b, int start, int end) { - if(start < bufMinPos) { - start = bufMinPos; - if(start > end) start = end; - return start; - } - if(end - start > 65) { - start = end - 10; // try to find good location - } - int i = start + 1; - while(--i > bufMinPos) { - if((end - i) > 65) break; - final char c = b[i]; - if(c == '<' && (start - i) > 10) break; - } - return i; - } - @Override - public String getPositionDescription() - { - return "line="+getLineNumber()+", col="+getColumnNumber(); - } - @Override - public int getLineNumber() - { - return lineNumber; - } - @Override - public int getColumnNumber() - { - return columnNumber; - } - - - public boolean isWhitespace() throws XmlPullParserException - { - if(eventType == TEXT || eventType == CDSECT) { - if(usePC) { - for (int i = pcStart; i = attributeCount) throw new IndexOutOfBoundsException( - "attribute position must be 0.."+(attributeCount-1)+" and not "+index); - return attributeUri[ index ]; - } - - public String getAttributeName(int index) - { - if(eventType != START_TAG) throw new IndexOutOfBoundsException( - "only START_TAG can have attributes"); - if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException( - "attribute position must be 0.."+(attributeCount-1)+" and not "+index); - return attributeName[ index ]; - } - - public String getAttributePrefix(int index) - { - if(eventType != START_TAG) throw new IndexOutOfBoundsException( - "only START_TAG can have attributes"); - if(processNamespaces == false) return null; - if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException( - "attribute position must be 0.."+(attributeCount-1)+" and not "+index); - return attributePrefix[ index ]; - } - - public String getAttributeType(int index) { - if(eventType != START_TAG) throw new IndexOutOfBoundsException( - "only START_TAG can have attributes"); - if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException( - "attribute position must be 0.."+(attributeCount-1)+" and not "+index); - return "CDATA"; - } - - public boolean isAttributeDefault(int index) { - if(eventType != START_TAG) throw new IndexOutOfBoundsException( - "only START_TAG can have attributes"); - if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException( - "attribute position must be 0.."+(attributeCount-1)+" and not "+index); - return false; - } - - public String getAttributeValue(int index) - { - if(eventType != START_TAG) throw new IndexOutOfBoundsException( - "only START_TAG can have attributes"); - if(index < 0 || index >= attributeCount) throw new IndexOutOfBoundsException( - "attribute position must be 0.."+(attributeCount-1)+" and not "+index); - return attributeValue[ index ]; - } - - @Override - public String getAttributeValue(String namespace, String name) - { - if(eventType != START_TAG) { - throw new IndexOutOfBoundsException("only START_TAG can have attributes " - +getPositionDescription()); - } - if(name == null) { - throw new IllegalArgumentException("attribute name can not be null"); - } - // TODO make check if namespace is interned!!! etc. for names!!! - if(processNamespaces) { - if(namespace == null) { - namespace = ""; - } - - for(int i = 0; i < attributeCount; ++i) { - if((namespace == attributeUri[ i ] || - namespace.equals(attributeUri[ i ]) ) - //(namespace != null && namespace.equals(attributeUri[ i ])) - // taking advantage of String.intern() - && name.equals(attributeName[ i ]) ) - { - return attributeValue[i]; - } - } - } else { - if(namespace != null && namespace.length() == 0) { - namespace = null; - } - if(namespace != null) throw new IllegalArgumentException( - "when namespaces processing is disabled attribute namespace must be null"); - for(int i = 0; i < attributeCount; ++i) { - if(name.equals(attributeName[i])) - { - return attributeValue[i]; - } - } - } - return null; - } - - - public int getEventType() - throws XmlPullParserException - { - return eventType; - } - - public void require(int type, String namespace, String name) - throws XmlPullParserException, IOException - { - if(processNamespaces == false && namespace != null) { - throw new XmlPullParserException( - "processing namespaces must be enabled on parser (or factory)"+ - " to have possible namespaces declared on elements" - +(" (position:"+ getPositionDescription())+")"); - } - if (type != getEventType() - || (namespace != null && !namespace.equals (getNamespace())) - || (name != null && !name.equals (getName ())) ) - { - throw new XmlPullParserException ( - "expected event "+TYPES[ type ] - +(name != null ? " with name '"+name+"'" : "") - +(namespace != null && name != null ? " and" : "") - +(namespace != null ? " with namespace '"+namespace+"'" : "") - +" but got" - +(type != getEventType() ? " "+TYPES[ getEventType() ] : "") - +(name != null && getName() != null && !name.equals (getName ()) - ? " name '"+getName()+"'" : "") - +(namespace != null && name != null - && getName() != null && !name.equals (getName ()) - && getNamespace() != null && !namespace.equals (getNamespace()) - ? " and" : "") - +(namespace != null && getNamespace() != null && !namespace.equals (getNamespace()) - ? " namespace '"+getNamespace()+"'" : "") - +(" (position:"+ getPositionDescription())+")"); - } - } - - public void skipSubTree() - throws XmlPullParserException, IOException - { - require(START_TAG, null, null); - int level = 1; - while(level > 0) { - int eventType = next(); - if(eventType == END_TAG) { - --level; - } else if(eventType == START_TAG) { - ++level; - } - } - } - - 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) { - final String result = getText(); - eventType = next(); - if(eventType != END_TAG) { - throw new XmlPullParserException( - "TEXT must be immediately followed by END_TAG and not " - +TYPES[ getEventType() ], 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 int nextTag() throws XmlPullParserException, IOException - { - next(); - if(eventType == TEXT && isWhitespace()) { // skip whitespace - next(); - } - if (eventType != START_TAG && eventType != END_TAG) { - throw new XmlPullParserException("expected START_TAG or END_TAG not " - +TYPES[ getEventType() ], this, null); - } - return eventType; - } - - public int next() - throws XmlPullParserException, IOException - { - tokenize = false; - return nextImpl(); - } - - public int nextToken() - throws XmlPullParserException, IOException - { - tokenize = true; - return nextImpl(); - } - - - protected int nextImpl() - throws XmlPullParserException, IOException - { - text = null; - pcEnd = pcStart = 0; - usePC = false; - bufStart = posEnd; - if(pastEndTag) { - pastEndTag = false; - --depth; - namespaceEnd = elNamespaceCount[ depth ]; // less namespaces available - } - if(emptyElementTag) { - emptyElementTag = false; - pastEndTag = true; - return eventType = END_TAG; - } - if(depth > 0) { - - if(seenStartTag) { - seenStartTag = false; - return eventType = parseStartTag(); - } - if(seenEndTag) { - seenEndTag = false; - return eventType = parseEndTag(); - } - - char ch; - if(seenMarkup) { // we have read ahead ... - seenMarkup = false; - ch = '<'; - } else if(seenAmpersand) { - seenAmpersand = false; - ch = '&'; - } else { - ch = more(); - } - posStart = pos - 1; // VERY IMPORTANT: this is correct start of event!!! - - // when true there is some potential event TEXT to return - keep gathering - boolean hadCharData = false; - - // when true TEXT data is not continual (like ) and requires PC merging - boolean needsMerging = false; - - MAIN_LOOP: - while(true) { - // work on MARKUP - if(ch == '<') { - if(hadCharData) { - //posEnd = pos - 1; - if(tokenize) { - seenMarkup = true; - return eventType = TEXT; - } - } - ch = more(); - if(ch == '/') { - if(!tokenize && hadCharData) { - seenEndTag = true; - //posEnd = pos - 2; - return eventType = TEXT; - } - return eventType = parseEndTag(); - } else if(ch == '!') { - ch = more(); - if(ch == '-') { - // note: if(tokenize == false) posStart/End is NOT changed!!!! - parseComment(); - if(tokenize) return eventType = COMMENT; - if( !usePC && hadCharData ) { - needsMerging = true; - } else { - posStart = pos; //completely ignore comment - } - } else if(ch == '[') { - parseCDSect(hadCharData); - if(tokenize) return eventType = CDSECT; - final int cdStart = posStart; - final int cdEnd = posEnd; - final int cdLen = cdEnd - cdStart; - - - if(cdLen > 0) { // was there anything inside CDATA section? - hadCharData = true; - if(!usePC) { - needsMerging = true; - } - } - } else { - throw new XmlPullParserException( - "unexpected character in markup "+printable(ch), this, null); - } - } else if(ch == '?') { - parsePI(); - if(tokenize) return eventType = PROCESSING_INSTRUCTION; - if( !usePC && hadCharData ) { - needsMerging = true; - } else { - posStart = pos; //completely ignore PI - } - - } else if( isNameStartChar(ch) ) { - if(!tokenize && hadCharData) { - seenStartTag = true; - //posEnd = pos - 2; - return eventType = TEXT; - } - return eventType = parseStartTag(); - } else { - throw new XmlPullParserException( - "unexpected character in markup "+printable(ch), this, null); - } - - } else if(ch == '&') { - // work on ENTITTY - //posEnd = pos - 1; - if(tokenize && hadCharData) { - seenAmpersand = true; - return eventType = TEXT; - } - final int oldStart = posStart + bufAbsoluteStart; - final int oldEnd = posEnd + bufAbsoluteStart; - final char[] resolvedEntity = parseEntityRef(); - if(tokenize) return eventType = ENTITY_REF; - // check if replacement text can be resolved !!! - if(resolvedEntity == null) { - if(entityRefName == null) { - entityRefName = newString(buf, posStart, posEnd - posStart); - } - throw new XmlPullParserException( - "could not resolve entity named '"+printable(entityRefName)+"'", - this, null); - } - //int entStart = posStart; - //int entEnd = posEnd; - posStart = oldStart - bufAbsoluteStart; - posEnd = oldEnd - bufAbsoluteStart; - if(!usePC) { - if(hadCharData) { - joinPC(); // posEnd is already set correctly!!! - needsMerging = false; - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - for (int i = 0; i < resolvedEntity.length; i++) - { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = resolvedEntity[ i ]; - - } - hadCharData = true; - } else { - - if(needsMerging) { - joinPC(); - needsMerging = false; - } - - hadCharData = true; - - boolean normalizedCR = false; - final boolean normalizeInput = tokenize == false || roundtripSupported == false; - - boolean seenBracket = false; - boolean seenBracketBracket = false; - do { - if(ch == ']') { - if(seenBracket) { - seenBracketBracket = true; - } else { - seenBracket = true; - } - } else if(seenBracketBracket && ch == '>') { - throw new XmlPullParserException( - "characters ]]> are not allowed in content", this, null); - } else { - if(seenBracket) { - seenBracketBracket = seenBracket = false; - } - // assert seenTwoBrackets == seenBracket == false; - } - if(normalizeInput) { - // deal with normalization issues ... - if(ch == '\r') { - normalizedCR = true; - posEnd = pos -1; - // posEnd is already is set - if(!usePC) { - if(posEnd > posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - // if(!usePC) { joinPC(); } else { if(pcEnd >= pc.length) ensurePC(); } - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - - ch = more(); - } while(ch != '<' && ch != '&'); - posEnd = pos - 1; - continue MAIN_LOOP; - } - ch = more(); - } // endless while(true) - } else { - if(seenRoot) { - return parseEpilog(); - } else { - return parseProlog(); - } - } - } - - - protected int parseProlog() - throws XmlPullParserException, IOException - { - char ch; - if(seenMarkup) { - ch = buf[ pos - 1 ]; - } else { - ch = more(); - } - - if(eventType == START_DOCUMENT) { - if(ch == '\uFFFE') { - throw new XmlPullParserException( - "first character in input was UNICODE noncharacter (0xFFFE)"+ - "- input requires int swapping", this, null); - } - if(ch == '\uFEFF') { - ch = more(); - } - } - seenMarkup = false; - boolean gotS = false; - posStart = pos - 1; - final boolean normalizeIgnorableWS = tokenize == true && roundtripSupported == false; - boolean normalizedCR = false; - while(true) { - if(ch == '<') { - if(gotS && tokenize) { - posEnd = pos - 1; - seenMarkup = true; - return eventType = IGNORABLE_WHITESPACE; - } - ch = more(); - if(ch == '?') { - if(parsePI()) { // make sure to skip XMLDecl - if(tokenize) { - return eventType = PROCESSING_INSTRUCTION; - } - } else { - // skip over - continue tokenizing - posStart = pos; - gotS = false; - } - - } else if(ch == '!') { - ch = more(); - if(ch == 'D') { - if(seenDocdecl) { - throw new XmlPullParserException( - "only one docdecl allowed in XML document", this, null); - } - seenDocdecl = true; - parseDocdecl(); - if(tokenize) return eventType = DOCDECL; - } else if(ch == '-') { - parseComment(); - if(tokenize) return eventType = COMMENT; - } else { - throw new XmlPullParserException( - "unexpected markup posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - } else { - throw new XmlPullParserException( - "only whitespace content allowed before start tag and not "+printable(ch), - this, null); - } - ch = more(); - } - } - - protected int parseEpilog() - throws XmlPullParserException, IOException - { - if(eventType == END_DOCUMENT) { - throw new XmlPullParserException("already reached end of XML input", this, null); - } - if(reachedEnd) { - return eventType = END_DOCUMENT; - } - boolean gotS = false; - final boolean normalizeIgnorableWS = tokenize && !roundtripSupported; - boolean normalizedCR = false; - try { - char ch; - if(seenMarkup) { - ch = buf[ pos - 1 ]; - } else { - ch = more(); - } - seenMarkup = false; - posStart = pos - 1; - if(!reachedEnd) { - while(true) { - if(ch == '<') { - if(gotS && tokenize) { - posEnd = pos - 1; - seenMarkup = true; - return eventType = IGNORABLE_WHITESPACE; - } - ch = more(); - if(reachedEnd) { - break; - } - if(ch == '?') { - parsePI(); - if(tokenize) return eventType = PROCESSING_INSTRUCTION; - - } else if(ch == '!') { - ch = more(); - if(reachedEnd) { - break; - } - if(ch == 'D') { - parseDocdecl(); - if(tokenize) { - return eventType = DOCDECL; - } - } else if(ch == '-') { - parseComment(); - if(tokenize) return eventType = COMMENT; - } else { - throw new XmlPullParserException( - "unexpected markup posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - } else { - throw new XmlPullParserException( - "in epilog non whitespace content is not allowed but got "+printable(ch), - this, null); - } - ch = more(); - if(reachedEnd) { - break; - } - - } - } - } catch(EOFException ex) { - reachedEnd = true; - } - if(reachedEnd) { - if(tokenize && gotS) { - posEnd = pos; // well - this is LAST available character pos - return eventType = IGNORABLE_WHITESPACE; - } - return eventType = END_DOCUMENT; - } else { - throw new XmlPullParserException("internal error in parseEpilog"); - } - } - - - public int parseEndTag() throws XmlPullParserException, IOException { - char ch = more(); - if(!isNameStartChar(ch)) { - throw new XmlPullParserException( - "expected name start and not "+printable(ch), this, null); - } - posStart = pos - 3; - final int nameStart = pos - 1 + bufAbsoluteStart; - do { - ch = more(); - } while(isNameChar(ch)); - - int off = nameStart - bufAbsoluteStart; - final int len = (pos - 1) - off; - final char[] cbuf = elRawName[depth]; - if(elRawNameEnd[depth] != len) { - // construct strings for exception - final String startname = new String(cbuf, 0, elRawNameEnd[depth]); - final String endname = new String(buf, off, len); - throw new XmlPullParserException( - "end tag name must match start tag name <"+startname+">" - +" from line "+elRawNameLine[depth], this, null); - } - for (int i = 0; i < len; i++) - { - if(buf[off++] != cbuf[i]) { - // construct strings for exception - final String startname = new String(cbuf, 0, len); - final String endname = new String(buf, off - i - 1, len); - throw new XmlPullParserException( - "end tag name must be the same as start tag <"+startname+">" - +" from line "+elRawNameLine[depth], this, null); - } - } - - while(isS(ch)) { - ch = more(); - } // skip additional white spaces - if(ch != '>') { - throw new XmlPullParserException( - "expected > to finish end tag not "+printable(ch) - +" from line "+elRawNameLine[depth], this, null); - } - posEnd = pos; - pastEndTag = true; - return eventType = END_TAG; - } - - public int parseStartTag() throws XmlPullParserException, IOException { - ++depth; - posStart = pos - 2; - emptyElementTag = false; - attributeCount = 0; - final int nameStart = pos - 1 + bufAbsoluteStart; - int colonPos = -1; - char ch = buf[ pos - 1]; - if(ch == ':' && processNamespaces) throw new XmlPullParserException( - "when namespaces processing enabled colon can not be at element name start", - this, null); - while(true) { - ch = more(); - if(!isNameChar(ch)) break; - if(ch == ':' && processNamespaces) { - if(colonPos != -1) throw new XmlPullParserException( - "only one colon is allowed in name of element when namespaces are enabled", - this, null); - colonPos = pos - 1 + bufAbsoluteStart; - } - } - ensureElementsCapacity(); - int elLen = (pos - 1) - (nameStart - bufAbsoluteStart); - if(elRawName[ depth ] == null || elRawName[ depth ].length < elLen) { - elRawName[ depth ] = new char[ 2 * elLen ]; - } - System.arraycopy(buf, nameStart - bufAbsoluteStart, elRawName[ depth ], 0, elLen); - elRawNameEnd[ depth ] = elLen; - elRawNameLine[ depth ] = lineNumber; - - String name = null; - - // work on prefixes and namespace URI - String prefix = null; - if(processNamespaces) { - if(colonPos != -1) { - prefix = elPrefix[ depth ] = newString(buf, nameStart - bufAbsoluteStart, - colonPos - nameStart); - name = elName[ depth ] = newString(buf, colonPos + 1 - bufAbsoluteStart, - //(pos -1) - (colonPos + 1)); - pos - 2 - (colonPos - bufAbsoluteStart)); - } else { - prefix = elPrefix[ depth ] = null; - name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen); - } - } else { - name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen); - } - - - while(true) { - while(isS(ch)) { - ch = more(); - } - - if(ch == '>') { - break; - } else if(ch == '/') { - if(emptyElementTag) throw new XmlPullParserException( - "repeated / in tag declaration", this, null); - emptyElementTag = true; - ch = more(); - if(ch != '>') throw new XmlPullParserException( - "expected > to end empty tag not "+printable(ch), this, null); - break; - } else if(isNameStartChar(ch)) { - ch = parseAttribute(); - ch = more(); - continue; - } else { - throw new XmlPullParserException( - "start tag unexpected character "+printable(ch), this, null); - } - } - - // now when namespaces were declared we can resolve them - if(processNamespaces) { - String uri = getNamespace(prefix); - if(uri == null) { - if(prefix == null) { // no prefix and no uri => use default namespace - uri = NO_NAMESPACE; - } else { - throw new XmlPullParserException( - "could not determine namespace bound to element prefix "+prefix, - this, null); - } - - } - elUri[ depth ] = uri; - - for (int i = 0; i < attributeCount; i++) - { - final String attrPrefix = attributePrefix[ i ]; - if(attrPrefix != null) { - final String attrUri = getNamespace(attrPrefix); - if(attrUri == null) { - throw new XmlPullParserException( - "could not determine namespace bound to attribute prefix "+attrPrefix, - this, null); - - } - attributeUri[ i ] = attrUri; - } else { - attributeUri[ i ] = NO_NAMESPACE; - } - } - - for (int i = 1; i < attributeCount; i++) - { - for (int j = 0; j < i; j++) - { - if( attributeUri[j] == attributeUri[i] - && (allStringsInterned && attributeName[j].equals(attributeName[i]) - || (!allStringsInterned - && attributeNameHash[ j ] == attributeNameHash[ i ] - && attributeName[j].equals(attributeName[i])) ) - - ) { - // prepare data for nice error message? - String attr1 = attributeName[j]; - if(attributeUri[j] != null) attr1 = attributeUri[j]+":"+attr1; - String attr2 = attributeName[i]; - if(attributeUri[i] != null) attr2 = attributeUri[i]+":"+attr2; - throw new XmlPullParserException( - "duplicated attributes "+attr1+" and "+attr2, this, null); - } - } - } - - - } else { - for (int i = 1; i < attributeCount; i++) - { - for (int j = 0; j < i; j++) - { - if((allStringsInterned && attributeName[j].equals(attributeName[i]) - || (!allStringsInterned - && attributeNameHash[ j ] == attributeNameHash[ i ] - && attributeName[j].equals(attributeName[i])) ) - - ) { - // prepare data for nice error message? - final String attr1 = attributeName[j]; - final String attr2 = attributeName[i]; - throw new XmlPullParserException( - "duplicated attributes "+attr1+" and "+attr2, this, null); - } - } - } - } - - elNamespaceCount[ depth ] = namespaceEnd; - posEnd = pos; - return eventType = START_TAG; - } - - protected char parseAttribute() throws XmlPullParserException, IOException - { - final int prevPosStart = posStart + bufAbsoluteStart; - final int nameStart = pos - 1 + bufAbsoluteStart; - int colonPos = -1; - char ch = buf[ pos - 1 ]; - if(ch == ':' && processNamespaces) throw new XmlPullParserException( - "when namespaces processing enabled colon can not be at attribute name start", - this, null); - - - boolean startsWithXmlns = processNamespaces && ch == 'x'; - int xmlnsPos = 0; - - ch = more(); - while(isNameChar(ch)) { - if(processNamespaces) { - if(startsWithXmlns && xmlnsPos < 5) { - ++xmlnsPos; - if(xmlnsPos == 1) { if(ch != 'm') startsWithXmlns = false; } - else if(xmlnsPos == 2) { if(ch != 'l') startsWithXmlns = false; } - else if(xmlnsPos == 3) { if(ch != 'n') startsWithXmlns = false; } - else if(xmlnsPos == 4) { if(ch != 's') startsWithXmlns = false; } - else if(xmlnsPos == 5) { - if(ch != ':') throw new XmlPullParserException( - "after xmlns in attribute name must be colon" - +"when namespaces are enabled", this, null); - //colonPos = pos - 1 + bufAbsoluteStart; - } - } - if(ch == ':') { - if(colonPos != -1) throw new XmlPullParserException( - "only one colon is allowed in attribute name" - +" when namespaces are enabled", this, null); - colonPos = pos - 1 + bufAbsoluteStart; - } - } - ch = more(); - } - - ensureAttributesCapacity(attributeCount); - - String name = null; - String prefix = null; - if(processNamespaces) { - if(xmlnsPos < 4) startsWithXmlns = false; - if(startsWithXmlns) { - if(colonPos != -1) { - //prefix = attributePrefix[ attributeCount ] = null; - final int nameLen = pos - 2 - (colonPos - bufAbsoluteStart); - if(nameLen == 0) { - throw new XmlPullParserException( - "namespace prefix is required after xmlns: " - +" when namespaces are enabled", this, null); - } - name = //attributeName[ attributeCount ] = - newString(buf, colonPos - bufAbsoluteStart + 1, nameLen); - } - } else { - if(colonPos != -1) { - int prefixLen = colonPos - nameStart; - prefix = attributePrefix[ attributeCount ] = - newString(buf, nameStart - bufAbsoluteStart,prefixLen); - //colonPos - (nameStart - bufAbsoluteStart)); - int nameLen = pos - 2 - (colonPos - bufAbsoluteStart); - name = attributeName[ attributeCount ] = - newString(buf, colonPos - bufAbsoluteStart + 1, nameLen); - } else { - prefix = attributePrefix[ attributeCount ] = null; - name = attributeName[ attributeCount ] = - newString(buf, nameStart - bufAbsoluteStart, - pos - 1 - (nameStart - bufAbsoluteStart)); - } - if(!allStringsInterned) { - attributeNameHash[ attributeCount ] = name.hashCode(); - } - } - - } else { - name = attributeName[ attributeCount ] = - newString(buf, nameStart - bufAbsoluteStart, - pos - 1 - (nameStart - bufAbsoluteStart)); - if(!allStringsInterned) { - attributeNameHash[ attributeCount ] = name.hashCode(); - } - } - - while(isS(ch)) { - ch = more(); - } // skip additional spaces - if(ch != '=') { - throw new XmlPullParserException( - "expected = after attribute name '"+name+processNamespaces+"'", this, null); - } - ch = more(); - while(isS(ch)) { - ch = more(); - } // skip additional spaces - - final char delimit = ch; - if(delimit != '"' && delimit != '\'') throw new XmlPullParserException( - "attribute value must start with quotation or apostrophe not " - +printable(delimit), this, null); - boolean normalizedCR = false; - usePC = false; - pcStart = pcEnd; - posStart = pos; - - while(true) { - ch = more(); - if(ch == delimit) { - break; - } if(ch == '<') { - throw new XmlPullParserException( - "markup not allowed inside attribute value - illegal < ", this, null); - } if(ch == '&') { - posEnd = pos - 1; - if(!usePC) { - final boolean hadCharData = posEnd > posStart; - if(hadCharData) { - // posEnd is already set correctly!!! - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - final char[] resolvedEntity = parseEntityRef(); - if(resolvedEntity == null) { - if(entityRefName == null) { - entityRefName = newString(buf, posStart, posEnd - posStart); - } - throw new XmlPullParserException( - "could not resolve entity named '"+printable(entityRefName)+"'", - this, null); - } - for (int i = 0; i < resolvedEntity.length; i++) - { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = resolvedEntity[ i ]; - } - } else if(ch == '\t' || ch == '\n' || ch == '\r') { - if(!usePC) { - posEnd = pos - 1; - if(posEnd > posStart) { - joinPC(); - } else { - usePC = true; - pcEnd = pcStart = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - if(ch != '\n' || !normalizedCR) { - pc[pcEnd++] = ' '; //'\n'; - } - - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - } - normalizedCR = ch == '\r'; - } - - - if(processNamespaces && startsWithXmlns) { - String ns = null; - if(!usePC) { - ns = newStringIntern(buf, posStart, pos - 1 - posStart); - } else { - ns = newStringIntern(pc, pcStart, pcEnd - pcStart); - } - ensureNamespacesCapacity(namespaceEnd); - int prefixHash = -1; - if(colonPos != -1) { - if(ns.length() == 0) { - throw new XmlPullParserException( - "non-default namespace can not be declared to be empty string", this, null); - } - // declare new namespace - namespacePrefix[ namespaceEnd ] = name; - if(!allStringsInterned) { - prefixHash = namespacePrefixHash[ namespaceEnd ] = name.hashCode(); - } - } else { - // declare new default namespace ... - namespacePrefix[ namespaceEnd ] = null; - if(!allStringsInterned) { - prefixHash = namespacePrefixHash[ namespaceEnd ] = -1; - } - } - namespaceUri[ namespaceEnd ] = ns; - final int startNs = elNamespaceCount[ depth - 1 ]; - for (int i = namespaceEnd - 1; i >= startNs; --i) - { - if(((allStringsInterned || name == null) && namespacePrefix[ i ] == name) - || (!allStringsInterned && name != null && - namespacePrefixHash[ i ] == prefixHash - && name.equals(namespacePrefix[ i ]) - )) - { - final String s = name == null ? "default" : "'"+name+"'"; - throw new XmlPullParserException( - "duplicated namespace declaration for "+s+" prefix", this, null); - } - } - - ++namespaceEnd; - - } else { - if(!usePC) { - attributeValue[ attributeCount ] = - new String(buf, posStart, pos - 1 - posStart); - } else { - attributeValue[ attributeCount ] = - new String(pc, pcStart, pcEnd - pcStart); - } - ++attributeCount; - } - posStart = prevPosStart - bufAbsoluteStart; - return ch; - } - - protected char[] charRefOneCharBuf = new char[1]; - - protected char[] parseEntityRef() - throws XmlPullParserException, IOException - { - entityRefName = null; - posStart = pos; - char ch = more(); - if(ch == '#') { - // parse character reference - char charRef = 0; - ch = more(); - if(ch == 'x') { - //encoded in hex - while(true) { - ch = more(); - if(ch >= '0' && ch <= '9') { - charRef = (char)(charRef * 16 + (ch - '0')); - } else if(ch >= 'a' && ch <= 'f') { - charRef = (char)(charRef * 16 + (ch - ('a' - 10))); - } else if(ch >= 'A' && ch <= 'F') { - charRef = (char)(charRef * 16 + (ch - ('A' - 10))); - } else if(ch == ';') { - break; - } else { - throw new XmlPullParserException( - "character reference (with hex value) may not contain " - +printable(ch), this, null); - } - } - } else { - // encoded in decimal - while(true) { - if(ch >= '0' && ch <= '9') { - charRef = (char)(charRef * 10 + (ch - '0')); - } else if(ch == ';') { - break; - } else { - throw new XmlPullParserException( - "character reference (with decimal value) may not contain " - +printable(ch), this, null); - } - ch = more(); - } - } - posEnd = pos - 1; - charRefOneCharBuf[0] = charRef; - if(tokenize) { - text = newString(charRefOneCharBuf, 0, 1); - } - return charRefOneCharBuf; - } else { - // [68] EntityRef ::= '&' Name ';' - // scan name until ; - if(!isNameStartChar(ch)) { - throw new XmlPullParserException( - "entity reference names can not start with character '" - +printable(ch)+"'", this, null); - } - while(true) { - ch = more(); - if(ch == ';') { - break; - } - if(!isNameChar(ch)) { - throw new XmlPullParserException( - "entity reference name can not contain character " - +printable(ch)+"'", this, null); - } - } - posEnd = pos - 1; - // determine what name maps to - final int len = posEnd - posStart; - if(len == 2 && buf[posStart] == 'l' && buf[posStart+1] == 't') { - if(tokenize) { - text = "<"; - } - charRefOneCharBuf[0] = '<'; - return charRefOneCharBuf; - } else if(len == 3 && buf[posStart] == 'a' - && buf[posStart+1] == 'm' && buf[posStart+2] == 'p') { - if(tokenize) { - text = "&"; - } - charRefOneCharBuf[0] = '&'; - return charRefOneCharBuf; - } else if(len == 2 && buf[posStart] == 'g' && buf[posStart+1] == 't') { - if(tokenize) { - text = ">"; - } - charRefOneCharBuf[0] = '>'; - return charRefOneCharBuf; - } else if(len == 4 && buf[posStart] == 'a' && buf[posStart+1] == 'p' - && buf[posStart+2] == 'o' && buf[posStart+3] == 's') - { - if(tokenize) { - text = "'"; - } - charRefOneCharBuf[0] = '\''; - return charRefOneCharBuf; - } else if(len == 4 && buf[posStart] == 'q' && buf[posStart+1] == 'u' - && buf[posStart+2] == 'o' && buf[posStart+3] == 't') - { - if(tokenize) { - text = "\""; - } - charRefOneCharBuf[0] = '"'; - return charRefOneCharBuf; - } else { - final char[] result = lookuEntityReplacement(len); - if(result != null) { - return result; - } - } - if(tokenize) text = null; - return null; - } - } - - protected char[] lookuEntityReplacement(int entitNameLen) - throws XmlPullParserException, IOException - - { - if(!allStringsInterned) { - final int hash = fastHash(buf, posStart, posEnd - posStart); - LOOP: - for (int i = entityEnd - 1; i >= 0; --i) - { - if(hash == entityNameHash[ i ] && entitNameLen == entityNameBuf[ i ].length) { - final char[] entityBuf = entityNameBuf[ i ]; - for (int j = 0; j < entitNameLen; j++) - { - if(buf[posStart + j] != entityBuf[j]) continue LOOP; - } - if(tokenize) text = entityReplacement[ i ]; - return entityReplacementBuf[ i ]; - } - } - } else { - entityRefName = newString(buf, posStart, posEnd - posStart); - for (int i = entityEnd - 1; i >= 0; --i) - { - // take advantage that interning for newStirng is enforced - if(entityRefName == entityName[ i ]) { - if(tokenize) text = entityReplacement[ i ]; - return entityReplacementBuf[ i ]; - } - } - } - return null; - } - - - protected void parseComment() - throws XmlPullParserException, IOException - { - // implements XML 1.0 Section 2.5 Comments - - //ASSUMPTION: seen - ch = more(); - if(seenDashDash && ch != '>') { - throw new XmlPullParserException( - "in comment after two dashes (--) next character must be >" - +" not "+printable(ch), this, null); - } - if(ch == '-') { - if(!seenDash) { - seenDash = true; - } else { - seenDashDash = true; - seenDash = false; - } - } else if(ch == '>') { - if(seenDashDash) { - break; // found end sequence!!!! - } else { - seenDashDash = false; - } - seenDash = false; - } else { - seenDash = false; - } - if(normalizeIgnorableWS) { - if(ch == '\r') { - normalizedCR = true; - //posEnd = pos -1; - //joinPC(); - // posEnd is already set - if(!usePC) { - posEnd = pos -1; - if(posEnd > posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - } - - } catch(EOFException ex) { - throw new XmlPullParserException( - "comment started on line "+curLine+" and column "+curColumn+" was not closed", - this, ex); - } - if(tokenize) { - posEnd = pos - 3; - if(usePC) { - pcEnd -= 2; - } - } - } - - protected boolean parsePI() - throws XmlPullParserException, IOException - { - if(tokenize) posStart = pos; - final int curLine = lineNumber; - final int curColumn = columnNumber; - int piTargetStart = pos + bufAbsoluteStart; - int piTargetEnd = -1; - final boolean normalizeIgnorableWS = tokenize == true && roundtripSupported == false; - boolean normalizedCR = false; - - try { - boolean seenQ = false; - char ch = more(); - if(isS(ch)) { - throw new XmlPullParserException( - "processing instruction PITarget must be exactly after - //ch = more(); - - if(ch == '?') { - seenQ = true; - } else if(ch == '>') { - if(seenQ) { - break; // found end sequence!!!! - } - seenQ = false; - } else { - if(piTargetEnd == -1 && isS(ch)) { - piTargetEnd = pos - 1 + bufAbsoluteStart; - - // [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) - if((piTargetEnd - piTargetStart) == 3) { - if((buf[piTargetStart] == 'x' || buf[piTargetStart] == 'X') - && (buf[piTargetStart+1] == 'm' || buf[piTargetStart+1] == 'M') - && (buf[piTargetStart+2] == 'l' || buf[piTargetStart+2] == 'L') - ) - { - if(piTargetStart > 3) { // posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - ch = more(); - } - } catch(EOFException ex) { - throw new XmlPullParserException( - "processing instruction started on line "+curLine+" and column "+curColumn - +" was not closed", - this, ex); - } - if(piTargetEnd == -1) { - piTargetEnd = pos - 2 + bufAbsoluteStart; - } - piTargetStart -= bufAbsoluteStart; - piTargetEnd -= bufAbsoluteStart; - if(tokenize) { - posEnd = pos - 2; - if(normalizeIgnorableWS) { - --pcEnd; - } - } - return true; - } - - protected final static char[] VERSION = "version".toCharArray(); - protected final static char[] NCODING = "ncoding".toCharArray(); - protected final static char[] TANDALONE = "tandalone".toCharArray(); - protected final static char[] YES = "yes".toCharArray(); - protected final static char[] NO = "no".toCharArray(); - - - - protected void parseXmlDecl(char ch) - throws XmlPullParserException, IOException - { - preventBufferCompaction = true; - bufStart = 0; // necessary to keep pos unchanged during expansion! - - ch = skipS(ch); - ch = requireInput(ch, VERSION); - ch = skipS(ch); - if(ch != '=') { - throw new XmlPullParserException( - "expected equals sign (=) after version and not "+printable(ch), this, null); - } - ch = more(); - ch = skipS(ch); - if(ch != '\'' && ch != '"') { - throw new XmlPullParserException( - "expected apostrophe (') or quotation mark (\") after version and not " - +printable(ch), this, null); - } - final char quotChar = ch; - final int versionStart = pos; - ch = more(); - while(ch != quotChar) { - if((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9') - && ch != '_' && ch != '.' && ch != ':' && ch != '-') - { - throw new XmlPullParserException( - " 'z') && (ch < 'A' || ch > 'Z')) - { - throw new XmlPullParserException( - " 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9') - && ch != '.' && ch != '_' && ch != '-') - { - throw new XmlPullParserException( - " as last part of ') { - throw new XmlPullParserException( - "expected ?> as last part of ' && bracketLevel == 0) break; - if(normalizeIgnorableWS) { - if(ch == '\r') { - normalizedCR = true; - //posEnd = pos -1; - //joinPC(); - // posEnd is alreadys set - if(!usePC) { - posEnd = pos -1; - if(posEnd > posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - - } - posEnd = pos - 1; - } - - protected void parseCDSect(boolean hadCharData) - throws XmlPullParserException, IOException - { - char ch = more(); - if(ch != 'C') throw new XmlPullParserException( - "expected <[CDATA[ for comment start", this, null); - ch = more(); - if(ch != 'D') throw new XmlPullParserException( - "expected <[CDATA[ for comment start", this, null); - ch = more(); - if(ch != 'A') throw new XmlPullParserException( - "expected <[CDATA[ for comment start", this, null); - ch = more(); - if(ch != 'T') throw new XmlPullParserException( - "expected <[CDATA[ for comment start", this, null); - ch = more(); - if(ch != 'A') throw new XmlPullParserException( - "expected <[CDATA[ for comment start", this, null); - ch = more(); - if(ch != '[') throw new XmlPullParserException( - "expected posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - } - } - boolean seenBracket = false; - boolean seenBracketBracket = false; - boolean normalizedCR = false; - while(true) { - // scan until it hits "]]>" - ch = more(); - if(ch == ']') { - if(!seenBracket) { - seenBracket = true; - } else { - seenBracketBracket = true; - //seenBracket = false; - } - } else if(ch == '>') { - if(seenBracket && seenBracketBracket) { - break; // found end sequence!!!! - } else { - seenBracketBracket = false; - } - seenBracket = false; - } else { - if(seenBracket) { - seenBracket = false; - } - } - if(normalizeInput) { - // deal with normalization issues ... - if(ch == '\r') { - normalizedCR = true; - posStart = cdStart - bufAbsoluteStart; - posEnd = pos - 1; // posEnd is alreadys set - if(!usePC) { - if(posEnd > posStart) { - joinPC(); - } else { - usePC = true; - pcStart = pcEnd = 0; - } - } - //assert usePC == true; - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } else if(ch == '\n') { - if(!normalizedCR && usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = '\n'; - } - normalizedCR = false; - } else { - if(usePC) { - if(pcEnd >= pc.length) ensurePC(pcEnd); - pc[pcEnd++] = ch; - } - normalizedCR = false; - } - } - } - } catch(EOFException ex) { - throw new XmlPullParserException( - "CDATA section started on line "+curLine+" and column "+curColumn+" was not closed", - this, ex); - } - if(normalizeInput) { - if(usePC) { - pcEnd = pcEnd - 2; - } - } - posStart = cdStart - bufAbsoluteStart; - posEnd = pos - 3; - } - - protected void fillBuf() throws IOException, XmlPullParserException { - if(reader == null) throw new XmlPullParserException( - "reader must be set before parsing is started"); - - if(bufEnd > bufSoftLimit) { - boolean compact = bufStart > bufSoftLimit; - boolean expand = false; - if(preventBufferCompaction) { - compact = false; - expand = true; - } else if(!compact) { - //freeSpace - if(bufStart < buf.length / 2) { - expand = true; - } else { - // at least half of buffer can be reclaimed --> worthwhile effort!!! - compact = true; - } - } - - // if buffer almost full then compact it - if(compact) { - System.arraycopy(buf, bufStart, buf, 0, bufEnd - bufStart); - } else if(expand) { - final int newSize = 2 * buf.length; - final char[] newBuf = new char[ newSize ]; - System.arraycopy(buf, bufStart, newBuf, 0, bufEnd - bufStart); - buf = newBuf; - if(bufLoadFactor > 0) { - bufSoftLimit = (int) (( ((long) bufLoadFactor) * buf.length ) /100); - } - - } else { - throw new XmlPullParserException("internal error in fillBuffer()"); - } - bufEnd -= bufStart; - pos -= bufStart; - posStart -= bufStart; - posEnd -= bufStart; - bufAbsoluteStart += bufStart; - bufStart = 0; - } - final int len = buf.length - bufEnd > READ_CHUNK_SIZE ? READ_CHUNK_SIZE : buf.length - bufEnd; - final int ret = reader.read(buf, bufEnd, len); - if(ret > 0) { - bufEnd += ret; - return; - } - if(ret == -1) { - if(bufAbsoluteStart == 0 && pos == 0) { - throw new EOFException("input contained no data"); - } else { - if(seenRoot && depth == 0) { - reachedEnd = true; - return; - } else { - StringBuffer expectedTagStack = new StringBuffer(); - if(depth > 0) { - expectedTagStack.append(" - expected end tag"); - if(depth > 1) { - expectedTagStack.append("s"); //more than one end tag - } - expectedTagStack.append(" "); - for (int i = depth; i > 0; i--) - { - String tagName = new String(elRawName[i], 0, elRawNameEnd[i]); - expectedTagStack.append("'); - } - expectedTagStack.append(" to close"); - for (int i = depth; i > 0; i--) - { - if(i != depth) { - expectedTagStack.append(" and"); //more than one end tag - } - String tagName = new String(elRawName[i], 0, elRawNameEnd[i]); - expectedTagStack.append(" start tag <"+tagName+">"); - expectedTagStack.append(" from line "+elRawNameLine[i]); - } - expectedTagStack.append(", parser stopped on"); - } - throw new EOFException("no more data available" - +expectedTagStack.toString()+getPositionDescription()); - } - } - } else { - throw new IOException("error reading input, returned "+ret); - } - } - - protected char more() throws IOException, XmlPullParserException { - if(pos >= bufEnd) { - fillBuf(); - if(reachedEnd) { - return (char)-1; - } - } - final char ch = buf[pos++]; - if(ch == '\n') { - ++lineNumber; columnNumber = 1; - } - else { - ++columnNumber; - } - return ch; - } - - protected void ensurePC(int end) { - final int newSize = end > READ_CHUNK_SIZE ? 2 * end : 2 * READ_CHUNK_SIZE; - final char[] newPC = new char[ newSize ]; - System.arraycopy(pc, 0, newPC, 0, pcEnd); - pc = newPC; - } - - protected void joinPC() { - final int len = posEnd - posStart; - final int newEnd = pcEnd + len + 1; - if(newEnd >= pc.length) { - ensurePC(newEnd); - } - System.arraycopy(buf, posStart, pc, pcEnd, len); - pcEnd += len; - usePC = true; - - } - - protected char requireInput(char ch, char[] input) - throws XmlPullParserException, IOException - { - for (int i = 0; i < input.length; i++) - { - if(ch != input[i]) { - throw new XmlPullParserException( - "expected "+printable(input[i])+" in "+new String(input) - +" and not "+printable(ch), this, null); - } - ch = more(); - } - return ch; - } - - protected char requireNextS() - throws XmlPullParserException, IOException - { - final char ch = more(); - if(!isS(ch)) { - throw new XmlPullParserException( - "white space is required and not "+printable(ch), this, null); - } - return skipS(ch); - } - - protected char skipS(char ch) - throws XmlPullParserException, IOException - { - while(isS(ch)) { ch = more(); } // skip additional spaces - return ch; - } - - protected static final int LOOKUP_MAX = 0x400; - protected static final char LOOKUP_MAX_CHAR = (char)LOOKUP_MAX; - protected static boolean[] lookupNameStartChar = new boolean[ LOOKUP_MAX ]; - protected static boolean[] lookupNameChar = new boolean[ LOOKUP_MAX ]; - - private static void setName(char ch) - { - lookupNameChar[ ch ] = true; - } - private static void setNameStart(char ch) - { - lookupNameStartChar[ ch ] = true; setName(ch); - } - - static { - setNameStart(':'); - for (char ch = 'A'; ch <= 'Z'; ++ch) setNameStart(ch); - setNameStart('_'); - for (char ch = 'a'; ch <= 'z'; ++ch) setNameStart(ch); - for (char ch = '\u00c0'; ch <= '\u02FF'; ++ch) setNameStart(ch); - for (char ch = '\u0370'; ch <= '\u037d'; ++ch) setNameStart(ch); - for (char ch = '\u037f'; ch < '\u0400'; ++ch) setNameStart(ch); - - setName('-'); - setName('.'); - for (char ch = '0'; ch <= '9'; ++ch) setName(ch); - setName('\u00b7'); - for (char ch = '\u0300'; ch <= '\u036f'; ++ch) setName(ch); - } - - //private final static boolean isNameStartChar(char ch) { - protected boolean isNameStartChar(char ch) { - return (ch < LOOKUP_MAX_CHAR && lookupNameStartChar[ ch ]) - || (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027') - || (ch >= '\u202A' && ch <= '\u218F') - || (ch >= '\u2800' && ch <= '\uFFEF') ; - - } - - protected boolean isNameChar(char ch) { - return (ch < LOOKUP_MAX_CHAR && lookupNameChar[ ch ]) - || (ch >= LOOKUP_MAX_CHAR && ch <= '\u2027') - || (ch >= '\u202A' && ch <= '\u218F') - || (ch >= '\u2800' && ch <= '\uFFEF') - || ch=='@' || ch=='$'; - } - - protected boolean isS(char ch) { - return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'); - } - - protected String printable(char ch) { - if(ch == '\n') { - return "\\n"; - } else if(ch == '\r') { - return "\\r"; - } else if(ch == '\t') { - return "\\t"; - } else if(ch == '\'') { - return "\\'"; - } if(ch > 127 || ch < 32) { - return "\\u"+Integer.toHexString((int)ch); - } - return ""+ch; - } - - protected String printable(String s) { - if(s == null) return null; - final int sLen = s.length(); - StringBuffer buf = new StringBuffer(sLen + 10); - for(int i = 0; i < sLen; ++i) { - buf.append(printable(s.charAt(i))); - } - s = buf.toString(); - return s; - } -} - - diff --git a/src/main/java/com/reandroid/xml/parser/MXParserCachingStrings.java b/src/main/java/com/reandroid/xml/parser/MXParserCachingStrings.java deleted file mode 100644 index 4be5a9d4..00000000 --- a/src/main/java/com/reandroid/xml/parser/MXParserCachingStrings.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * This class is taken from org.xmlpull.* - * - * Check license: http://xmlpull.org - * - */ - -/*This package is renamed from org.xmlpull.* to avoid conflicts*/ -package com.reandroid.xml.parser; - -public class MXParserCachingStrings extends MXParser implements Cloneable -{ - protected final static boolean CACHE_STATISTICS = false; - protected final static boolean TRACE_SIZING = false; - protected final static int INITIAL_CAPACITY = 13; - protected int cacheStatCalls; - protected int cacheStatWalks; - protected int cacheStatResets; - protected int cacheStatRehash; - - protected static final int CACHE_LOAD = 77; - protected int cacheEntriesCount; - protected int cacheEntriesThreshold; - - protected char[][] keys; - protected String[] values; - - public MXParserCachingStrings() { - super(); - allStringsInterned = true; - initStringCache(); - } - - @Override - public void setFeature(String name, boolean state) throws XmlPullParserException - { - if(FEATURE_NAMES_INTERNED.equals(name)) { - if(eventType != START_DOCUMENT) throw new XmlPullParserException( - "interning names feature can only be changed before parsing", this, null); - allStringsInterned = state; - if(!state && keys != null) { - resetStringCache(); - } - } else { - super.setFeature(name, state); - } - } - - public boolean getFeature(String name) - { - if(FEATURE_NAMES_INTERNED.equals(name)) { - return allStringsInterned; - } else { - return super.getFeature(name); - } - } - - - protected String newString(char[] cbuf, int off, int len) { - if(allStringsInterned) { - return newStringIntern(cbuf, off, len); - } else { - return super.newString(cbuf, off, len); - } - } - - protected String newStringIntern(char[] cbuf, int off, int len) { - if(CACHE_STATISTICS) { - ++cacheStatCalls; - } - if (cacheEntriesCount >= cacheEntriesThreshold) { - rehash(); - } - int offset = fastHash(cbuf, off, len) % keys.length; - char[] k = null; - while( (k = keys[offset]) != null - && !keysAreEqual(k, 0, k.length, - cbuf, off, len)) - { - offset = (offset + 1) % keys.length; - if(CACHE_STATISTICS) ++cacheStatWalks; - } - if (k != null) { - return values[offset]; - } else { - k = new char[len]; - System.arraycopy(cbuf, off, k, 0, len); - final String v = new String(k).intern(); - keys[offset] = k; - values[offset] = v; - ++cacheEntriesCount; - return v; - } - - } - - protected void initStringCache() { - if(keys == null) { - if(INITIAL_CAPACITY < 0) { - throw new IllegalArgumentException("Illegal initial capacity: " + INITIAL_CAPACITY); - } - if(CACHE_LOAD < 0 || CACHE_LOAD > 99) { - throw new IllegalArgumentException("Illegal load factor: " + CACHE_LOAD); - } - cacheEntriesThreshold = (int)((INITIAL_CAPACITY * CACHE_LOAD)/100); - if(cacheEntriesThreshold >= INITIAL_CAPACITY) { - throw new RuntimeException( - "internal error: threshold must be less than capacity: "+INITIAL_CAPACITY); - } - keys = new char[INITIAL_CAPACITY][]; - values = new String[INITIAL_CAPACITY]; - cacheEntriesCount = 0; - } - } - - protected void resetStringCache() { - if(CACHE_STATISTICS) { - ++cacheStatResets; - } - initStringCache(); - } - - private void rehash() { - if(CACHE_STATISTICS) ++cacheStatRehash; - final int newSize = 2 * keys.length + 1; - cacheEntriesThreshold = (int)((newSize * CACHE_LOAD)/100); - if(cacheEntriesThreshold >= newSize) throw new RuntimeException( - "internal error: threshold must be less than capacity: "+newSize); - - final char[][] newKeys = new char[newSize][]; - final String[] newValues = new String[newSize]; - for(int i = 0; i < keys.length; i++) { - final char[] k = keys[i]; - keys[i] = null; - final String v = values[i]; - values[i] = null; - if(k != null) { - int newOffset = fastHash(k, 0, k.length) % newSize; - char[] newk = null; - while((newk = newKeys[newOffset]) != null) { - if(keysAreEqual(newk, 0, newk.length, - k, 0, k.length)) { - throw new RuntimeException("internal cache error: duplicated keys: "+ - new String(newk)+" and "+new String(k)); - } - newOffset = (newOffset + 1) % newSize; - } - - newKeys[newOffset] = k; - newValues[newOffset] = v; - } - } - keys = newKeys; - values = newValues; - } - - private static boolean keysAreEqual (char[] a, int astart, int alength, - char[] b, int bstart, int blength) { - if(alength != blength) { - return false; - } else { - for(int i = 0; i < alength; i++) { - if(a[astart + i] != b[bstart + i]) { - return false; - } - } - return true; - } - } - -} diff --git a/src/main/java/com/reandroid/xml/parser/MXParserNonValidating.java b/src/main/java/com/reandroid/xml/parser/MXParserNonValidating.java deleted file mode 100644 index 2141bd34..00000000 --- a/src/main/java/com/reandroid/xml/parser/MXParserNonValidating.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * This class is taken from org.xmlpull.* - * - * Check license: http://xmlpull.org - * - */ - -/*This package is renamed from org.xmlpull.* to avoid conflicts*/ -package com.reandroid.xml.parser; - -import java.io.IOException; - -public class MXParserNonValidating extends MXParserCachingStrings -{ - private boolean processDocDecl; - - public MXParserNonValidating() { - super(); - } - - @Override - public void setFeature(String name, - boolean state) throws XmlPullParserException - { - if(FEATURE_PROCESS_DOCDECL.equals(name)) { - if(eventType != START_DOCUMENT) throw new XmlPullParserException( - "process DOCDECL feature can only be changed before parsing", this, null); - processDocDecl = state; - } else { - super.setFeature(name, state); - } - } - - @Override - public boolean getFeature(String name) - { - if(FEATURE_PROCESS_DOCDECL.equals(name)) { - return processDocDecl; - } else { - return super.getFeature(name); - } - } - - - @Override - protected char more() throws IOException, XmlPullParserException { - return super.more(); - } - - @Override - protected char[] lookuEntityReplacement(int entitNameLen) throws XmlPullParserException, IOException - - { - if(!allStringsInterned) { - final int hash = fastHash(buf, posStart, posEnd - posStart); - LOOP: - for (int i = entityEnd - 1; i >= 0; --i) - { - if(hash == entityNameHash[ i ] && entitNameLen == entityNameBuf[ i ].length) { - final char[] entityBuf = entityNameBuf[ i ]; - for (int j = 0; j < entitNameLen; j++) - { - if(buf[posStart + j] != entityBuf[j]) continue LOOP; - } - if(tokenize) text = entityReplacement[ i ]; - return entityReplacementBuf[ i ]; - } - } - } else { - entityRefName = newString(buf, posStart, posEnd - posStart); - for (int i = entityEnd - 1; i >= 0; --i) - { - // take advantage that interning for newStirng is enforced - if(entityRefName == entityName[ i ]) { - if(tokenize) { - text = entityReplacement[ i ]; - } - return entityReplacementBuf[ i ]; - } - } - } - return null; - } - - - @Override - protected void parseDocdecl() - throws XmlPullParserException, IOException - { - //make sure that tokenize flag is disabled temporarily!!!! - final boolean oldTokenize = tokenize; - try { - //ASSUMPTION: seen ' - ch = requireNextS(); - int nameStart = pos; - ch = readName(ch); - int nameEnd = pos; - ch = skipS(ch); - // [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral - if(ch == 'S' || ch == 'P') { - ch = processExternalId(ch); - ch = skipS(ch); - } - if(ch == '[') { - processInternalSubset(); - } - ch = skipS(ch); - if(ch != '>') { - throw new XmlPullParserException( - "expected > to finish <[DOCTYPE but got "+printable(ch), this, null); - } - posEnd = pos - 1; - } finally { - tokenize = oldTokenize; - } - } - protected char processExternalId(char ch) - throws XmlPullParserException, IOException - { - // [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral - // [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") - // [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" - // [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] - - //TODO - - return ch; - } - - protected void processInternalSubset() - throws XmlPullParserException, IOException - { - // [28] ... (markupdecl | DeclSep)* ']' // [WFC: External Subset] - // [28a] DeclSep ::= PEReference | S // [WFC: PE Between Declarations] - - // [69] PEReference ::= '%' Name ';' //[WFC: No Recursion] [WFC: In DTD] - while(true) { - char ch = more(); // firs ttime called it will skip initial "[" - if(ch == ']') break; - if(ch == '%') { - processPEReference(); - } else if(isS(ch)) { - ch = skipS(ch); - } else { - processMarkupDecl(ch); - } - } - } - - protected void processPEReference() - throws XmlPullParserException, IOException - { - //TODO - } - protected void processMarkupDecl(char ch) - throws XmlPullParserException, IOException - { - // [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment - // [WFC: PEs in Internal Subset] - - - //BIG SWITCH statement - if(ch != '<') { - throw new XmlPullParserException("expected < for markupdecl in DTD not "+printable(ch), - this, null); - } - ch = more(); - if(ch == '?') { - parsePI(); - } else if(ch == '!') { - ch = more(); - if(ch == '-') { - // note: if(tokenize == false) posStart/End is NOT changed!!!! - parseComment(); - } else { - ch = more(); - if(ch == 'A') { - processAttlistDecl(ch); //A-TTLIST - } else if(ch == 'E') { - ch = more(); - if(ch == 'L') { - processElementDecl(ch); //EL-EMENT - } else if(ch == 'N') { - processEntityDecl(ch); // EN-TITY - } else { - throw new XmlPullParserException( - "expected ELEMENT or ENTITY after ' - //???? [VC: Unique Element Type Declaration] - // [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children - // [47] children ::= (choice | seq) ('?' | '*' | '+')? - // [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')? - // [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')' - // [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' - // [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' - // | '(' S? '#PCDATA' S? ')' - - //assert ch == 'L' - ch = requireNextS(); - readName(ch); - ch = requireNextS(); - // readContentSpec(ch); - } - - protected void processAttlistDecl(char ch) - throws XmlPullParserException, IOException - { - // [52] AttlistDecl ::= '' - // [53] AttDef ::= S Name S AttType S DefaultDecl - // [54] AttType ::= StringType | TokenizedType | EnumeratedType - // [55] StringType ::= 'CDATA' - // [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' - // | 'NMTOKENS' - // [57] EnumeratedType ::= NotationType | Enumeration - // [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' - // [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' - // [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) - // [WFC: No < in Attribute Values] - - //assert ch == 'A' - - } - - - protected void processEntityDecl(char ch) - throws XmlPullParserException, IOException - { - - // [70] EntityDecl ::= GEDecl | PEDecl - // [71] GEDecl ::= '' - // [72] PEDecl ::= '' - // [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) - // [74] PEDef ::= EntityValue | ExternalID - // [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral - - //[9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' - // | "'" ([^%&'] | PEReference | Reference)* "'" - - //assert ch == 'N' - - } - - protected void processNotationDecl(char ch) - throws XmlPullParserException, IOException - { - - // [82] NotationDecl ::= '' - // [83] PublicID ::= 'PUBLIC' S PubidLiteral - - //assert ch == 'N' - } - - - - protected char readName(char ch) - throws XmlPullParserException, IOException - { - if(isNameStartChar(ch)) { - throw new XmlPullParserException( - "XML name must start with name start character not "+printable(ch), this, null); - } - while(isNameChar(ch)) { - ch = more(); - } - return ch; - } -} \ No newline at end of file diff --git a/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java b/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java index 5457df93..cb7f2d4b 100755 --- a/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java +++ b/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java @@ -1,18 +1,18 @@ - /* - * Copyright (C) 2022 github.com/REAndroid - * - * 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. - */ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 com.reandroid.xml.parser; import com.android.org.kxml2.io.KXmlParser; @@ -67,30 +67,24 @@ private void close(){ mComments=null; } private void closeFileInputStream(){ - if(!(mParser instanceof MXParser)){ - return; - } - MXParser parser=(MXParser) mParser; - InputStream inputStream = parser.getInputStream(); - if(!(inputStream instanceof FileInputStream)){ + if(!(mParser instanceof Closeable)){ return; } + Closeable closeable=(Closeable) mParser; try { - inputStream.close(); + closeable.close(); } catch (IOException ignored) { } } private void closeReader(){ - if(!(mParser instanceof MXParser)){ + if(!(mParser instanceof Closeable)){ return; } - MXParser parser=(MXParser) mParser; - Reader reader = parser.getReader(); - if(reader!=null){ - try { - reader.close(); - } catch (IOException ignored) { - } + Closeable closeable=(Closeable) mParser; + try { + closeable.close(); + } catch (IOException exception) { + exception.printStackTrace(); } } private void closeParser(){ diff --git a/src/main/java/com/reandroid/xml/parser/XMLSpanParser.java b/src/main/java/com/reandroid/xml/parser/XMLSpanParser.java index 9e8e08fc..04f1834c 100644 --- a/src/main/java/com/reandroid/xml/parser/XMLSpanParser.java +++ b/src/main/java/com/reandroid/xml/parser/XMLSpanParser.java @@ -20,12 +20,15 @@ import java.io.IOException; import java.io.StringReader; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + public class XMLSpanParser { private final Object mLock = new Object(); private final XmlPullParser mParser; private XMLElement mCurrentElement; public XMLSpanParser(){ - this.mParser = new MXParserNonValidating(); + this.mParser = XMLParserFactory.newPullParser(); } public XMLElement parse(String text) throws XMLException { synchronized (mLock){ diff --git a/src/main/java/com/reandroid/xml/parser/XmlPullParser.java b/src/main/java/com/reandroid/xml/parser/XmlPullParser.java deleted file mode 100644 index fe2c9ea8..00000000 --- a/src/main/java/com/reandroid/xml/parser/XmlPullParser.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This class is taken from org.xmlpull.* - * - * Check license: http://xmlpull.org - * - */ - -/*This package is renamed from org.xmlpull.* to avoid conflicts*/ -package com.reandroid.xml.parser; - -import java.io.InputStream; -import java.io.IOException; -import java.io.Reader; - -@Deprecated -public interface XmlPullParser { - - String NO_NAMESPACE = ""; - int START_DOCUMENT = 0; - int END_DOCUMENT = 1; - int START_TAG = 2; - int END_TAG = 3; - int TEXT = 4; - int CDSECT = 5; - int ENTITY_REF = 6; - int IGNORABLE_WHITESPACE = 7; - int PROCESSING_INSTRUCTION = 8; - int COMMENT = 9; - int DOCDECL = 10; - String [] TYPES = { - "START_DOCUMENT", - "END_DOCUMENT", - "START_TAG", - "END_TAG", - "TEXT", - "CDSECT", - "ENTITY_REF", - "IGNORABLE_WHITESPACE", - "PROCESSING_INSTRUCTION", - "COMMENT", - "DOCDECL" - }; - - String FEATURE_PROCESS_NAMESPACES = "http://xmlpull.org/v1/doc/features.html#process-namespaces"; - - String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = "http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes"; - String FEATURE_PROCESS_DOCDECL = "http://xmlpull.org/v1/doc/features.html#process-docdecl"; - - String FEATURE_VALIDATION = "http://xmlpull.org/v1/doc/features.html#validation"; - - void setFeature(String name, boolean state) throws XmlPullParserException; - boolean getFeature(String name); - void setProperty(String name, Object value) throws XmlPullParserException; - Object getProperty(String name); - void setInput(Reader in) throws XmlPullParserException; - void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException; - String getInputEncoding(); - void defineEntityReplacementText( String entityName, String replacementText ) throws XmlPullParserException; - int getNamespaceCount(int depth) throws XmlPullParserException; - String getNamespacePrefix(int pos) throws XmlPullParserException; - String getNamespaceUri(int pos) throws XmlPullParserException; - String getNamespace (String prefix); - int getDepth(); - String getPositionDescription(); - int getLineNumber(); - int getColumnNumber(); - boolean isWhitespace() throws XmlPullParserException; - String getText (); - char[] getTextCharacters(int [] holderForStartAndLength); - String getNamespace (); - String getName(); - String getPrefix(); - boolean isEmptyElementTag() throws XmlPullParserException; - int getAttributeCount(); - String getAttributeNamespace (int index); - String getAttributeName (int index); - String getAttributePrefix(int index); - String getAttributeType(int index); - boolean isAttributeDefault(int index); - String getAttributeValue(int index); - String getAttributeValue(String namespace, String name); - int getEventType() throws XmlPullParserException; - int next() throws XmlPullParserException, IOException; - int nextToken() throws XmlPullParserException, IOException; - void require(int type, String namespace, String name) throws XmlPullParserException, IOException; - String nextText() throws XmlPullParserException, IOException; - int nextTag() throws XmlPullParserException, IOException; -// public void skipSubTree() throws XmlPullParserException, IOException; -} - diff --git a/src/main/java/com/reandroid/xml/parser/XmlPullParserException.java b/src/main/java/com/reandroid/xml/parser/XmlPullParserException.java deleted file mode 100644 index 50915cc7..00000000 --- a/src/main/java/com/reandroid/xml/parser/XmlPullParserException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This class is taken from org.xmlpull.* - * - * Check license: http://xmlpull.org - * - */ - -/*This package is renamed from org.xmlpull.* to avoid conflicts*/ -package com.reandroid.xml.parser; - -@Deprecated -public class XmlPullParserException extends Exception { - protected Throwable detail; - protected int row = -1; - protected int column = -1; - - public XmlPullParserException(String s) { - super(s); - } - public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) { - super(buildMessage(msg, parser)); - if (parser != null) { - this.row = parser.getLineNumber(); - this.column = parser.getColumnNumber(); - } - this.detail = chain; - } - public Throwable getDetail() { return detail; } - public int getLineNumber() { return row; } - 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(); - } -} -