diff --git a/build.gradle b/build.gradle index 1bac861..817d1c8 100755 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,7 @@ repositories { dependencies { compile(files("$rootProject.projectDir/libs/ArchiveUtil.jar")) + compile(files("$rootProject.projectDir/libs/json.jar")) } processResources { diff --git a/libs/ArchiveUtil.jar b/libs/ArchiveUtil.jar index 222aafb..44b6cd7 100644 Binary files a/libs/ArchiveUtil.jar and b/libs/ArchiveUtil.jar differ diff --git a/libs/json.jar b/libs/json.jar new file mode 100755 index 0000000..a9eab7a Binary files /dev/null and b/libs/json.jar differ diff --git a/src/main/java/com/reandroid/lib/apk/ApkEntry.java b/src/main/java/com/reandroid/lib/apk/ApkEntry.java new file mode 100644 index 0000000..7054a3e --- /dev/null +++ b/src/main/java/com/reandroid/lib/apk/ApkEntry.java @@ -0,0 +1,10 @@ +package com.reandroid.lib.apk; + +import com.reandroid.archive.InputSource; + +public class ApkEntry { + private InputSource mInputSource; + public ApkEntry(InputSource inputSource){ + this.mInputSource=inputSource; + } +} diff --git a/src/main/java/com/reandroid/lib/apk/ApkModule.java b/src/main/java/com/reandroid/lib/apk/ApkModule.java new file mode 100644 index 0000000..29aecbc --- /dev/null +++ b/src/main/java/com/reandroid/lib/apk/ApkModule.java @@ -0,0 +1,170 @@ +package com.reandroid.lib.apk; + +import com.reandroid.archive.APKArchive; +import com.reandroid.archive.InputSource; +import com.reandroid.lib.arsc.array.PackageArray; +import com.reandroid.lib.arsc.chunk.PackageBlock; +import com.reandroid.lib.arsc.chunk.TableBlock; +import com.reandroid.lib.arsc.chunk.xml.AndroidManifestBlock; +import com.reandroid.lib.arsc.group.StringGroup; +import com.reandroid.lib.arsc.item.TableString; +import com.reandroid.lib.arsc.pool.TableStringPool; +import com.reandroid.lib.arsc.value.EntryBlock; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +public class ApkModule { + private final APKArchive apkArchive; + private boolean loadDefaultFramework = true; + private TableBlock mTableBlock; + private AndroidManifestBlock mManifestBlock; + private ApkModule(APKArchive apkArchive){ + this.apkArchive=apkArchive; + } + public void writeTo(File file) throws IOException { + APKArchive archive=getApkArchive(); + archive.writeApk(file); + } + public void removeDir(String dirName){ + getApkArchive().removeDir(dirName); + } + public void setResourcesRootDir(String dirName) throws IOException { + List resFileList = listResFiles(); + Set existPaths=new HashSet<>(); + List sourceList = getApkArchive().listInputSources(); + for(InputSource inputSource:sourceList){ + existPaths.add(inputSource.getAlias()); + } + for(ResFile resFile:resFileList){ + String path=resFile.getFilePath(); + String pathNew=ApkUtil.replaceRootDir(path, dirName); + if(existPaths.contains(pathNew)){ + continue; + } + existPaths.remove(path); + existPaths.add(pathNew); + resFile.setFilePath(pathNew); + } + TableStringPool stringPool= getTableBlock().getTableStringPool(); + stringPool.refreshUniqueIdMap(); + } + public List listResFiles() throws IOException { + List results=new ArrayList<>(); + TableBlock tableBlock=getTableBlock(); + TableStringPool stringPool= tableBlock.getTableStringPool(); + for(InputSource inputSource:getApkArchive().listInputSources()){ + String name=inputSource.getAlias(); + StringGroup groupTableString = stringPool.get(name); + if(groupTableString==null){ + continue; + } + for(TableString tableString:groupTableString.listItems()){ + List entryBlockList = tableString.listReferencedEntries(); + ResFile resFile=new ResFile(inputSource, entryBlockList); + results.add(resFile); + } + } + return results; + } + public String getPackageName() throws IOException { + if(hasAndroidManifestBlock()){ + return getAndroidManifestBlock().getPackageName(); + } + if(!hasTableBlock()){ + return null; + } + TableBlock tableBlock=getTableBlock(); + PackageArray pkgArray = tableBlock.getPackageArray(); + PackageBlock pkg = pkgArray.get(0); + if(pkg==null){ + return null; + } + return pkg.getPackageName(); + } + public void setPackageName(String name) throws IOException { + String old=getPackageName(); + if(hasAndroidManifestBlock()){ + getAndroidManifestBlock().setPackageName(name); + } + if(!hasTableBlock()){ + return; + } + TableBlock tableBlock=getTableBlock(); + PackageArray pkgArray = tableBlock.getPackageArray(); + for(PackageBlock pkg:pkgArray.listItems()){ + if(pkgArray.childesCount()==1){ + pkg.setPackageName(name); + continue; + } + String pkgName=pkg.getPackageName(); + if(pkgName.startsWith(old)){ + pkgName=pkgName.replace(old, name); + pkg.setPackageName(pkgName); + } + } + } + public boolean hasAndroidManifestBlock(){ + return mManifestBlock!=null + || getApkArchive().getInputSource(AndroidManifestBlock.FILE_NAME)!=null; + } + public AndroidManifestBlock getAndroidManifestBlock() throws IOException { + if(mManifestBlock!=null){ + return mManifestBlock; + } + APKArchive archive=getApkArchive(); + InputSource inputSource = archive.getInputSource(AndroidManifestBlock.FILE_NAME); + if(inputSource==null){ + throw new IOException("Entry not found: "+AndroidManifestBlock.FILE_NAME); + } + InputStream inputStream = inputSource.openStream(); + AndroidManifestBlock manifestBlock=AndroidManifestBlock.load(inputStream); + inputStream.close(); + BlockInputSource blockInputSource=new BlockInputSource<>(inputSource.getName(),manifestBlock); + blockInputSource.setSort(inputSource.getSort()); + blockInputSource.setMethod(inputSource.getMethod()); + archive.add(blockInputSource); + mManifestBlock=manifestBlock; + return mManifestBlock; + } + public boolean hasTableBlock(){ + return mTableBlock!=null + || getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null; + } + public TableBlock getTableBlock() throws IOException { + if(mTableBlock!=null){ + return mTableBlock; + } + APKArchive archive=getApkArchive(); + InputSource inputSource = archive.getInputSource(TableBlock.FILE_NAME); + if(inputSource==null){ + throw new IOException("Entry not found: "+TableBlock.FILE_NAME); + } + TableBlock tableBlock; + InputStream inputStream = inputSource.openStream(); + if(loadDefaultFramework){ + tableBlock=TableBlock.loadWithAndroidFramework(inputStream); + }else { + tableBlock=TableBlock.load(inputStream); + } + inputStream.close(); + mTableBlock=tableBlock; + BlockInputSource blockInputSource=new BlockInputSource<>(inputSource.getName(),tableBlock); + blockInputSource.setMethod(inputSource.getMethod()); + blockInputSource.setSort(inputSource.getSort()); + archive.add(blockInputSource); + return mTableBlock; + } + public APKArchive getApkArchive() { + return apkArchive; + } + public void setLoadDefaultFramework(boolean loadDefaultFramework) { + this.loadDefaultFramework = loadDefaultFramework; + } + public static ApkModule loadApkFile(File apkFile) throws IOException { + APKArchive archive=APKArchive.loadZippedApk(apkFile); + return new ApkModule(archive); + } +} diff --git a/src/main/java/com/reandroid/lib/apk/ApkUtil.java b/src/main/java/com/reandroid/lib/apk/ApkUtil.java new file mode 100644 index 0000000..adc5097 --- /dev/null +++ b/src/main/java/com/reandroid/lib/apk/ApkUtil.java @@ -0,0 +1,15 @@ +package com.reandroid.lib.apk; + +public class ApkUtil { + public static String replaceRootDir(String path, String dirName){ + int i=path.indexOf('/')+1; + path=path.substring(i); + if(dirName != null && dirName.length()>0){ + if(!dirName.endsWith("/")){ + dirName=dirName+"/"; + } + path=dirName+path; + } + return path; + } +} diff --git a/src/main/java/com/reandroid/lib/apk/BlockInputSource.java b/src/main/java/com/reandroid/lib/apk/BlockInputSource.java new file mode 100644 index 0000000..9c5010a --- /dev/null +++ b/src/main/java/com/reandroid/lib/apk/BlockInputSource.java @@ -0,0 +1,24 @@ +package com.reandroid.lib.apk; + +import com.reandroid.archive.InputSource; +import com.reandroid.lib.arsc.chunk.BaseChunk; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +public class BlockInputSource extends InputSource { + private final T mBlock; + public BlockInputSource(String name, T block) { + super(name); + this.mBlock=block; + } + public T getBlock() { + return mBlock; + } + @Override + public InputStream openStream(){ + T block=getBlock(); + block.refresh(); + return new ByteArrayInputStream(block.getBytes()); + } +} diff --git a/src/main/java/com/reandroid/lib/apk/ResFile.java b/src/main/java/com/reandroid/lib/apk/ResFile.java new file mode 100644 index 0000000..2c856ec --- /dev/null +++ b/src/main/java/com/reandroid/lib/apk/ResFile.java @@ -0,0 +1,33 @@ +package com.reandroid.lib.apk; + +import com.reandroid.archive.InputSource; +import com.reandroid.lib.arsc.value.EntryBlock; + +import java.util.List; + +public class ResFile { + private final List entryBlockList; + private final InputSource inputSource; + public ResFile(InputSource inputSource, List entryBlockList){ + this.inputSource=inputSource; + this.entryBlockList=entryBlockList; + } + public String getFilePath(){ + return getInputSource().getAlias(); + } + public void setFilePath(String filePath){ + getInputSource().setAlias(filePath); + for(EntryBlock entryBlock:entryBlockList){ + entryBlock.getValueAsTableString().set(filePath); + } + } + public InputSource getInputSource() { + return inputSource; + } + @Override + public String toString(){ + return getFilePath(); + } + + public static final String DIR_NAME="res"; +} diff --git a/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java index 2d56f09..3182eef 100755 --- a/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java @@ -3,9 +3,12 @@ package com.reandroid.lib.arsc.array; import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.value.EntryBlock; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; +import org.json.JSONObject; -public class EntryBlockArray extends OffsetBlockArray { +public class EntryBlockArray extends OffsetBlockArray implements JsonItem { public EntryBlockArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){ super(offsets, itemCount, itemStart); } @@ -38,4 +41,42 @@ public class EntryBlockArray extends OffsetBlockArray { return new EntryBlock[len]; } + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + int index=0; + for(EntryBlock entryBlock:listItems()){ + JSONObject childObject = entryBlock.toJson(); + if(childObject==null){ + continue; + } + childObject.put(NAME_id, entryBlock.getIndex()); + jsonArray.put(index, childObject); + index++; + } + return jsonArray; + } + + @Override + public void fromJson(JSONArray json) { + clearChildes(); + int length=json.length(); + ensureSize(length); + for(int i=0;i { +public class LibraryInfoArray extends BlockArray implements JsonItem { private final IntegerItem mInfoCount; public LibraryInfoArray(IntegerItem infoCount){ this.mInfoCount=infoCount; @@ -30,4 +34,32 @@ public class LibraryInfoArray extends BlockArray { setChildesCount(mInfoCount.get()); super.onReadBytes(reader); } + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + int i=0; + for(LibraryInfo libraryInfo:listItems()){ + JSONObject jsonObject= libraryInfo.toJson(); + if(jsonObject==null){ + continue; + } + jsonArray.put(i, jsonObject); + i++; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + clearChildes(); + if(json==null){ + return; + } + int length= json.length(); + ensureSize(length); + for (int i=0;i implements BlockLoad { +public class PackageArray extends BlockArray implements BlockLoad, JsonItem { private final IntegerItem mPackageCount; public PackageArray(IntegerItem packageCount){ this.mPackageCount=packageCount; @@ -60,4 +64,29 @@ public class PackageArray extends BlockArray implements BlockLoad setChildesCount(mPackageCount.get()); } } + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + int i=0; + for(PackageBlock packageBlock:listItems()){ + JSONObject jsonObject= packageBlock.toJson(); + if(jsonObject==null){ + continue; + } + jsonArray.put(i, jsonObject); + i++; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + int length= json.length(); + clearChildes(); + ensureSize(length); + for (int i=0;i { +public class ResValueBagItemArray extends BlockArray implements JsonItem { public ResValueBagItemArray(){ super(); } @@ -21,4 +23,28 @@ public class ResValueBagItemArray extends BlockArray { protected void onRefreshed() { } + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + if(isNull()){ + return jsonArray; + } + ResValueBagItem[] childes = getChildes(); + for(int i=0;i implements Comparator { +public class ResXmlAttributeArray extends BlockArray + implements Comparator, JsonItem { private final HeaderBlock mHeaderBlock; private final ShortItem mAttributeStart; private final ShortItem mAttributeCount; @@ -70,4 +74,27 @@ public class ResXmlAttributeArray extends BlockArray implements public int compare(ResXmlAttribute attr1, ResXmlAttribute attr2) { return attr1.compareTo(attr2); } + + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + int i=0; + for(ResXmlAttribute attr:listItems()){ + JSONObject jsonObject = attr.toJson(); + jsonArray.put(i, jsonObject); + i++; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + clearChildes(); + int length= json.length(); + ensureSize(length); + for(int i=0;i { +public class ResXmlIDArray extends BlockArray implements JsonItem { private final HeaderBlock mHeaderBlock; private final Map mResIdMap; private boolean mUpdated; @@ -86,4 +89,23 @@ public class ResXmlIDArray extends BlockArray { count=count/4; return count; } + @Override + public JSONArray toJson() { + if(childesCount()==0){ + return null; + } + JSONArray jsonArray=new JSONArray(); + int i=0; + for(ResXmlID xmlID:listItems()){ + JSONObject jsonObject=xmlID.toJson(); + jsonArray.put(i,jsonObject); + i++; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + //TODO + throw new IllegalArgumentException("Not implemented yet"); + } } diff --git a/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java b/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java index 77c5bca..84eeb8e 100755 --- a/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java @@ -4,10 +4,13 @@ import com.reandroid.lib.arsc.base.BlockArray; import com.reandroid.lib.arsc.chunk.TypeBlock; import com.reandroid.lib.arsc.container.SpecTypePair; import com.reandroid.lib.arsc.value.EntryBlock; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; +import org.json.JSONObject; import java.util.*; -public class SpecTypePairArray extends BlockArray { +public class SpecTypePairArray extends BlockArray implements JsonItem { public SpecTypePairArray(){ super(); } @@ -140,4 +143,29 @@ public class SpecTypePairArray extends BlockArray { } return results; } + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + int i=0; + for(SpecTypePair specTypePair:listItems()){ + JSONObject jsonObject= specTypePair.toJson(); + if(jsonObject==null){ + continue; + } + jsonArray.put(i, jsonObject); + i++; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + int length= json.length(); + clearChildes(); + ensureSize(length); + for (int i=0;i extends OffsetBlockArray{ +public abstract class StringArray extends OffsetBlockArray implements JsonItem { private boolean mUtf8; public StringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { @@ -50,5 +53,48 @@ public abstract class StringArray extends OffsetBlockArray protected void refreshChildes(){ // Not required } + @Override + public JSONArray toJson() { + return toJson(true); + } + public JSONArray toJson(boolean styledOnly) { + if(childesCount()==0){ + return null; + } + JSONArray jsonArray=new JSONArray(); + int i=0; + for(T item:listItems()){ + if(item.isNull()){ + continue; + } + if(styledOnly && !item.hasStyle()){ + continue; + } + JSONObject jsonObject= item.toJson(); + if(jsonObject==null){ + continue; + } + jsonArray.put(i, jsonObject); + i++; + } + if(i==0){ + return null; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + clearChildes(); + if(json==null){ + return; + } + int length = json.length(); + ensureSize(length); + for(int i=0; i { +public class StyleArray extends OffsetBlockArray implements JsonItem { public StyleArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart) { super(offsets, itemCount, itemStart); setEndBytes(END_BYTE); @@ -51,5 +53,21 @@ public class StyleArray extends OffsetBlockArray { public StyleItem[] newInstance(int len) { return new StyleItem[len]; } + + @Override + public JSONArray toJson() { + if(childesCount()==0){ + return null; + } + int i=0; + for(StyleItem styleItem:listItems()){ + + } + return null; + } + @Override + public void fromJson(JSONArray json) { + + } private static final byte END_BYTE= (byte) 0xFF; } diff --git a/src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java b/src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java index 086e0c1..fe13ce3 100755 --- a/src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java @@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.array; import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.TableString; +import org.json.JSONArray; public class TableStringArray extends StringArray { public TableStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { diff --git a/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java index ce0dc31..238f1b8 100755 --- a/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java @@ -10,13 +10,16 @@ import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.TypeString; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.ResConfig; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; +import org.json.JSONObject; import java.io.IOException; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; -public class TypeBlockArray extends BlockArray { +public class TypeBlockArray extends BlockArray implements JsonItem { private byte mTypeId; public TypeBlockArray(){ super(); @@ -232,4 +235,29 @@ public class TypeBlockArray extends BlockArray { } return null; } + @Override + public JSONArray toJson() { + JSONArray jsonArray=new JSONArray(); + int i=0; + for(TypeBlock typeBlock:listItems()){ + JSONObject jsonObject= typeBlock.toJson(); + if(jsonObject==null){ + continue; + } + jsonArray.put(i, jsonObject); + i++; + } + return jsonArray; + } + @Override + public void fromJson(JSONArray json) { + int length= json.length(); + clearChildes(); + ensureSize(length); + for (int i=0;i listLibraryInfo(){ - return mLibraryInfoArray.listItems(); + return getLibraryInfoArray().listItems(); } @Override public boolean isNull(){ diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java index 5634b67..1b79380 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java @@ -1,5 +1,6 @@ package com.reandroid.lib.arsc.chunk; +import com.reandroid.lib.arsc.array.LibraryInfoArray; import com.reandroid.lib.arsc.array.SpecTypePairArray; import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.container.PackageLastBlocks; @@ -14,11 +15,13 @@ import com.reandroid.lib.arsc.pool.TableStringPool; import com.reandroid.lib.arsc.pool.TypeStringPool; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.LibraryInfo; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.util.*; -public class PackageBlock extends BaseChunk { +public class PackageBlock extends BaseChunk implements JsonItem { private final IntegerItem mPackageId; private final PackageName mPackageName; @@ -157,11 +160,7 @@ public class PackageBlock extends BaseChunk { if(libraryBlock==null){ return; } - LibraryInfo[] allInfo=libraryBlock.getAllInfo(); - if (allInfo==null){ - return; - } - for(LibraryInfo info:allInfo){ + for(LibraryInfo info:libraryBlock.getLibraryInfoArray().listItems()){ addLibraryInfo(info); } } @@ -268,6 +267,26 @@ public class PackageBlock extends BaseChunk { refreshKeyStrings(); } + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put(NAME_id, getId()); + jsonObject.put(NAME_name, getName()); + jsonObject.put(NAME_specs, getSpecTypePairArray().toJson()); + LibraryInfoArray libraryInfoArray = mLibraryBlock.getLibraryInfoArray(); + if(libraryInfoArray.childesCount()>0){ + jsonObject.put(NAME_libraries,libraryInfoArray.toJson()); + } + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + setId(json.getInt(NAME_id)); + setName(json.getString(NAME_name)); + getSpecTypePairArray().fromJson(json.getJSONArray(NAME_specs)); + LibraryInfoArray libraryInfoArray = mLibraryBlock.getLibraryInfoArray(); + libraryInfoArray.fromJson(json.optJSONArray(NAME_libraries)); + } @Override public String toString(){ StringBuilder builder=new StringBuilder(); @@ -283,4 +302,9 @@ public class PackageBlock extends BaseChunk { } return builder.toString(); } + + private static final String NAME_id="id"; + private static final String NAME_name="name"; + private static final String NAME_specs="specs"; + private static final String NAME_libraries="libraries"; } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java index 3c889d2..5bd4051 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java @@ -7,10 +7,12 @@ import com.reandroid.lib.arsc.io.BlockLoad; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.io.IOException; -public class SpecBlock extends BaseTypeBlock implements BlockLoad { +public class SpecBlock extends BaseTypeBlock implements BlockLoad , JsonItem { private final IntegerArray mOffsets; public SpecBlock() { super(ChunkType.SPEC, 1); @@ -51,4 +53,16 @@ public class SpecBlock extends BaseTypeBlock implements BlockLoad { } return builder.toString(); } + + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put("id", getTypeId()); + return jsonObject; + } + + @Override + public void fromJson(JSONObject json) { + setTypeId((byte) json.getInt("id")); + } } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java index b0fba2b..25e5935 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java @@ -8,13 +8,16 @@ import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.pool.TableStringPool; import com.reandroid.lib.common.Frameworks; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; +import org.json.JSONObject; import java.io.*; import java.util.Collection; import java.util.HashSet; import java.util.Set; -public class TableBlock extends BaseChunk { +public class TableBlock extends BaseChunk implements JsonItem { private final IntegerItem mPackageCount; private final TableStringPool mTableStringPool; private final PackageArray mPackageArray; @@ -116,10 +119,33 @@ public class TableBlock extends BaseChunk { } mFrameWorks.add(tableBlock); } + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put(NAME_packages, getPackageArray().toJson()); + JSONArray jsonArray = getTableStringPool().toJson(); + if(jsonArray!=null){ + jsonObject.put(NAME_styled_strings, jsonArray); + } + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + JSONArray jsonArray= json.optJSONArray(NAME_styled_strings); + if(jsonArray!=null){ + getTableStringPool().fromJson(jsonArray); + } + getPackageArray().fromJson(json.getJSONArray(NAME_packages)); + refresh(); + } public static TableBlock loadWithAndroidFramework(InputStream inputStream) throws IOException{ + TableBlock tableBlock=load(inputStream); + tableBlock.addFramework(Frameworks.getAndroid()); + return tableBlock; + } + public static TableBlock load(InputStream inputStream) throws IOException{ TableBlock tableBlock=new TableBlock(); tableBlock.readBytes(inputStream); - tableBlock.addFramework(Frameworks.getAndroid()); return tableBlock; } @@ -164,4 +190,6 @@ public class TableBlock extends BaseChunk { } public static final String FILE_NAME="resources.arsc"; + private static final String NAME_packages="packages"; + private static final String NAME_styled_strings="styled_strings"; } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java index fbaeaeb..daa321f 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java @@ -5,14 +5,17 @@ import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.container.SpecTypePair; import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; +import com.reandroid.lib.arsc.item.TypeString; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.ResConfig; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public class TypeBlock extends BaseTypeBlock { +public class TypeBlock extends BaseTypeBlock implements JsonItem { private final IntegerItem mEntriesStart; private final ResConfig mResConfig; private final IntegerArray mEntryOffsets; @@ -113,6 +116,28 @@ public class TypeBlock extends BaseTypeBlock { super.onPreRefreshRefresh(); } @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put("id", getTypeId()); + TypeString typeString=getTypeString(); + if(typeString!=null){ + jsonObject.put("name", typeString.get()); + } + jsonObject.put("config", getResConfig().toJson()); + jsonObject.put("entries", getEntryBlockArray().toJson()); + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + setTypeId((byte) json.getInt("id")); + String name= json.optString("name"); + if(name!=null){ + setTypeName(name); + } + getEntryBlockArray().fromJson(json.getJSONArray("entries")); + getResConfig().fromJson(json.getJSONObject("config")); + } + @Override public String toString(){ StringBuilder builder=new StringBuilder(); builder.append(getResConfig().toString()); diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java index 48161c2..7a563f2 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java @@ -4,6 +4,10 @@ import com.reandroid.lib.arsc.item.ResXmlString; import com.reandroid.lib.arsc.pool.ResXmlStringPool; import com.reandroid.lib.arsc.value.ValueType; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -219,6 +223,14 @@ public class AndroidManifestBlock extends ResXmlBlock{ builder.append("}"); return builder.toString(); } + public static AndroidManifestBlock load(File file) throws IOException { + return load(new FileInputStream(file)); + } + public static AndroidManifestBlock load(InputStream inputStream) throws IOException { + AndroidManifestBlock manifestBlock=new AndroidManifestBlock(); + manifestBlock.readBytes(inputStream); + return manifestBlock; + } public static final String TAG_manifest ="manifest"; public static final String TAG_uses_permission="uses-permission"; public static final String TAG_application ="application"; diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java index a4ed4da..b0d906b 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java @@ -6,8 +6,11 @@ import com.reandroid.lib.arsc.container.FixedBlockContainer; import com.reandroid.lib.arsc.item.*; import com.reandroid.lib.arsc.pool.ResXmlStringPool; import com.reandroid.lib.arsc.value.ValueType; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; -public class ResXmlAttribute extends FixedBlockContainer implements Comparable{ +public class ResXmlAttribute extends FixedBlockContainer + implements Comparable, JsonItem { private final IntegerItem mNamespaceReference; private final IntegerItem mNameReference; private final IntegerItem mValueStringReference; @@ -105,6 +108,17 @@ public class ResXmlAttribute extends FixedBlockContainer implements Comparable { private final ResXmlStringPool mResXmlStringPool; private final ResXmlIDMap mResXmlIDMap; private ResXmlElement mResXmlElement; @@ -129,6 +132,26 @@ public class ResXmlBlock extends BaseChunk { outputStream.close(); return length; } + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put(NAME_element, getResXmlElement().toJson()); + JSONArray pool =getStringPool().toJson(); + if(pool!=null){ + jsonObject.put(NAME_styled_strings, getResXmlElement().toJson()); + } + JSONArray idArray = getResXmlIDMap().getResXmlIDArray().toJson(); + if(idArray!=null){ + jsonObject.put("resource_ids", idArray); + } + return jsonObject; + } + + @Override + public void fromJson(JSONObject json) { + // TODO + throw new IllegalArgumentException("Not implemented yet"); + } public static boolean isResXmlBlock(File file){ if(file==null){ @@ -167,4 +190,6 @@ public class ResXmlBlock extends BaseChunk { ChunkType chunkType=headerBlock.getChunkType(); return chunkType==ChunkType.XML; } + private static final String NAME_element ="element"; + private static final String NAME_styled_strings="styled_strings"; } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java index 9ec14f6..e5fa8c1 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java @@ -9,6 +9,9 @@ import com.reandroid.lib.arsc.header.HeaderBlock; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.ResXmlString; import com.reandroid.lib.arsc.pool.ResXmlStringPool; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; +import org.json.JSONObject; import java.io.IOException; @@ -16,7 +19,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class ResXmlElement extends FixedBlockContainer { +public class ResXmlElement extends FixedBlockContainer implements JsonItem { private final BlockList mStartNamespaceList; private final SingleBlockContainer mStartElementContainer; private final BlockList mBody; @@ -492,6 +495,55 @@ public class ResXmlElement extends FixedBlockContainer { } } @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + ResXmlStartElement start = getStartElement(); + jsonObject.put(NAME_line, start.getLineNumber()); + int i=0; + JSONArray nsList=new JSONArray(); + for(ResXmlStartNamespace namespace:getStartNamespaceList()){ + JSONObject ns=new JSONObject(); + ns.put(NAME_namespace_uri, namespace.getUri()); + ns.put(NAME_namespace_prefix, namespace.getPrefix()); + nsList.put(i, ns); + i++; + } + if(i>0){ + jsonObject.put(NAME_namespaces, nsList); + } + jsonObject.put(NAME_name, start.getName()); + String uri=start.getUri(); + if(uri!=null){ + jsonObject.put(NAME_namespace_uri, uri); + jsonObject.put(NAME_namespace_prefix, start.getPrefix()); + } + JSONArray attrArray=start.getResXmlAttributeArray().toJson(); + jsonObject.put(NAME_attributes, attrArray); + i=0; + JSONArray childes=new JSONArray(); + for(ResXmlElement element:listElements()){ + childes.put(i, element.toJson()); + i++; + } + if(i>0){ + jsonObject.put(NAME_childes, childes); + } + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + ResXmlStartElement start = getStartElement(); + start.setLineNumber(json.getInt(NAME_line)); + String uri= json.optString(NAME_namespace_uri); + String prefix= json.optString(NAME_namespace_prefix); + if(uri!=null && prefix!=null){ + ResXmlStartNamespace ns = getOrCreateNamespace(uri, prefix); + start.setNamespaceReference(ns.getUriReference()); + } + JSONArray attributes = json.getJSONArray(NAME_attributes); + start.getResXmlAttributeArray().fromJson(attributes); + } + @Override public String toString(){ ResXmlStartElement start = getStartElement(); if(start!=null){ @@ -524,4 +576,12 @@ public class ResXmlElement extends FixedBlockContainer { public static final String NS_ANDROID_URI = "http://schemas.android.com/apk/res/android"; public static final String NS_ANDROID_PREFIX = "android"; + + private static final String NAME_name = "name"; + private static final String NAME_namespaces = "namespaces"; + private static final String NAME_namespace_uri = "namespace_uri"; + private static final String NAME_namespace_prefix = "namespace_name"; + private static final String NAME_line = "line"; + private static final String NAME_attributes = "attributes"; + private static final String NAME_childes = "childes"; } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java index 8e4679d..4987e98 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java @@ -74,6 +74,19 @@ public class ResXmlStartElement extends BaseXmlChunk { public ResXmlAttributeArray getResXmlAttributeArray(){ return mAttributeArray; } + + public String getUri(){ + int uriRef=getNamespaceReference(); + if(uriRef<0){ + return null; + } + ResXmlElement parentElement=getParentResXmlElement(); + ResXmlStartNamespace startNamespace=parentElement.getStartNamespaceByUriRef(uriRef); + if(startNamespace!=null){ + return startNamespace.getUri(); + } + return null; + } public String getPrefix(){ int uriRef=getNamespaceReference(); if(uriRef<0){ diff --git a/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java b/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java index 81d732f..f461fc3 100755 --- a/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java +++ b/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java @@ -12,6 +12,8 @@ import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.TypeString; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.ResConfig; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; @@ -19,7 +21,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -public class SpecTypePair extends BlockContainer { +public class SpecTypePair extends BlockContainer implements JsonItem { private final Block[] mChildes; private final SpecBlock mSpecBlock; private final TypeBlockArray mTypeBlockArray; @@ -140,4 +142,17 @@ public class SpecTypePair extends BlockContainer { public TypeString getTypeString(){ return getTypeBlockArray().getTypeString(); } + + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put("id", getSpecBlock().getTypeId()); + jsonObject.put("types", getTypeBlockArray().toJson()); + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + getSpecBlock().setTypeId((byte) json.getInt("id")); + getTypeBlockArray().fromJson(json.getJSONArray("types")); + } } diff --git a/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java b/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java index bebad35..c86139e 100755 --- a/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java +++ b/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java @@ -28,13 +28,28 @@ public class EntryGroup extends ItemGroup { if(specStringPool==null){ return false; } - String oldName=getSpecName(); - if(name.equals(oldName)){ - return false; + if(isAllSameSpec()){ + String oldName=getSpecName(); + if(name.equals(oldName)){ + return false; + } } SpecString specString=specStringPool.getOrCreate(name); return renameSpec(specString.getIndex()); } + private boolean isAllSameSpec(){ + EntryBlock first=null; + for(EntryBlock entryBlock:listItems()){ + if(first==null){ + first=entryBlock; + continue; + } + if(first.getSpecReference()!=entryBlock.getSpecReference()){ + return false; + } + } + return true; + } public boolean renameSpec(int specReference){ EntryBlock[] items=getItems(); if(items==null){ diff --git a/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java b/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java index 4199305..06a4fd4 100755 --- a/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java +++ b/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java @@ -1,10 +1,16 @@ package com.reandroid.lib.arsc.item; +import com.reandroid.lib.arsc.base.Block; +import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock; +import com.reandroid.lib.arsc.pool.ResXmlStringPool; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; + import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class ResXmlID extends IntegerItem { +public class ResXmlID extends IntegerItem implements JsonItem { private final List mReferencedList; public ResXmlID(int resId){ super(resId); @@ -47,6 +53,29 @@ public class ResXmlID extends IntegerItem { public void onIndexChanged(int oldIndex, int newIndex){ reUpdateReferences(newIndex); } + + private ResXmlStringPool getXmlStringPool(){ + Block parent=this; + while (parent!=null){ + if(parent instanceof ResXmlBlock){ + return ((ResXmlBlock)parent).getStringPool(); + } + parent=parent.getParent(); + } + return null; + } + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put("id", get()); + jsonObject.put("name", getXmlStringPool().get(getIndex()).getHtml()); + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + //TODO + throw new IllegalArgumentException("Not implemented yet"); + } @Override public String toString(){ return getIndex()+": "+String.format("0x%08x", get()); diff --git a/src/main/java/com/reandroid/lib/arsc/item/StringItem.java b/src/main/java/com/reandroid/lib/arsc/item/StringItem.java index d233bc4..a4d41d0 100755 --- a/src/main/java/com/reandroid/lib/arsc/item/StringItem.java +++ b/src/main/java/com/reandroid/lib/arsc/item/StringItem.java @@ -3,6 +3,8 @@ package com.reandroid.lib.arsc.item; import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.pool.BaseStringPool; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.io.IOException; @@ -15,7 +17,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class StringItem extends BlockItem { +public class StringItem extends BlockItem implements JsonItem { private String mCache; private boolean mUtf8; private final List mReferencedList; @@ -166,27 +168,54 @@ public class StringItem extends BlockItem { return new String(allStringBytes, offLen[0], offLen[1], StandardCharsets.UTF_16LE); } } - - + public boolean hasStyle(){ + StyleItem styleItem=getStyle(); + if(styleItem==null){ + return false; + } + return !styleItem.isNull(); + } public StyleItem getStyle(){ - BaseStringPool stringPool=getStringPool(); + BaseStringPool stringPool=getStringPool(); if(stringPool==null){ return null; } int index=getIndex(); return stringPool.getStyle(index); } - private BaseStringPool getStringPool(){ + private BaseStringPool getStringPool(){ Block parent=getParent(); while (parent!=null){ if(parent instanceof BaseStringPool){ - return (BaseStringPool)parent; + return (BaseStringPool)parent; } parent=parent.getParent(); } return null; } @Override + public JSONObject toJson() { + if(isNull()){ + return null; + } + JSONObject jsonObject=new JSONObject(); + jsonObject.put(NAME_string, get()); + StyleItem styleItem=getStyle(); + if(styleItem!=null){ + JSONObject styleJson=styleItem.toJson(); + if(styleJson!=null){ + jsonObject.put(NAME_style, styleJson); + } + } + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + String str = json.getString(NAME_string); + set(str); + throw new IllegalArgumentException("Not implemented"); + } + @Override public String toString(){ String str=get(); if(str==null){ @@ -332,4 +361,7 @@ public class StringItem extends BlockItem { private final CharsetDecoder UTF16LE_DECODER = StandardCharsets.UTF_16LE.newDecoder(); private final CharsetDecoder UTF8_DECODER = StandardCharsets.UTF_8.newDecoder(); + + private static final String NAME_string="string"; + private static final String NAME_style="style"; } diff --git a/src/main/java/com/reandroid/lib/arsc/item/StyleItem.java b/src/main/java/com/reandroid/lib/arsc/item/StyleItem.java index f166118..509ad0f 100755 --- a/src/main/java/com/reandroid/lib/arsc/item/StyleItem.java +++ b/src/main/java/com/reandroid/lib/arsc/item/StyleItem.java @@ -4,16 +4,19 @@ import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.model.StyleSpanInfo; import com.reandroid.lib.arsc.pool.BaseStringPool; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; +import org.json.JSONObject; import java.io.IOException; +import java.util.AbstractList; import java.util.ArrayList; import java.util.List; -public class StyleItem extends IntegerArray{ - private List mStyleList; +public class StyleItem extends IntegerArray implements JsonItem { + private List mSpanInfoList; public StyleItem() { super(); - mStyleList=createStyleList(); } private void setEndValue(int negOne){ super.put(size()-1, negOne); @@ -22,54 +25,67 @@ public class StyleItem extends IntegerArray{ return super.get(size()-1); } final Integer getStringRef(int index){ - int i=index*STYLE_PIECE_COUNT+STRING_REF; + int i=index * INTEGERS_COUNT + INDEX_STRING_REF; return super.get(i); } final void setStringRef(int index, int val){ - int i=index*STYLE_PIECE_COUNT+STRING_REF; + int i=index * INTEGERS_COUNT + INDEX_STRING_REF; super.put(i, val); } final Integer getFirstChar(int index){ - int i=index*STYLE_PIECE_COUNT+CHAR_FIRST; + int i=index * INTEGERS_COUNT + INDEX_CHAR_FIRST; return super.get(i); } final void setFirstChar(int index, int val){ - int i=index*STYLE_PIECE_COUNT+CHAR_FIRST; + int i=index * INTEGERS_COUNT + INDEX_CHAR_FIRST; super.put(i, val); } final Integer getLastChar(int index){ - int i=index*STYLE_PIECE_COUNT+CHAR_LAST; + int i=index * INTEGERS_COUNT + INDEX_CHAR_LAST; return super.get(i); } final void setLastChar(int index, int val){ - int i=index*STYLE_PIECE_COUNT+CHAR_LAST; + int i=index * INTEGERS_COUNT + INDEX_CHAR_LAST; super.put(i, val); } + public void addStylePiece(String tag, int firstChar, int lastChar){ + BaseStringPool stringPool = getStringPool(); + if(stringPool==null){ + throw new IllegalArgumentException("Null string pool, must be added to parent StyleArray first"); + } + StringItem stringItem=stringPool.getOrCreate(tag); + addStylePiece(stringItem.getIndex(), firstChar, lastChar); + } + public void addStylePiece(int refString, int firstChar, int lastChar){ + int index=getStylePieceCount(); + setStylePieceCount(index+1); + setStylePiece(index, refString, firstChar, lastChar); + } final void setStylePiece(int index, int refString, int firstChar, int lastChar){ - int i=index*STYLE_PIECE_COUNT; - super.put(i+STRING_REF, refString); - super.put(i+CHAR_FIRST, firstChar); - super.put(i+CHAR_LAST, lastChar); + int i=index * INTEGERS_COUNT; + super.put(i+ INDEX_STRING_REF, refString); + super.put(i+ INDEX_CHAR_FIRST, firstChar); + super.put(i+ INDEX_CHAR_LAST, lastChar); } final int[] getStylePiece(int index){ if(index<0||index>= getStylePieceCount()){ return null; } - int[] result=new int[STYLE_PIECE_COUNT]; - int i=index*STYLE_PIECE_COUNT; - result[STRING_REF]=super.get(i); - result[CHAR_FIRST]=super.get(i+CHAR_FIRST); - result[CHAR_LAST]=super.get(i+CHAR_LAST); + int[] result=new int[INTEGERS_COUNT]; + int i=index * INTEGERS_COUNT; + result[INDEX_STRING_REF]=super.get(i); + result[INDEX_CHAR_FIRST]=super.get(i+ INDEX_CHAR_FIRST); + result[INDEX_CHAR_LAST]=super.get(i+ INDEX_CHAR_LAST); return result; } final void setStylePiece(int index, int[] three){ - if(three==null || three.length copy=new ArrayList<>(mStyleList); + List copy=new ArrayList<>(getIntSpanInfoList()); Integer end= getEndValue(); if(end==null){ end=END_VALUE; @@ -117,42 +133,54 @@ public class StyleItem extends IntegerArray{ } setEndValue(end); } - final List getStyleList(){ - return mStyleList; + private List getIntSpanInfoList(){ + return new AbstractList() { + @Override + public int[] get(int i) { + return StyleItem.this.getStylePiece(i); + } + @Override + public int size() { + return StyleItem.this.getStylePieceCount(); + } + }; } - private List createStyleList(){ - List results=new ArrayList<>(); - int max=getStylePieceCount(); - for(int i=0;i getSpanInfoList(){ + if(mSpanInfoList!=null){ + return mSpanInfoList; } - return results; + mSpanInfoList = new AbstractList() { + @Override + public StyleSpanInfo get(int i) { + int ref=getStringRef(i); + return new StyleSpanInfo( + getStringFromPool(ref), + getFirstChar(i), + getLastChar(i)); + } + @Override + public int size() { + return getStylePieceCount(); + } + }; + return mSpanInfoList; } - final List getSpanInfo(){ - BaseStringPool stringPool= getStringPool(); + private String getStringFromPool(int ref){ + BaseStringPool stringPool = getStringPool(); if(stringPool==null){ return null; } - List allPiece=getStyleList(); - List results=new ArrayList<>(); - for(int[] piece:allPiece){ - StringItem stringItem=stringPool.get(piece[STRING_REF]); - if(stringItem==null){ - return null; - } - StyleSpanInfo info=new StyleSpanInfo(stringItem.get(), piece[CHAR_FIRST], piece[CHAR_LAST]); - results.add(info); - } - if(results.size()==0){ + StringItem stringItem = stringPool.get(ref); + if(stringItem==null){ return null; } - return results; + return stringItem.get(); } - private BaseStringPool getStringPool(){ + private BaseStringPool getStringPool(){ Block parent=getParent(); while (parent!=null){ if(parent instanceof BaseStringPool){ - return (BaseStringPool)parent; + return (BaseStringPool)parent; } parent=parent.getParent(); } @@ -163,8 +191,8 @@ public class StyleItem extends IntegerArray{ if(str==null){ return null; } - List allInfo=getSpanInfo(); - if(allInfo==null){ + List spanInfoList = getSpanInfoList(); + if(spanInfoList.size()==0){ return str; } StringBuilder builder=new StringBuilder(); @@ -173,9 +201,9 @@ public class StyleItem extends IntegerArray{ for(int i=0;i stringPool = getStringPool(); + if(stringPool==null){ + throw new IllegalArgumentException("Null string pool, must be added to parent StyleArray first"); + } + StringItem stringItem=stringPool.getOrCreate(tag); + setStylePiece(index, stringItem.getIndex(), first, last); + } + @Override + public JSONObject toJson() { + if(isNull()){ + return null; + } + JSONObject jsonObject=new JSONObject(); + JSONArray jsonArray=new JSONArray(); + int i=0; + for(StyleSpanInfo spanInfo:getSpanInfoList()){ + JSONObject jsonObjectSpan=spanInfo.toJson(); + jsonArray.put(i, jsonObjectSpan); + i++; + } + jsonObject.put(NAME_spans, jsonArray); + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + setNull(true); + if(json==null){ + return; + } + JSONArray jsonArray= json.getJSONArray(NAME_spans); + int length = jsonArray.length(); + for(int i=0;i listReferencedEntries(){ + List results=new ArrayList<>(); + for(ReferenceItem ref:getReferencedList()){ + if(!(ref instanceof ResValueReference)){ + continue; + } + EntryBlock entryBlock=((ResValueReference)ref).getEntryBlock(); + if(entryBlock==null){ + continue; + } + results.add(entryBlock); + } + return results; + } @Override public String toString(){ List refList = getReferencedList(); diff --git a/src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java b/src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java index 48823db..4cb7a25 100755 --- a/src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java +++ b/src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java @@ -1,44 +1,84 @@ package com.reandroid.lib.arsc.model; -public class StyleSpanInfo { - public final String TAG; - public final int FIRST; - public final int LAST; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; + +public class StyleSpanInfo implements JsonItem { + private String mTag; + private int mFirst; + private int mLast; public StyleSpanInfo(String tag, int first, int last){ - this.TAG=tag; - this.FIRST=first; - this.LAST=last; + this.mTag = tag; + this.mFirst = first; + this.mLast = last; } + public int getFirst() { + return mFirst; + } + public void setFirst(int first) { + this.mFirst = first; + } + public int getLast() { + return mLast; + } + public void setLast(int last) { + this.mLast = last; + } + public String getTag() { + return mTag; + } + public void setTag(String tag) { + this.mTag = tag; + } + public String getStartTag(){ - int i=TAG.indexOf(';'); + int i= mTag.indexOf(';'); StringBuilder builder=new StringBuilder(); builder.append('<'); if(i<0){ - builder.append(TAG); + builder.append(mTag); }else { - builder.append(TAG, 0, i); + builder.append(mTag, 0, i); builder.append(' '); - builder.append(TAG.substring(i+1)); + builder.append(mTag.substring(i+1)); } builder.append('>'); return builder.toString(); } public String getEndTag(){ - int i=TAG.indexOf(';'); + int i= mTag.indexOf(';'); StringBuilder builder=new StringBuilder(); builder.append('<'); builder.append('/'); if(i<0){ - builder.append(TAG); + builder.append(mTag); }else { - builder.append(TAG, 0, i); + builder.append(mTag, 0, i); } builder.append('>'); return builder.toString(); } @Override - public String toString(){ - return TAG+" ("+FIRST+", "+LAST+")"; + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put(NAME_tag, mTag); + jsonObject.put(NAME_first, mFirst); + jsonObject.put(NAME_last, mLast); + return jsonObject; } + @Override + public void fromJson(JSONObject json) { + setTag(json.getString(NAME_tag)); + setFirst(json.getInt(NAME_first)); + setLast(json.getInt(NAME_last)); + } + @Override + public String toString(){ + return mTag +" ("+ mFirst +", "+ mLast +")"; + } + + private static final String NAME_tag="tag"; + private static final String NAME_first="first"; + private static final String NAME_last="last"; } diff --git a/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java index d2e12aa..69f2fa4 100755 --- a/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java +++ b/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java @@ -10,12 +10,14 @@ import com.reandroid.lib.arsc.io.BlockLoad; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.*; import com.reandroid.lib.arsc.pool.builder.StyleBuilder; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONArray; import java.io.IOException; import java.util.*; -public abstract class BaseStringPool extends BaseChunk implements BlockLoad { +public abstract class BaseStringPool extends BaseChunk implements BlockLoad, JsonItem { private final IntegerItem mCountStrings; private final IntegerItem mCountStyles; private final ShortItem mFlagUtf8; @@ -64,6 +66,10 @@ public abstract class BaseStringPool extends BaseChunk imp mUniqueMap=new HashMap<>(); } + // call this after modifying string values + public void refreshUniqueIdMap(){ + reUpdateUniqueMap(); + } public void recreateStyles(){ StyleArray styleArray = getStyleArray(); //styleArray.clearChildes(); @@ -249,6 +255,16 @@ public abstract class BaseStringPool extends BaseChunk imp mArrayStrings.setUtf8(isUtf8Flag()); } } + @Override + public JSONArray toJson() { + return getStringsArray().toJson(); + } + @Override + public void fromJson(JSONArray json) { + getStringsArray().fromJson(json); + refreshUniqueIdMap(); + refresh(); + } private static final short UTF8_FLAG_VALUE=0x0100; private static final short FLAG_UTF8 = 0x0100; diff --git a/src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java index beb4ac6..1a9bba0 100755 --- a/src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java +++ b/src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java @@ -6,6 +6,7 @@ import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.ReferenceItem; import com.reandroid.lib.arsc.item.TableString; +import org.json.JSONArray; public class TableStringPool extends BaseStringPool { public TableStringPool(boolean is_utf8) { diff --git a/src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java b/src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java index a4094bd..10532b8 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java +++ b/src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java @@ -3,8 +3,10 @@ package com.reandroid.lib.arsc.value; import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.item.BlockItem; import com.reandroid.lib.arsc.item.ReferenceItem; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; -public abstract class BaseResValue extends BlockItem { +public abstract class BaseResValue extends BlockItem implements JsonItem { BaseResValue(int bytesLength){ super(bytesLength); } @@ -95,4 +97,10 @@ public abstract class BaseResValue extends BlockItem { byte getByte(int offset){ return getBytesInternal()[offset]; } + + static final String NAME_data = "data"; + static final String NAME_value_type="value_type"; + static final String NAME_id="id"; + static final String NAME_parent="parent"; + static final String NAME_items="items"; } diff --git a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java index ea61098..918eb55 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java @@ -46,7 +46,7 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI return null; } if(mReferenceItem==null){ - mReferenceItem=createReferenceItem(); + mReferenceItem=new ResValueReference(this); } return mReferenceItem; } @@ -62,23 +62,6 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI mReferenceItem=null; return entryBlock.removeTableReference(ref); } - private ReferenceItem createReferenceItem(){ - return new ReferenceItem() { - @Override - public void set(int val) { - if(getValueType()==ValueType.STRING){ - BaseResValueItem.this.setData(val); - } - } - @Override - public int get() { - if(getValueType()==ValueType.STRING){ - return BaseResValueItem.this.getData(); - } - return -1; - } - }; - } public ValueType getValueType(){ return ValueType.valueOf(getType()); diff --git a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java index bece53f..314568b 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java @@ -10,6 +10,8 @@ import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.*; import com.reandroid.lib.arsc.pool.SpecStringPool; import com.reandroid.lib.arsc.pool.TableStringPool; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.io.IOException; @@ -17,7 +19,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -public class EntryBlock extends Block{ +public class EntryBlock extends Block implements JsonItem { private ShortItem mHeaderSize; private ShortItem mFlags; private IntegerItem mSpecReference; @@ -84,7 +86,18 @@ public class EntryBlock extends Block{ resValueInt.setData(tableString.getIndex()); return resValueInt; } + public boolean getValueAsBoolean(){ + int data = ((ResValueInt)getResValue()).getData(); + return data!=0; + } public String getValueAsString(){ + TableString tableString= getValueAsTableString(); + if(tableString==null){ + return null; + } + return tableString.getHtml(); + } + public TableString getValueAsTableString(){ TableStringPool stringPool=getTableStringPool(); if(stringPool==null){ return null; @@ -98,7 +111,7 @@ public class EntryBlock extends Block{ if(tableString==null){ return null; } - return tableString.getHtml(); + return tableString; } private TableStringPool getTableStringPool(){ PackageBlock pkg=getPackageBlock(); @@ -298,6 +311,11 @@ public class EntryBlock extends Block{ } return specString.get(); } + private void setName(String name){ + PackageBlock packageBlock=getPackageBlock(); + EntryGroup entryGroup = packageBlock.getEntryGroup(getResourceId()); + entryGroup.renameSpec(name); + } public String getTypeName(){ TypeString typeString=getTypeString(); if(typeString==null){ @@ -479,6 +497,12 @@ public class EntryBlock extends Block{ private boolean isFlagsComplex(){ return ((mFlags.get() & FLAG_COMPLEX_MASK) != 0); } + public boolean isArray(){ + return ((mFlags.get() & FLAG_COMPLEX_MASK) != 0); + } + public void setIsArray(boolean is_array){ + setFlagComplex(is_array); + } @Override public void setNull(boolean is_null){ if(is_null){ @@ -596,6 +620,35 @@ public class EntryBlock extends Block{ updateSpecRef(); } @Override + public JSONObject toJson() { + if(isNull()){ + return null; + } + JSONObject jsonObject=new JSONObject(); + jsonObject.put(NAME_name, getSpecString().get()); + jsonObject.put(NAME_is_array, isArray()); + jsonObject.put(NAME_value, getResValue().toJson()); + return jsonObject; + } + + @Override + public void fromJson(JSONObject json) { + if(json==null){ + setNull(true); + return; + } + setName(json.getString(NAME_name)); + setNull(false); + BaseResValue baseResValue; + if(json.getBoolean(NAME_is_array)){ + baseResValue=new ResValueInt(); + }else { + baseResValue=new ResValueBag(); + } + setResValue(baseResValue); + baseResValue.fromJson(json.getJSONObject(NAME_value)); + } + @Override public String toString(){ StringBuilder builder=new StringBuilder(); builder.append(getClass().getSimpleName()); @@ -654,4 +707,8 @@ public class EntryBlock extends Block{ private final static short HEADER_COMPLEX=0x0010; private final static short HEADER_INT=0x0008; + private static final String NAME_name="name"; + private static final String NAME_is_array="is_array"; + private static final String NAME_value="value"; + } diff --git a/src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java b/src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java index 385a7a7..2d1bdc5 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java +++ b/src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java @@ -5,11 +5,13 @@ import com.reandroid.lib.arsc.base.BlockCounter; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.PackageName; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.io.IOException; import java.io.OutputStream; -public class LibraryInfo extends Block { +public class LibraryInfo extends Block implements JsonItem { private final IntegerItem mPackageId; private final PackageName mPackageName; @@ -74,6 +76,18 @@ public class LibraryInfo extends Block { mPackageName.readBytes(reader); } + @Override + public JSONObject toJson() { + JSONObject jsonObject=new JSONObject(); + jsonObject.put("id", getPackageId()); + jsonObject.put("name", getPackageName()); + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + setPackageId(json.getInt("id")); + setPackageName(json.getString("name")); + } @Override public String toString(){ StringBuilder builder=new StringBuilder(); diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java b/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java index a39d467..a287407 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java @@ -6,12 +6,13 @@ import com.reandroid.lib.arsc.io.BlockLoad; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.ByteArray; import com.reandroid.lib.arsc.item.IntegerItem; +import com.reandroid.lib.json.JsonItem; +import org.json.JSONObject; import java.io.IOException; import java.util.Arrays; -import java.util.Objects; -public class ResConfig extends FixedBlockContainer implements BlockLoad { +public class ResConfig extends FixedBlockContainer implements BlockLoad, JsonItem { private final IntegerItem configSize; private final ByteArray mValuesContainer; @@ -156,7 +157,17 @@ public class ResConfig extends FixedBlockContainer implements BlockLoad { } return mValuesContainer.get(OFFSET_languageIn1); } - public char[] getLanguage(){ + public String getLanguage(){ + return ResConfigHelper.decodeLanguage(getLanguageChars()); + } + public void setLanguage(String language){ + char[] chs=new char[2]; + if(language!=null){ + chs=language.toCharArray(); + } + setLanguage(chs); + } + public char[] getLanguageChars(){ byte b0=getLanguageIn0(); byte b1=getLanguageIn1(); return unpackLanguageOrRegion(b0, b1, 'a'); @@ -201,7 +212,10 @@ public class ResConfig extends FixedBlockContainer implements BlockLoad { } return mValuesContainer.get(OFFSET_countryIn1); } - public char[] getRegion(){ + public String getRegion(){ + return ResConfigHelper.decodeRegion(getRegionChars()); + } + public char[] getRegionChars(){ byte b0=getCountryIn0(); byte b1=getCountryIn1(); return unpackLanguageOrRegion(b0, b1, '0'); @@ -225,12 +239,18 @@ public class ResConfig extends FixedBlockContainer implements BlockLoad { } mValuesContainer.put(OFFSET_orientation, b); } - public byte getOrientation(){ + public byte getOrientationByte(){ if(getConfigSize()