mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-03 07:34:25 +02:00
V1.0.3 (to/from json convert)
This commit is contained in:
parent
1523b6341d
commit
3626e24c90
@ -23,7 +23,6 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
compile(files("$rootProject.projectDir/libs/ArchiveUtil.jar"))
|
||||
compile(files("$rootProject.projectDir/libs/json.jar"))
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
BIN
libs/json.jar
BIN
libs/json.jar
Binary file not shown.
@ -12,6 +12,7 @@ import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
@ -163,6 +164,46 @@ public class ApkModule {
|
||||
public void setLoadDefaultFramework(boolean loadDefaultFramework) {
|
||||
this.loadDefaultFramework = loadDefaultFramework;
|
||||
}
|
||||
public void convertToJson(File outDir) throws IOException {
|
||||
Set<String> convertedFiles=new HashSet<>();
|
||||
if(hasAndroidManifestBlock()){
|
||||
AndroidManifestBlock manifestBlock=getAndroidManifestBlock();
|
||||
String fileName=AndroidManifestBlock.FILE_NAME+ApkUtil.JSON_FILE_EXTENSION;
|
||||
File file=new File(outDir, fileName);
|
||||
manifestBlock.toJson().write(file);
|
||||
convertedFiles.add(AndroidManifestBlock.FILE_NAME);
|
||||
}
|
||||
if(hasTableBlock()){
|
||||
TableBlock tableBlock=getTableBlock();
|
||||
String fileName=TableBlock.FILE_NAME+ApkUtil.JSON_FILE_EXTENSION;
|
||||
File file=new File(outDir, fileName);
|
||||
tableBlock.toJson().write(file);
|
||||
convertedFiles.add(TableBlock.FILE_NAME);
|
||||
}
|
||||
List<ResFile> resFileList=listResFiles();
|
||||
for(ResFile resFile:resFileList){
|
||||
boolean convertOk=resFile.dumpToJson(outDir);
|
||||
if(convertOk){
|
||||
convertedFiles.add(resFile.getFilePath());
|
||||
}
|
||||
}
|
||||
List<InputSource> allSources = getApkArchive().listInputSources();
|
||||
for(InputSource inputSource:allSources){
|
||||
String path=inputSource.getAlias();
|
||||
if(convertedFiles.contains(path)){
|
||||
continue;
|
||||
}
|
||||
path=path.replace('/', File.separatorChar);
|
||||
File file=new File(outDir, path);
|
||||
File dir=file.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream=new FileOutputStream(file);
|
||||
inputSource.write(outputStream);
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
public static ApkModule loadApkFile(File apkFile) throws IOException {
|
||||
APKArchive archive=APKArchive.loadZippedApk(apkFile);
|
||||
return new ApkModule(archive);
|
||||
|
@ -12,4 +12,5 @@ public class ApkUtil {
|
||||
}
|
||||
return path;
|
||||
}
|
||||
public static final String JSON_FILE_EXTENSION=".a.json";
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
package com.reandroid.lib.apk;
|
||||
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ResFile {
|
||||
private final List<EntryBlock> entryBlockList;
|
||||
private final InputSource inputSource;
|
||||
private boolean mBinXml;
|
||||
private boolean mBinXmlChecked;
|
||||
public ResFile(InputSource inputSource, List<EntryBlock> entryBlockList){
|
||||
this.inputSource=inputSource;
|
||||
this.entryBlockList=entryBlockList;
|
||||
@ -24,6 +30,30 @@ public class ResFile {
|
||||
public InputSource getInputSource() {
|
||||
return inputSource;
|
||||
}
|
||||
public boolean isBinaryXml(){
|
||||
if(mBinXmlChecked){
|
||||
return mBinXml;
|
||||
}
|
||||
mBinXmlChecked=true;
|
||||
try {
|
||||
mBinXml=ResXmlBlock.isResXmlBlock(getInputSource().openStream());
|
||||
} catch (IOException exception) {
|
||||
}
|
||||
return mBinXml;
|
||||
}
|
||||
public boolean dumpToJson(File rootDir) throws IOException {
|
||||
if(!isBinaryXml()){
|
||||
return false;
|
||||
}
|
||||
String fileName=getFilePath()+ApkUtil.JSON_FILE_EXTENSION;
|
||||
fileName=fileName.replace('/', File.separatorChar);
|
||||
File file=new File(rootDir, fileName);
|
||||
ResXmlBlock resXmlBlock=new ResXmlBlock();
|
||||
resXmlBlock.readBytes(getInputSource().openStream());
|
||||
JSONObject jsonObject=resXmlBlock.toJson();
|
||||
jsonObject.write(file);
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return getFilePath();
|
||||
|
@ -3,12 +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;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
|
||||
public class EntryBlockArray extends OffsetBlockArray<EntryBlock> implements JsonItem<JSONArray> {
|
||||
public class EntryBlockArray extends OffsetBlockArray<EntryBlock> implements JSONConvert<JSONArray> {
|
||||
public EntryBlockArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){
|
||||
super(offsets, itemCount, itemStart);
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.container.SpecTypePair;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.value.LibraryInfo;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LibraryInfoArray extends BlockArray<LibraryInfo> implements JsonItem<JSONArray> {
|
||||
public class LibraryInfoArray extends BlockArray<LibraryInfo> implements JSONConvert<JSONArray> {
|
||||
private final IntegerItem mInfoCount;
|
||||
public LibraryInfoArray(IntegerItem infoCount){
|
||||
this.mInfoCount=infoCount;
|
||||
|
@ -3,18 +3,17 @@ package com.reandroid.lib.arsc.array;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.container.SpecTypePair;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class PackageArray extends BlockArray<PackageBlock> implements BlockLoad, JsonItem<JSONArray> {
|
||||
public class PackageArray extends BlockArray<PackageBlock> implements BlockLoad, JSONConvert<JSONArray> {
|
||||
private final IntegerItem mPackageCount;
|
||||
public PackageArray(IntegerItem packageCount){
|
||||
this.mPackageCount=packageCount;
|
||||
|
@ -2,10 +2,10 @@ package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.value.ResValueBagItem;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
|
||||
public class ResValueBagItemArray extends BlockArray<ResValueBagItem> implements JsonItem<JSONArray> {
|
||||
public class ResValueBagItemArray extends BlockArray<ResValueBagItem> implements JSONConvert<JSONArray> {
|
||||
public ResValueBagItemArray(){
|
||||
super();
|
||||
}
|
||||
|
@ -2,14 +2,13 @@ package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlStartElement;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlAttribute;
|
||||
import com.reandroid.lib.arsc.item.ShortItem;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -18,7 +17,7 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ResXmlAttributeArray extends BlockArray<ResXmlAttribute>
|
||||
implements Comparator<ResXmlAttribute>, JsonItem<JSONArray> {
|
||||
implements Comparator<ResXmlAttribute>, JSONConvert<JSONArray> {
|
||||
private final HeaderBlock mHeaderBlock;
|
||||
private final ShortItem mAttributeStart;
|
||||
private final ShortItem mAttributeCount;
|
||||
|
@ -4,15 +4,15 @@ import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ResXmlID;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResXmlIDArray extends BlockArray<ResXmlID> implements JsonItem<JSONArray> {
|
||||
public class ResXmlIDArray extends BlockArray<ResXmlID> {
|
||||
private final HeaderBlock mHeaderBlock;
|
||||
private final Map<Integer, ResXmlID> mResIdMap;
|
||||
private boolean mUpdated;
|
||||
@ -89,23 +89,4 @@ public class ResXmlIDArray extends BlockArray<ResXmlID> implements JsonItem<JSON
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SpecTypePairArray extends BlockArray<SpecTypePair> implements JsonItem<JSONArray> {
|
||||
public class SpecTypePairArray extends BlockArray<SpecTypePair> implements JSONConvert<JSONArray> {
|
||||
public SpecTypePairArray(){
|
||||
super();
|
||||
}
|
||||
|
@ -3,14 +3,14 @@ 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.StringItem;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class StringArray<T extends StringItem> extends OffsetBlockArray<T> implements JsonItem<JSONArray> {
|
||||
public abstract class StringArray<T extends StringItem> extends OffsetBlockArray<T> implements JSONConvert<JSONArray> {
|
||||
private boolean mUtf8;
|
||||
|
||||
public StringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
|
@ -5,12 +5,12 @@ import com.reandroid.lib.arsc.item.ByteArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.StyleItem;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StyleArray extends OffsetBlockArray<StyleItem> implements JsonItem<JSONArray> {
|
||||
public class StyleArray extends OffsetBlockArray<StyleItem> implements JSONConvert<JSONArray> {
|
||||
public StyleArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart) {
|
||||
super(offsets, itemCount, itemStart);
|
||||
setEndBytes(END_BYTE);
|
||||
|
@ -3,7 +3,6 @@ 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<TableString> {
|
||||
public TableStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
|
@ -10,16 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TypeBlockArray extends BlockArray<TypeBlock> implements JsonItem<JSONArray> {
|
||||
public class TypeBlockArray extends BlockArray<TypeBlock> implements JSONConvert<JSONArray> {
|
||||
private byte mTypeId;
|
||||
public TypeBlockArray(){
|
||||
super();
|
||||
|
@ -9,19 +9,18 @@ import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.PackageName;
|
||||
import com.reandroid.lib.arsc.item.ReferenceItem;
|
||||
import com.reandroid.lib.arsc.item.TypeString;
|
||||
import com.reandroid.lib.arsc.pool.SpecStringPool;
|
||||
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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class PackageBlock extends BaseChunk implements JsonItem<JSONObject> {
|
||||
public class PackageBlock extends BaseChunk implements JSONConvert<JSONObject> {
|
||||
private final IntegerItem mPackageId;
|
||||
private final PackageName mPackageName;
|
||||
|
||||
|
@ -7,12 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SpecBlock extends BaseTypeBlock implements BlockLoad , JsonItem<JSONObject> {
|
||||
public class SpecBlock extends BaseTypeBlock implements BlockLoad , JSONConvert<JSONObject> {
|
||||
private final IntegerArray mOffsets;
|
||||
public SpecBlock() {
|
||||
super(ChunkType.SPEC, 1);
|
||||
|
@ -1,23 +1,22 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.array.PackageArray;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
||||
import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class TableBlock extends BaseChunk implements JsonItem<JSONObject> {
|
||||
public class TableBlock extends BaseChunk implements JSONConvert<JSONObject> {
|
||||
private final IntegerItem mPackageCount;
|
||||
private final TableStringPool mTableStringPool;
|
||||
private final PackageArray mPackageArray;
|
||||
|
@ -8,14 +8,14 @@ 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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class TypeBlock extends BaseTypeBlock implements JsonItem<JSONObject> {
|
||||
public class TypeBlock extends BaseTypeBlock implements JSONConvert<JSONObject> {
|
||||
private final IntegerItem mEntriesStart;
|
||||
private final ResConfig mResConfig;
|
||||
private final IntegerArray mEntryOffsets;
|
||||
|
@ -0,0 +1,63 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.array.ResXmlIDArray;
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.item.ResXmlID;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ResIdBuilder implements Comparator<Integer> {
|
||||
private final Map<Integer, String> mIdNameMap;
|
||||
public ResIdBuilder(){
|
||||
this.mIdNameMap=new HashMap<>();
|
||||
}
|
||||
public void buildTo(ResXmlIDMap resXmlIDMap){
|
||||
ResXmlStringPool stringPool = resXmlIDMap.getXmlStringPool();
|
||||
StringArray<ResXmlString> xmlStringsArray = stringPool.getStringsArray();
|
||||
ResXmlIDArray xmlIDArray = resXmlIDMap.getResXmlIDArray();
|
||||
List<Integer> idList=getSortedIds();
|
||||
int size = idList.size();
|
||||
xmlStringsArray.ensureSize(size);
|
||||
xmlIDArray.ensureSize(size);
|
||||
for(int i=0;i<size;i++){
|
||||
ResXmlString xmlString = xmlStringsArray.get(i);
|
||||
if(xmlString.getReferencedList().size()>0){
|
||||
ResXmlString replaceXmlString=new ResXmlString(xmlString.isUtf8(), xmlString.get());
|
||||
xmlStringsArray.setItem(i, replaceXmlString);
|
||||
xmlStringsArray.add(xmlString);
|
||||
xmlString=replaceXmlString;
|
||||
}
|
||||
ResXmlID xmlID = xmlIDArray.get(i);
|
||||
if(xmlID.getReferencedList().size()>0){
|
||||
ResXmlID replaceXmlId = new ResXmlID(xmlID.get());
|
||||
xmlIDArray.setItem(i, replaceXmlId);
|
||||
xmlIDArray.add(xmlID);
|
||||
xmlID=replaceXmlId;
|
||||
}
|
||||
int resourceId = idList.get(i);
|
||||
String name = mIdNameMap.get(resourceId);
|
||||
xmlID.set(resourceId);
|
||||
xmlString.set(name);
|
||||
}
|
||||
}
|
||||
public void add(int id, String name){
|
||||
if(id==0){
|
||||
return;
|
||||
}
|
||||
if(name==null){
|
||||
name="";
|
||||
}
|
||||
mIdNameMap.put(id, name);
|
||||
}
|
||||
private List<Integer> getSortedIds(){
|
||||
List<Integer> results=new ArrayList<>(mIdNameMap.keySet());
|
||||
results.sort(this);
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int compare(Integer i1, Integer i2) {
|
||||
return i1.compareTo(i2);
|
||||
}
|
||||
}
|
@ -3,14 +3,15 @@ package com.reandroid.lib.arsc.chunk.xml;
|
||||
import com.reandroid.lib.arsc.array.ResXmlIDArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.FixedBlockContainer;
|
||||
import com.reandroid.lib.arsc.decoder.ValueDecoder;
|
||||
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;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
public class ResXmlAttribute extends FixedBlockContainer
|
||||
implements Comparable<ResXmlAttribute>, JsonItem<JSONObject> {
|
||||
implements Comparable<ResXmlAttribute>, JSONConvert<JSONObject> {
|
||||
private final IntegerItem mNamespaceReference;
|
||||
private final IntegerItem mNameReference;
|
||||
private final IntegerItem mValueStringReference;
|
||||
@ -364,7 +365,6 @@ public class ResXmlAttribute extends FixedBlockContainer
|
||||
jsonObject.put(NAME_name, getName());
|
||||
jsonObject.put(NAME_id, getNameResourceID());
|
||||
jsonObject.put(NAME_namespace_uri, getNamespace());
|
||||
jsonObject.put(NAME_namespace_prefix, getNamePrefix());
|
||||
ValueType valueType=getValueType();
|
||||
jsonObject.put(NAME_value_type, valueType.name());
|
||||
if(valueType==ValueType.STRING){
|
||||
@ -379,12 +379,13 @@ public class ResXmlAttribute extends FixedBlockContainer
|
||||
@Override
|
||||
public void fromJson(JSONObject json) {
|
||||
String name = json.getString(NAME_name);
|
||||
int id = json.optInt(NAME_id);
|
||||
int id = json.optInt(NAME_id, 0);
|
||||
setName(name, id);
|
||||
String uri= json.optString(NAME_namespace_uri);
|
||||
String prefix= json.optString(NAME_namespace_prefix);
|
||||
ResXmlStartNamespace ns = getParentResXmlElement().getOrCreateNamespace(uri, prefix);
|
||||
setNamespaceReference(ns.getUriReference());
|
||||
String uri= json.optString(NAME_namespace_uri, null);
|
||||
if(uri!=null){
|
||||
ResXmlStartNamespace ns = getParentResXmlElement().getStartNamespaceByUri(uri);
|
||||
setNamespaceReference(ns.getUriReference());
|
||||
}
|
||||
ValueType valueType=ValueType.fromName(json.getString(NAME_value_type));
|
||||
if(valueType==ValueType.STRING){
|
||||
setValueAsString(json.getString(NAME_data));
|
||||
@ -403,11 +404,19 @@ public class ResXmlAttribute extends FixedBlockContainer
|
||||
if(id>0){
|
||||
fullName=fullName+"(@"+String.format("0x%08x",id)+")";
|
||||
}
|
||||
String valStr=getValueString();
|
||||
String valStr;
|
||||
ValueType valueType=getValueType();
|
||||
if(valueType==ValueType.STRING){
|
||||
valStr=getValueAsString();
|
||||
}else if (valueType==ValueType.INT_BOOLEAN){
|
||||
valStr=String.valueOf(getValueAsBoolean());
|
||||
}else {
|
||||
valStr="["+valueType+"] "+getRawValue();
|
||||
}
|
||||
if(valStr!=null){
|
||||
return fullName+"=\""+valStr+"\"";
|
||||
}
|
||||
return fullName+"["+getValueType()+"]=\""+getRawValue()+"\"";
|
||||
return fullName+"["+valueType+"]=\""+getRawValue()+"\"";
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
@ -423,10 +432,9 @@ public class ResXmlAttribute extends FixedBlockContainer
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
private static final String NAME_id="id";
|
||||
private static final String NAME_value_type="value_type";
|
||||
private static final String NAME_name="name";
|
||||
private static final String NAME_namespace_uri ="namespace_uri";
|
||||
private static final String NAME_namespace_prefix ="namespace_name";
|
||||
private static final String NAME_data="data";
|
||||
static final String NAME_id="id";
|
||||
static final String NAME_value_type="value_type";
|
||||
static final String NAME_name="name";
|
||||
static final String NAME_namespace_uri ="namespace_uri";
|
||||
static final String NAME_data="data";
|
||||
}
|
||||
|
@ -6,13 +6,18 @@ import com.reandroid.lib.arsc.container.SingleBlockContainer;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ResXmlBlock extends BaseChunk implements JsonItem<JSONObject> {
|
||||
public class ResXmlBlock extends BaseChunk implements JSONConvert<JSONObject> {
|
||||
private final ResXmlStringPool mResXmlStringPool;
|
||||
private final ResXmlIDMap mResXmlIDMap;
|
||||
private ResXmlElement mResXmlElement;
|
||||
@ -135,22 +140,106 @@ public class ResXmlBlock extends BaseChunk implements JsonItem<JSONObject> {
|
||||
@Override
|
||||
public JSONObject toJson() {
|
||||
JSONObject jsonObject=new JSONObject();
|
||||
jsonObject.put(NAME_element, getResXmlElement().toJson());
|
||||
JSONArray pool =getStringPool().toJson();
|
||||
jsonObject.put(ResXmlBlock.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);
|
||||
jsonObject.put(ResXmlBlock.NAME_styled_strings, getResXmlElement().toJson());
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromJson(JSONObject json) {
|
||||
// TODO
|
||||
throw new IllegalArgumentException("Not implemented yet");
|
||||
onFromJson(json);
|
||||
ResXmlElement xmlElement=getResXmlElement();
|
||||
xmlElement.fromJson(json.optJSONObject(ResXmlBlock.NAME_element));
|
||||
refresh();
|
||||
}
|
||||
private void onFromJson(JSONObject json){
|
||||
List<JSONObject> attributeList=recursiveAttributes(json.optJSONObject(ResXmlBlock.NAME_element));
|
||||
buildResourceIds(attributeList);
|
||||
Set<String> allStrings=recursiveStrings(json.optJSONObject(ResXmlBlock.NAME_element));
|
||||
ResXmlStringPool stringPool = getStringPool();
|
||||
stringPool.addAllStrings(allStrings);
|
||||
stringPool.refresh();
|
||||
}
|
||||
private void buildResourceIds(List<JSONObject> attributeList){
|
||||
ResIdBuilder builder=new ResIdBuilder();
|
||||
for(JSONObject attribute:attributeList){
|
||||
int id=attribute.getInt(ResXmlAttribute.NAME_id);
|
||||
if(id==0){
|
||||
continue;
|
||||
}
|
||||
String name=attribute.getString(ResXmlAttribute.NAME_name);
|
||||
builder.add(id, name);
|
||||
}
|
||||
builder.buildTo(getResXmlIDMap());
|
||||
}
|
||||
private List<JSONObject> recursiveAttributes(JSONObject elementJson){
|
||||
List<JSONObject> results = new ArrayList<>();
|
||||
if(elementJson==null){
|
||||
return results;
|
||||
}
|
||||
JSONArray attributes = elementJson.optJSONArray(ResXmlElement.NAME_attributes);
|
||||
if(attributes != null){
|
||||
int length = attributes.length();
|
||||
for(int i=0; i<length; i++){
|
||||
JSONObject attr=attributes.optJSONObject(i);
|
||||
if(attr!=null){
|
||||
results.add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
JSONArray childElements = elementJson.optJSONArray(ResXmlElement.NAME_childes);
|
||||
if(childElements!=null){
|
||||
int length=childElements.length();
|
||||
for(int i=0;i<length;i++){
|
||||
JSONObject child=childElements.getJSONObject(i);
|
||||
results.addAll(recursiveAttributes(child));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
private Set<String> recursiveStrings(JSONObject elementJson){
|
||||
Set<String> results = new HashSet<>();
|
||||
if(elementJson==null){
|
||||
return results;
|
||||
}
|
||||
results.add(elementJson.optString(ResXmlElement.NAME_namespace_uri));
|
||||
results.add(elementJson.optString(ResXmlElement.NAME_name));
|
||||
JSONArray namespaces=elementJson.optJSONArray(ResXmlElement.NAME_namespaces);
|
||||
if(namespaces != null){
|
||||
int length = namespaces.length();
|
||||
for(int i=0; i<length; i++){
|
||||
JSONObject nsObject=namespaces.getJSONObject(i);
|
||||
results.add(nsObject.getString(ResXmlElement.NAME_namespace_uri));
|
||||
results.add(nsObject.getString(ResXmlElement.NAME_namespace_prefix));
|
||||
}
|
||||
}
|
||||
JSONArray attributes = elementJson.optJSONArray(ResXmlElement.NAME_attributes);
|
||||
if(attributes != null){
|
||||
int length = attributes.length();
|
||||
for(int i=0; i<length; i++){
|
||||
JSONObject attr=attributes.optJSONObject(i);
|
||||
if(attr==null){
|
||||
continue;
|
||||
}
|
||||
results.add(attr.getString(ResXmlAttribute.NAME_name));
|
||||
ValueType valueType=ValueType.fromName(attr.getString(ResXmlAttribute.NAME_value_type));
|
||||
if(valueType==ValueType.STRING){
|
||||
results.add(attr.getString(ResXmlAttribute.NAME_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
JSONArray childElements = elementJson.optJSONArray(ResXmlElement.NAME_childes);
|
||||
if(childElements!=null){
|
||||
int length=childElements.length();
|
||||
for(int i=0;i<length;i++){
|
||||
JSONObject child=childElements.getJSONObject(i);
|
||||
results.addAll(recursiveStrings(child));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static boolean isResXmlBlock(File file){
|
||||
|
@ -7,11 +7,10 @@ import com.reandroid.lib.arsc.container.FixedBlockContainer;
|
||||
import com.reandroid.lib.arsc.container.SingleBlockContainer;
|
||||
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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@ -19,7 +18,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ResXmlElement extends FixedBlockContainer implements JsonItem<JSONObject> {
|
||||
public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JSONObject> {
|
||||
private final BlockList<ResXmlStartNamespace> mStartNamespaceList;
|
||||
private final SingleBlockContainer<ResXmlStartElement> mStartElementContainer;
|
||||
private final BlockList<ResXmlElement> mBody;
|
||||
@ -50,23 +49,19 @@ public class ResXmlElement extends FixedBlockContainer implements JsonItem<JSONO
|
||||
}
|
||||
start.getResXmlAttributeArray().sortAttributes();
|
||||
}
|
||||
public ResXmlElement createChildElement(){
|
||||
return createChildElement(null);
|
||||
}
|
||||
public ResXmlElement createChildElement(String tag){
|
||||
int lineNo=getStartElement().getLineNumber()+1;
|
||||
ResXmlElement resXmlElement=new ResXmlElement();
|
||||
ResXmlStartElement startElement=new ResXmlStartElement();
|
||||
resXmlElement.setStartElement(startElement);
|
||||
|
||||
ResXmlEndElement endElement=new ResXmlEndElement();
|
||||
startElement.setResXmlEndElement(endElement);
|
||||
|
||||
resXmlElement.setEndElement(endElement);
|
||||
endElement.setResXmlStartElement(startElement);
|
||||
resXmlElement.newStartElement(lineNo);
|
||||
|
||||
addElement(resXmlElement);
|
||||
|
||||
resXmlElement.setTag(tag);
|
||||
int lineNo=getStartElement().getLineNumber()+1;
|
||||
startElement.setLineNumber(lineNo);
|
||||
endElement.setLineNumber(lineNo);
|
||||
if(tag!=null){
|
||||
resXmlElement.setTag(tag);
|
||||
}
|
||||
return resXmlElement;
|
||||
}
|
||||
public ResXmlAttribute createAndroidAttribute(String name, int resourceId){
|
||||
@ -285,6 +280,22 @@ public class ResXmlElement extends FixedBlockContainer implements JsonItem<JSONO
|
||||
mEndNamespaceList.add(item);
|
||||
}
|
||||
|
||||
private ResXmlStartElement newStartElement(int lineNo){
|
||||
ResXmlStartElement startElement=new ResXmlStartElement();
|
||||
setStartElement(startElement);
|
||||
|
||||
ResXmlEndElement endElement=new ResXmlEndElement();
|
||||
startElement.setResXmlEndElement(endElement);
|
||||
|
||||
setEndElement(endElement);
|
||||
endElement.setResXmlStartElement(startElement);
|
||||
|
||||
startElement.setLineNumber(lineNo);
|
||||
endElement.setLineNumber(lineNo);
|
||||
|
||||
return startElement;
|
||||
}
|
||||
|
||||
public ResXmlStartElement getStartElement(){
|
||||
return mStartElementContainer.getItem();
|
||||
}
|
||||
@ -515,7 +526,6 @@ public class ResXmlElement extends FixedBlockContainer implements JsonItem<JSONO
|
||||
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);
|
||||
@ -533,15 +543,45 @@ public class ResXmlElement extends FixedBlockContainer implements JsonItem<JSONO
|
||||
@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);
|
||||
int lineNo=json.optInt(NAME_line, 1);
|
||||
if(start==null){
|
||||
start = newStartElement(lineNo);
|
||||
}else {
|
||||
start.setLineNumber(lineNo);
|
||||
}
|
||||
JSONArray nsArray = json.optJSONArray(NAME_namespaces);
|
||||
if(nsArray!=null){
|
||||
int length=nsArray.length();
|
||||
for(int i=0;i<length;i++){
|
||||
JSONObject nsObject=nsArray.getJSONObject(i);
|
||||
String uri=nsObject.getString(NAME_namespace_uri);
|
||||
String prefix=nsObject.getString(NAME_namespace_prefix);
|
||||
getOrCreateNamespace(uri,prefix);
|
||||
}
|
||||
}
|
||||
setTag(json.getString(NAME_name));
|
||||
String uri = json.optString(NAME_namespace_uri, null);
|
||||
if(uri!=null){
|
||||
ResXmlStartNamespace ns = getStartNamespaceByUri(uri);
|
||||
if(ns==null){
|
||||
throw new IllegalArgumentException("Undefined uri: "
|
||||
+uri+", must be defined in array: "+NAME_namespaces);
|
||||
}
|
||||
start.setNamespaceReference(ns.getUriReference());
|
||||
}
|
||||
JSONArray attributes = json.getJSONArray(NAME_attributes);
|
||||
start.getResXmlAttributeArray().fromJson(attributes);
|
||||
JSONArray attributes = json.optJSONArray(NAME_attributes);
|
||||
if(attributes!=null){
|
||||
start.getResXmlAttributeArray().fromJson(attributes);
|
||||
}
|
||||
JSONArray childArray= json.optJSONArray(NAME_childes);
|
||||
if(childArray!=null){
|
||||
int length=childArray.length();
|
||||
for(int i=0;i<length;i++){
|
||||
JSONObject childObject=childArray.getJSONObject(i);
|
||||
ResXmlElement child = createChildElement();
|
||||
child.fromJson(childObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
@ -577,11 +617,11 @@ public class ResXmlElement extends FixedBlockContainer implements JsonItem<JSONO
|
||||
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";
|
||||
static final String NAME_name = "name";
|
||||
static final String NAME_namespaces = "namespaces";
|
||||
static final String NAME_namespace_uri = "namespace_uri";
|
||||
static final String NAME_namespace_prefix = "namespace_prefix";
|
||||
private static final String NAME_line = "line";
|
||||
private static final String NAME_attributes = "attributes";
|
||||
private static final String NAME_childes = "childes";
|
||||
static final String NAME_attributes = "attributes";
|
||||
static final String NAME_childes = "childes";
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.array.ResXmlIDArray;
|
||||
import com.reandroid.lib.arsc.chunk.BaseChunk;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ResXmlID;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@ -39,8 +41,14 @@ public class ResXmlIDMap extends BaseChunk {
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
super.onReadBytes(reader);
|
||||
ResXmlStringPool getXmlStringPool(){
|
||||
Block parent=this;
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlBlock){
|
||||
return ((ResXmlBlock)parent).getStringPool();
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -21,7 +21,7 @@ import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class SpecTypePair extends BlockContainer<Block> implements JsonItem<JSONObject> {
|
||||
public class SpecTypePair extends BlockContainer<Block> implements JSONConvert<JSONObject> {
|
||||
private final Block[] mChildes;
|
||||
private final SpecBlock mSpecBlock;
|
||||
private final TypeBlockArray mTypeBlockArray;
|
||||
|
@ -3,14 +3,14 @@ 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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ResXmlID extends IntegerItem implements JsonItem<JSONObject> {
|
||||
public class ResXmlID extends IntegerItem {
|
||||
private final List<ReferenceItem> mReferencedList;
|
||||
public ResXmlID(int resId){
|
||||
super(resId);
|
||||
@ -53,7 +53,17 @@ public class ResXmlID extends IntegerItem implements JsonItem<JSONObject> {
|
||||
public void onIndexChanged(int oldIndex, int newIndex){
|
||||
reUpdateReferences(newIndex);
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
ResXmlStringPool stringPool=getXmlStringPool();
|
||||
if(stringPool==null){
|
||||
return null;
|
||||
}
|
||||
ResXmlString xmlString = stringPool.get(getIndex());
|
||||
if(xmlString==null){
|
||||
return null;
|
||||
}
|
||||
return xmlString.getHtml();
|
||||
}
|
||||
private ResXmlStringPool getXmlStringPool(){
|
||||
Block parent=this;
|
||||
while (parent!=null){
|
||||
@ -65,18 +75,6 @@ public class ResXmlID extends IntegerItem implements JsonItem<JSONObject> {
|
||||
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());
|
||||
}
|
||||
|
@ -4,4 +4,8 @@ public class ResXmlString extends StringItem {
|
||||
public ResXmlString(boolean utf8) {
|
||||
super(utf8);
|
||||
}
|
||||
public ResXmlString(boolean utf8, String value) {
|
||||
this(utf8);
|
||||
set(value);
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@ -17,7 +17,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class StringItem extends BlockItem implements JsonItem<JSONObject> {
|
||||
public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
|
||||
private String mCache;
|
||||
private boolean mUtf8;
|
||||
private final List<ReferenceItem> mReferencedList;
|
||||
|
@ -4,16 +4,16 @@ 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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StyleItem extends IntegerArray implements JsonItem<JSONObject> {
|
||||
public class StyleItem extends IntegerArray implements JSONConvert<JSONObject> {
|
||||
private List<StyleSpanInfo> mSpanInfoList;
|
||||
public StyleItem() {
|
||||
super();
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.reandroid.lib.arsc.model;
|
||||
|
||||
import com.reandroid.lib.json.JsonItem;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
public class StyleSpanInfo implements JsonItem<JSONObject> {
|
||||
public class StyleSpanInfo implements JSONConvert<JSONObject> {
|
||||
private String mTag;
|
||||
private int mFirst;
|
||||
private int mLast;
|
||||
|
@ -10,14 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public abstract class BaseStringPool<T extends StringItem> extends BaseChunk implements BlockLoad, JsonItem<JSONArray> {
|
||||
public abstract class BaseStringPool<T extends StringItem> extends BaseChunk implements BlockLoad, JSONConvert<JSONArray>, Comparator<String> {
|
||||
private final IntegerItem mCountStrings;
|
||||
private final IntegerItem mCountStyles;
|
||||
private final ShortItem mFlagUtf8;
|
||||
@ -66,6 +66,19 @@ public abstract class BaseStringPool<T extends StringItem> extends BaseChunk imp
|
||||
mUniqueMap=new HashMap<>();
|
||||
|
||||
}
|
||||
public void addAllStrings(Collection<String> stringList){
|
||||
List<String> sortedList=new ArrayList<>(stringList);
|
||||
sortedList.sort(this);
|
||||
addAllStrings(sortedList);
|
||||
}
|
||||
public void addAllStrings(List<String> sortedStringList){
|
||||
// call refreshUniqueIdMap() just in case elements modified somewhere
|
||||
refreshUniqueIdMap();
|
||||
|
||||
for(String str:sortedStringList){
|
||||
getOrCreate(str);
|
||||
}
|
||||
}
|
||||
// call this after modifying string values
|
||||
public void refreshUniqueIdMap(){
|
||||
reUpdateUniqueMap();
|
||||
@ -265,6 +278,10 @@ public abstract class BaseStringPool<T extends StringItem> extends BaseChunk imp
|
||||
refreshUniqueIdMap();
|
||||
refresh();
|
||||
}
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
private static final short UTF8_FLAG_VALUE=0x0100;
|
||||
|
||||
private static final short FLAG_UTF8 = 0x0100;
|
||||
|
@ -4,9 +4,7 @@ import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.array.TableStringArray;
|
||||
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<TableString> {
|
||||
public TableStringPool(boolean is_utf8) {
|
||||
|
@ -3,10 +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;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
public abstract class BaseResValue extends BlockItem implements JsonItem<JSONObject> {
|
||||
public abstract class BaseResValue extends BlockItem implements JSONConvert<JSONObject> {
|
||||
BaseResValue(int bytesLength){
|
||||
super(bytesLength);
|
||||
}
|
||||
|
@ -10,8 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@ -19,7 +19,7 @@ import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EntryBlock extends Block implements JsonItem<JSONObject> {
|
||||
public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
private ShortItem mHeaderSize;
|
||||
private ShortItem mFlags;
|
||||
private IntegerItem mSpecReference;
|
||||
@ -630,7 +630,6 @@ public class EntryBlock extends Block implements JsonItem<JSONObject> {
|
||||
jsonObject.put(NAME_value, getResValue().toJson());
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromJson(JSONObject json) {
|
||||
if(json==null){
|
||||
|
@ -5,13 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class LibraryInfo extends Block implements JsonItem<JSONObject> {
|
||||
public class LibraryInfo extends Block implements JSONConvert<JSONObject> {
|
||||
private final IntegerItem mPackageId;
|
||||
private final PackageName mPackageName;
|
||||
|
||||
|
@ -6,13 +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 com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ResConfig extends FixedBlockContainer implements BlockLoad, JsonItem<JSONObject> {
|
||||
public class ResConfig extends FixedBlockContainer implements BlockLoad, JSONConvert<JSONObject> {
|
||||
|
||||
private final IntegerItem configSize;
|
||||
private final ByteArray mValuesContainer;
|
||||
|
@ -6,8 +6,7 @@ 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.ReferenceItem;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
@ -3,7 +3,7 @@ package com.reandroid.lib.arsc.value;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.item.ReferenceItem;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
public class ResValueBagItem extends BaseResValueItem{
|
||||
|
||||
|
@ -2,7 +2,7 @@ package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.decoder.ValueDecoder;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
import org.json.JSONObject;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
public class ResValueInt extends BaseResValueItem {
|
||||
public ResValueInt() {
|
||||
|
165
src/main/java/com/reandroid/lib/json/CDL.java
Normal file
165
src/main/java/com/reandroid/lib/json/CDL.java
Normal file
@ -0,0 +1,165 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public class CDL {
|
||||
|
||||
private static String getValue(JSONTokener x) throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuilder sb;
|
||||
do {
|
||||
c = x.next();
|
||||
} while (c == ' ' || c == '\t');
|
||||
switch (c) {
|
||||
case 0:
|
||||
return null;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuilder();
|
||||
for (;;) {
|
||||
c = x.next();
|
||||
if (c == q) {
|
||||
//Handle escaped double-quote
|
||||
char nextC = x.next();
|
||||
if(nextC != '\"') {
|
||||
// if our quote was the end of the file, don't step
|
||||
if(nextC > 0) {
|
||||
x.back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == 0 || c == '\n' || c == '\r') {
|
||||
throw x.syntaxError("Missing close quote '" + q + "'.");
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
case ',':
|
||||
x.back();
|
||||
return "";
|
||||
default:
|
||||
x.back();
|
||||
return x.nextTo(',');
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
|
||||
JSONArray ja = new JSONArray();
|
||||
for (;;) {
|
||||
String value = getValue(x);
|
||||
char c = x.next();
|
||||
if (value == null ||
|
||||
(ja.length() == 0 && value.length() == 0 && c != ',')) {
|
||||
return null;
|
||||
}
|
||||
ja.put(value);
|
||||
for (;;) {
|
||||
if (c == ',') {
|
||||
break;
|
||||
}
|
||||
if (c != ' ') {
|
||||
if (c == '\n' || c == '\r' || c == 0) {
|
||||
return ja;
|
||||
}
|
||||
throw x.syntaxError("Bad character '" + c + "' (" +
|
||||
(int)c + ").");
|
||||
}
|
||||
c = x.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
|
||||
throws JSONException {
|
||||
JSONArray ja = rowToJSONArray(x);
|
||||
return ja != null ? ja.toJSONObject(names) : null;
|
||||
}
|
||||
|
||||
public static String rowToString(JSONArray ja) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
Object object = ja.opt(i);
|
||||
if (object != null) {
|
||||
String string = object.toString();
|
||||
if (string.length() > 0 && (string.indexOf(',') >= 0 ||
|
||||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
|
||||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
|
||||
sb.append('"');
|
||||
int length = string.length();
|
||||
for (int j = 0; j < length; j += 1) {
|
||||
char c = string.charAt(j);
|
||||
if (c >= ' ' && c != '"') {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
sb.append('"');
|
||||
} else {
|
||||
sb.append(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new JSONTokener(string));
|
||||
}
|
||||
|
||||
public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
|
||||
return toJSONArray(rowToJSONArray(x), x);
|
||||
}
|
||||
|
||||
public static JSONArray toJSONArray(JSONArray names, String string)
|
||||
throws JSONException {
|
||||
return toJSONArray(names, new JSONTokener(string));
|
||||
}
|
||||
|
||||
public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
|
||||
throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONArray ja = new JSONArray();
|
||||
for (;;) {
|
||||
JSONObject jo = rowToJSONObject(names, x);
|
||||
if (jo == null) {
|
||||
break;
|
||||
}
|
||||
ja.put(jo);
|
||||
}
|
||||
if (ja.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
JSONObject jo = ja.optJSONObject(0);
|
||||
if (jo != null) {
|
||||
JSONArray names = jo.names();
|
||||
if (names != null) {
|
||||
return rowToString(names) + toString(names, ja);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toString(JSONArray names, JSONArray ja)
|
||||
throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
JSONObject jo = ja.optJSONObject(i);
|
||||
if (jo != null) {
|
||||
sb.append(rowToString(jo.toJSONArray(names)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
138
src/main/java/com/reandroid/lib/json/Cookie.java
Normal file
138
src/main/java/com/reandroid/lib/json/Cookie.java
Normal file
@ -0,0 +1,138 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class Cookie {
|
||||
|
||||
public static String escape(String string) {
|
||||
char c;
|
||||
String s = string.trim();
|
||||
int length = s.length();
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
c = s.charAt(i);
|
||||
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
|
||||
sb.append('%');
|
||||
sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
|
||||
sb.append(Character.forDigit((char)(c & 0x0f), 16));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
public static JSONObject toJSONObject(String string) {
|
||||
final JSONObject jo = new JSONObject();
|
||||
String name;
|
||||
Object value;
|
||||
|
||||
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
|
||||
name = unescape(x.nextTo('=').trim());
|
||||
//per RFC6265, if the name is blank, the cookie should be ignored.
|
||||
if("".equals(name)) {
|
||||
throw new JSONException("Cookies must have a 'name'");
|
||||
}
|
||||
jo.put("name", name);
|
||||
// per RFC6265, if there is no '=', the cookie should be ignored.
|
||||
// the 'next' call here throws an exception if the '=' is not found.
|
||||
x.next('=');
|
||||
jo.put("value", unescape(x.nextTo(';')).trim());
|
||||
// discard the ';'
|
||||
x.next();
|
||||
// parse the remaining cookie attributes
|
||||
while (x.more()) {
|
||||
name = unescape(x.nextTo("=;")).trim().toLowerCase(Locale.ROOT);
|
||||
// don't allow a cookies attributes to overwrite it's name or value.
|
||||
if("name".equalsIgnoreCase(name)) {
|
||||
throw new JSONException("Illegal attribute name: 'name'");
|
||||
}
|
||||
if("value".equalsIgnoreCase(name)) {
|
||||
throw new JSONException("Illegal attribute name: 'value'");
|
||||
}
|
||||
// check to see if it's a flag property
|
||||
if (x.next() != '=') {
|
||||
value = Boolean.TRUE;
|
||||
} else {
|
||||
value = unescape(x.nextTo(';')).trim();
|
||||
x.next();
|
||||
}
|
||||
// only store non-blank attributes
|
||||
if(!"".equals(name) && !"".equals(value)) {
|
||||
jo.put(name, value);
|
||||
}
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String name = null;
|
||||
Object value = null;
|
||||
for(String key : jo.keySet()){
|
||||
if("name".equalsIgnoreCase(key)) {
|
||||
name = jo.getString(key).trim();
|
||||
}
|
||||
if("value".equalsIgnoreCase(key)) {
|
||||
value=jo.getString(key).trim();
|
||||
}
|
||||
if(name != null && value != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(name == null || "".equals(name.trim())) {
|
||||
throw new JSONException("Cookie does not have a name");
|
||||
}
|
||||
if(value == null) {
|
||||
value = "";
|
||||
}
|
||||
|
||||
sb.append(escape(name));
|
||||
sb.append("=");
|
||||
sb.append(escape((String)value));
|
||||
|
||||
for(String key : jo.keySet()){
|
||||
if("name".equalsIgnoreCase(key)
|
||||
|| "value".equalsIgnoreCase(key)) {
|
||||
// already processed above
|
||||
continue;
|
||||
}
|
||||
value = jo.opt(key);
|
||||
if(value instanceof Boolean) {
|
||||
if(Boolean.TRUE.equals(value)) {
|
||||
sb.append(';').append(escape(key));
|
||||
}
|
||||
// don't emit false values
|
||||
} else {
|
||||
sb.append(';')
|
||||
.append(escape(key))
|
||||
.append('=')
|
||||
.append(escape(value.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String unescape(String string) {
|
||||
int length = string.length();
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char c = string.charAt(i);
|
||||
if (c == '+') {
|
||||
c = ' ';
|
||||
} else if (c == '%' && i + 2 < length) {
|
||||
int d = JSONTokener.dehexchar(string.charAt(i + 1));
|
||||
int e = JSONTokener.dehexchar(string.charAt(i + 2));
|
||||
if (d >= 0 && e >= 0) {
|
||||
c = (char)(d * 16 + e);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
35
src/main/java/com/reandroid/lib/json/CookieList.java
Normal file
35
src/main/java/com/reandroid/lib/json/CookieList.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public class CookieList {
|
||||
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
while (x.more()) {
|
||||
String name = Cookie.unescape(x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put(name, Cookie.unescape(x.nextTo(';')));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
boolean b = false;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
// Don't use the new entrySet API to maintain Android support
|
||||
for (final String key : jo.keySet()) {
|
||||
final Object value = jo.opt(key);
|
||||
if (!JSONObject.NULL.equals(value)) {
|
||||
if (b) {
|
||||
sb.append(';');
|
||||
}
|
||||
sb.append(Cookie.escape(key));
|
||||
sb.append("=");
|
||||
sb.append(Cookie.escape(value.toString()));
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
81
src/main/java/com/reandroid/lib/json/HTTP.java
Normal file
81
src/main/java/com/reandroid/lib/json/HTTP.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class HTTP {
|
||||
|
||||
/** Carriage return/line feed. */
|
||||
public static final String CRLF = "\r\n";
|
||||
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
HTTPTokener x = new HTTPTokener(string);
|
||||
String token;
|
||||
|
||||
token = x.nextToken();
|
||||
if (token.toUpperCase(Locale.ROOT).startsWith("HTTP")) {
|
||||
|
||||
// Response
|
||||
|
||||
jo.put("HTTP-Version", token);
|
||||
jo.put("Status-Code", x.nextToken());
|
||||
jo.put("Reason-Phrase", x.nextTo('\0'));
|
||||
x.next();
|
||||
|
||||
} else {
|
||||
|
||||
// Request
|
||||
|
||||
jo.put("Method", token);
|
||||
jo.put("Request-URI", x.nextToken());
|
||||
jo.put("HTTP-Version", x.nextToken());
|
||||
}
|
||||
|
||||
// Fields
|
||||
|
||||
while (x.more()) {
|
||||
String name = x.nextTo(':');
|
||||
x.next(':');
|
||||
jo.put(name, x.nextTo('\0'));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (jo.has("Status-Code") && jo.has("Reason-Phrase")) {
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Status-Code"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Reason-Phrase"));
|
||||
} else if (jo.has("Method") && jo.has("Request-URI")) {
|
||||
sb.append(jo.getString("Method"));
|
||||
sb.append(' ');
|
||||
sb.append('"');
|
||||
sb.append(jo.getString("Request-URI"));
|
||||
sb.append('"');
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
} else {
|
||||
throw new JSONException("Not enough material for an HTTP header.");
|
||||
}
|
||||
sb.append(CRLF);
|
||||
// Don't use the new entrySet API to maintain Android support
|
||||
for (final String key : jo.keySet()) {
|
||||
String value = jo.optString(key);
|
||||
if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) &&
|
||||
!"Reason-Phrase".equals(key) && !"Method".equals(key) &&
|
||||
!"Request-URI".equals(key) && !JSONObject.NULL.equals(value)) {
|
||||
sb.append(key);
|
||||
sb.append(": ");
|
||||
sb.append(jo.optString(key));
|
||||
sb.append(CRLF);
|
||||
}
|
||||
}
|
||||
sb.append(CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
36
src/main/java/com/reandroid/lib/json/HTTPTokener.java
Normal file
36
src/main/java/com/reandroid/lib/json/HTTPTokener.java
Normal file
@ -0,0 +1,36 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public class HTTPTokener extends JSONTokener {
|
||||
|
||||
public HTTPTokener(String string) {
|
||||
super(string);
|
||||
}
|
||||
public String nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == '"' || c == '\'') {
|
||||
q = c;
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (c < ' ') {
|
||||
throw syntaxError("Unterminated string.");
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (c == 0 || Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
}
|
||||
}
|
755
src/main/java/com/reandroid/lib/json/JSONArray.java
Normal file
755
src/main/java/com/reandroid/lib/json/JSONArray.java
Normal file
@ -0,0 +1,755 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JSONArray extends JSONItem implements Iterable<Object> {
|
||||
|
||||
private final ArrayList<Object> myArrayList;
|
||||
|
||||
public JSONArray() {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public JSONArray(JSONTokener x) throws JSONException {
|
||||
this();
|
||||
if (x.nextClean() != '[') {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
|
||||
char nextChar = x.nextClean();
|
||||
if (nextChar == 0) {
|
||||
// array is unclosed. No ']' found, instead EOF
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
if (nextChar != ']') {
|
||||
x.back();
|
||||
for (;;) {
|
||||
if (x.nextClean() == ',') {
|
||||
x.back();
|
||||
this.myArrayList.add(JSONObject.NULL);
|
||||
} else {
|
||||
x.back();
|
||||
this.myArrayList.add(x.nextValue());
|
||||
}
|
||||
switch (x.nextClean()) {
|
||||
case 0:
|
||||
// array is unclosed. No ']' found, instead EOF
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
case ',':
|
||||
nextChar = x.nextClean();
|
||||
if (nextChar == 0) {
|
||||
// array is unclosed. No ']' found, instead EOF
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
if (nextChar == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JSONArray(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
}
|
||||
|
||||
public JSONArray(Collection<?> collection) {
|
||||
if (collection == null) {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
} else {
|
||||
this.myArrayList = new ArrayList<Object>(collection.size());
|
||||
this.addAll(collection, true);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONArray(Iterable<?> iter) {
|
||||
this();
|
||||
if (iter == null) {
|
||||
return;
|
||||
}
|
||||
this.addAll(iter, true);
|
||||
}
|
||||
|
||||
public JSONArray(JSONArray array) {
|
||||
if (array == null) {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
} else {
|
||||
// shallow copy directly the internal array lists as any wrapping
|
||||
// should have been done already in the original JSONArray
|
||||
this.myArrayList = new ArrayList<Object>(array.myArrayList);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONArray(Object array) throws JSONException {
|
||||
this();
|
||||
if (!array.getClass().isArray()) {
|
||||
throw new JSONException(
|
||||
"JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
this.addAll(array, true);
|
||||
}
|
||||
|
||||
public JSONArray(int initialCapacity) throws JSONException {
|
||||
if (initialCapacity < 0) {
|
||||
throw new JSONException(
|
||||
"JSONArray initial capacity cannot be negative.");
|
||||
}
|
||||
this.myArrayList = new ArrayList<Object>(initialCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return this.myArrayList.iterator();
|
||||
}
|
||||
|
||||
public Object get(int index) throws JSONException {
|
||||
Object object = this.opt(index);
|
||||
if (object == null) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
public boolean getBoolean(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object.equals(Boolean.FALSE)
|
||||
|| (object instanceof String && ((String) object)
|
||||
.equalsIgnoreCase("false"))) {
|
||||
return false;
|
||||
} else if (object.equals(Boolean.TRUE)
|
||||
|| (object instanceof String && ((String) object)
|
||||
.equalsIgnoreCase("true"))) {
|
||||
return true;
|
||||
}
|
||||
throw wrongValueFormatException(index, "boolean", null);
|
||||
}
|
||||
|
||||
public double getDouble(int index) throws JSONException {
|
||||
final Object object = this.get(index);
|
||||
if(object instanceof Number) {
|
||||
return ((Number)object).doubleValue();
|
||||
}
|
||||
try {
|
||||
return Double.parseDouble(object.toString());
|
||||
} catch (Exception e) {
|
||||
throw wrongValueFormatException(index, "double", e);
|
||||
}
|
||||
}
|
||||
|
||||
public float getFloat(int index) throws JSONException {
|
||||
final Object object = this.get(index);
|
||||
if(object instanceof Number) {
|
||||
return ((Float)object).floatValue();
|
||||
}
|
||||
try {
|
||||
return Float.parseFloat(object.toString());
|
||||
} catch (Exception e) {
|
||||
throw wrongValueFormatException(index, "float", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Number getNumber(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
if (object instanceof Number) {
|
||||
return (Number)object;
|
||||
}
|
||||
return JSONObject.stringToNumber(object.toString());
|
||||
} catch (Exception e) {
|
||||
throw wrongValueFormatException(index, "number", e);
|
||||
}
|
||||
}
|
||||
|
||||
public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONException {
|
||||
E val = optEnum(clazz, index);
|
||||
if(val==null) {
|
||||
// JSONException should really take a throwable argument.
|
||||
// If it did, I would re-implement this with the Enum.valueOf
|
||||
// method and place any thrown exception in the JSONException
|
||||
throw wrongValueFormatException(index, "enum of type "
|
||||
+ JSONObject.quote(clazz.getSimpleName()), null);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public BigDecimal getBigDecimal (int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
BigDecimal val = JSONObject.objectToBigDecimal(object, null);
|
||||
if(val == null) {
|
||||
throw wrongValueFormatException(index, "BigDecimal", object, null);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public BigInteger getBigInteger (int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
BigInteger val = JSONObject.objectToBigInteger(object, null);
|
||||
if(val == null) {
|
||||
throw wrongValueFormatException(index, "BigInteger", object, null);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public int getInt(int index) throws JSONException {
|
||||
final Object object = this.get(index);
|
||||
if(object instanceof Number) {
|
||||
return ((Number)object).intValue();
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(object.toString());
|
||||
} catch (Exception e) {
|
||||
throw wrongValueFormatException(index, "int", e);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONArray getJSONArray(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONArray) {
|
||||
return (JSONArray) object;
|
||||
}
|
||||
throw wrongValueFormatException(index, "JSONArray", null);
|
||||
}
|
||||
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof JSONObject) {
|
||||
return (JSONObject) object;
|
||||
}
|
||||
throw wrongValueFormatException(index, "JSONObject", null);
|
||||
}
|
||||
|
||||
public long getLong(int index) throws JSONException {
|
||||
final Object object = this.get(index);
|
||||
if(object instanceof Number) {
|
||||
return ((Number)object).longValue();
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(object.toString());
|
||||
} catch (Exception e) {
|
||||
throw wrongValueFormatException(index, "long", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if (object instanceof String) {
|
||||
return (String) object;
|
||||
}
|
||||
throw wrongValueFormatException(index, "String", null);
|
||||
}
|
||||
|
||||
public boolean isNull(int index) {
|
||||
return JSONObject.NULL.equals(this.opt(index));
|
||||
}
|
||||
|
||||
public String join(String separator) throws JSONException {
|
||||
int len = this.length();
|
||||
if (len == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(
|
||||
JSONObject.valueToString(this.myArrayList.get(0)));
|
||||
|
||||
for (int i = 1; i < len; i++) {
|
||||
sb.append(separator)
|
||||
.append(JSONObject.valueToString(this.myArrayList.get(i)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return this.myArrayList.size();
|
||||
}
|
||||
|
||||
public Object opt(int index) {
|
||||
return (index < 0 || index >= this.length()) ? null : this.myArrayList
|
||||
.get(index);
|
||||
}
|
||||
|
||||
public boolean optBoolean(int index) {
|
||||
return this.optBoolean(index, false);
|
||||
}
|
||||
|
||||
public boolean optBoolean(int index, boolean defaultValue) {
|
||||
try {
|
||||
return this.getBoolean(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public double optDouble(int index) {
|
||||
return this.optDouble(index, Double.NaN);
|
||||
}
|
||||
|
||||
public double optDouble(int index, double defaultValue) {
|
||||
final Number val = this.optNumber(index, null);
|
||||
if (val == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
final double doubleValue = val.doubleValue();
|
||||
// if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
|
||||
// return defaultValue;
|
||||
// }
|
||||
return doubleValue;
|
||||
}
|
||||
|
||||
public float optFloat(int index) {
|
||||
return this.optFloat(index, Float.NaN);
|
||||
}
|
||||
|
||||
public float optFloat(int index, float defaultValue) {
|
||||
final Number val = this.optNumber(index, null);
|
||||
if (val == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
final float floatValue = val.floatValue();
|
||||
// if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
|
||||
// return floatValue;
|
||||
// }
|
||||
return floatValue;
|
||||
}
|
||||
|
||||
public int optInt(int index) {
|
||||
return this.optInt(index, 0);
|
||||
}
|
||||
|
||||
public int optInt(int index, int defaultValue) {
|
||||
final Number val = this.optNumber(index, null);
|
||||
if (val == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return val.intValue();
|
||||
}
|
||||
|
||||
public <E extends Enum<E>> E optEnum(Class<E> clazz, int index) {
|
||||
return this.optEnum(clazz, index, null);
|
||||
}
|
||||
|
||||
public <E extends Enum<E>> E optEnum(Class<E> clazz, int index, E defaultValue) {
|
||||
try {
|
||||
Object val = this.opt(index);
|
||||
if (JSONObject.NULL.equals(val)) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (clazz.isAssignableFrom(val.getClass())) {
|
||||
// we just checked it!
|
||||
@SuppressWarnings("unchecked")
|
||||
E myE = (E) val;
|
||||
return myE;
|
||||
}
|
||||
return Enum.valueOf(clazz, val.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return defaultValue;
|
||||
} catch (NullPointerException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public BigInteger optBigInteger(int index, BigInteger defaultValue) {
|
||||
Object val = this.opt(index);
|
||||
return JSONObject.objectToBigInteger(val, defaultValue);
|
||||
}
|
||||
|
||||
public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
|
||||
Object val = this.opt(index);
|
||||
return JSONObject.objectToBigDecimal(val, defaultValue);
|
||||
}
|
||||
|
||||
public JSONArray optJSONArray(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONArray ? (JSONArray) o : null;
|
||||
}
|
||||
|
||||
public JSONObject optJSONObject(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONObject ? (JSONObject) o : null;
|
||||
}
|
||||
|
||||
public long optLong(int index) {
|
||||
return this.optLong(index, 0);
|
||||
}
|
||||
|
||||
public long optLong(int index, long defaultValue) {
|
||||
final Number val = this.optNumber(index, null);
|
||||
if (val == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return val.longValue();
|
||||
}
|
||||
|
||||
public Number optNumber(int index) {
|
||||
return this.optNumber(index, null);
|
||||
}
|
||||
|
||||
public Number optNumber(int index, Number defaultValue) {
|
||||
Object val = this.opt(index);
|
||||
if (JSONObject.NULL.equals(val)) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (val instanceof Number){
|
||||
return (Number) val;
|
||||
}
|
||||
|
||||
if (val instanceof String) {
|
||||
try {
|
||||
return JSONObject.stringToNumber((String) val);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String optString(int index) {
|
||||
return this.optString(index, "");
|
||||
}
|
||||
|
||||
public String optString(int index, String defaultValue) {
|
||||
Object object = this.opt(index);
|
||||
return JSONObject.NULL.equals(object) ? defaultValue : object
|
||||
.toString();
|
||||
}
|
||||
|
||||
public JSONArray put(boolean value) {
|
||||
return this.put(value ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
|
||||
public JSONArray put(Collection<?> value) {
|
||||
return this.put(new JSONArray(value));
|
||||
}
|
||||
|
||||
public JSONArray put(double value) throws JSONException {
|
||||
return this.put(Double.valueOf(value));
|
||||
}
|
||||
|
||||
|
||||
public JSONArray put(float value) throws JSONException {
|
||||
return this.put(Float.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(int value) {
|
||||
return this.put(Integer.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(long value) {
|
||||
return this.put(Long.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(Map<?, ?> value) {
|
||||
return this.put(new JSONObject(value));
|
||||
}
|
||||
|
||||
public JSONArray put(Object value) {
|
||||
JSONObject.testValidity(value);
|
||||
this.myArrayList.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONArray put(int index, boolean value) throws JSONException {
|
||||
return this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
|
||||
public JSONArray put(int index, Collection<?> value) throws JSONException {
|
||||
return this.put(index, new JSONArray(value));
|
||||
}
|
||||
|
||||
public JSONArray put(int index, double value) throws JSONException {
|
||||
return this.put(index, Double.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(int index, float value) throws JSONException {
|
||||
return this.put(index, Float.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(int index, int value) throws JSONException {
|
||||
return this.put(index, Integer.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(int index, long value) throws JSONException {
|
||||
return this.put(index, Long.valueOf(value));
|
||||
}
|
||||
|
||||
public JSONArray put(int index, Map<?, ?> value) throws JSONException {
|
||||
this.put(index, new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
if (index < 0) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
if (index < this.length()) {
|
||||
JSONObject.testValidity(value);
|
||||
this.myArrayList.set(index, value);
|
||||
return this;
|
||||
}
|
||||
if(index == this.length()){
|
||||
// simple append
|
||||
return this.put(value);
|
||||
}
|
||||
// if we are inserting past the length, we want to grow the array all at once
|
||||
// instead of incrementally.
|
||||
this.myArrayList.ensureCapacity(index + 1);
|
||||
while (index != this.length()) {
|
||||
// we don't need to test validity of NULL objects
|
||||
this.myArrayList.add(JSONObject.NULL);
|
||||
}
|
||||
return this.put(value);
|
||||
}
|
||||
|
||||
public JSONArray putAll(Collection<?> collection) {
|
||||
this.addAll(collection, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public JSONArray putAll(Iterable<?> iter) {
|
||||
this.addAll(iter, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONArray putAll(JSONArray array) {
|
||||
// directly copy the elements from the source array to this one
|
||||
// as all wrapping should have been done already in the source.
|
||||
this.myArrayList.addAll(array.myArrayList);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONArray putAll(Object array) throws JSONException {
|
||||
this.addAll(array, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Object query(String jsonPointer) {
|
||||
return query(new JSONPointer(jsonPointer));
|
||||
}
|
||||
|
||||
|
||||
public Object query(JSONPointer jsonPointer) {
|
||||
return jsonPointer.queryFrom(this);
|
||||
}
|
||||
|
||||
|
||||
public Object optQuery(String jsonPointer) {
|
||||
return optQuery(new JSONPointer(jsonPointer));
|
||||
}
|
||||
|
||||
|
||||
public Object optQuery(JSONPointer jsonPointer) {
|
||||
try {
|
||||
return jsonPointer.queryFrom(this);
|
||||
} catch (JSONPointerException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Object remove(int index) {
|
||||
return index >= 0 && index < this.length()
|
||||
? this.myArrayList.remove(index)
|
||||
: null;
|
||||
}
|
||||
|
||||
public boolean similar(Object other) {
|
||||
if (!(other instanceof JSONArray)) {
|
||||
return false;
|
||||
}
|
||||
int len = this.length();
|
||||
if (len != ((JSONArray)other).length()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
Object valueThis = this.myArrayList.get(i);
|
||||
Object valueOther = ((JSONArray)other).myArrayList.get(i);
|
||||
if(valueThis == valueOther) {
|
||||
continue;
|
||||
}
|
||||
if(valueThis == null) {
|
||||
return false;
|
||||
}
|
||||
if (valueThis instanceof JSONObject) {
|
||||
if (!((JSONObject)valueThis).similar(valueOther)) {
|
||||
return false;
|
||||
}
|
||||
} else if (valueThis instanceof JSONArray) {
|
||||
if (!((JSONArray)valueThis).similar(valueOther)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!valueThis.equals(valueOther)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if (names == null || names.isEmpty() || this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jo = new JSONObject(names.length());
|
||||
for (int i = 0; i < names.length(); i += 1) {
|
||||
jo.put(names.getString(i), this.opt(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer write(Writer writer, int indentFactor, int indent)
|
||||
throws JSONException {
|
||||
try {
|
||||
boolean needsComma = false;
|
||||
int length = this.length();
|
||||
writer.write('[');
|
||||
|
||||
if (length == 1) {
|
||||
try {
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(0),
|
||||
indentFactor, indent);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("Unable to write JSONArray value at index: 0", e);
|
||||
}
|
||||
} else if (length != 0) {
|
||||
final int newIndent = indent + indentFactor;
|
||||
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
if (needsComma) {
|
||||
writer.write(',');
|
||||
}
|
||||
if (indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, newIndent);
|
||||
try {
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(i),
|
||||
indentFactor, newIndent);
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("Unable to write JSONArray value at index: " + i, e);
|
||||
}
|
||||
needsComma = true;
|
||||
}
|
||||
if (indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, indent);
|
||||
}
|
||||
writer.write(']');
|
||||
return writer;
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Object> toList() {
|
||||
List<Object> results = new ArrayList<Object>(this.myArrayList.size());
|
||||
for (Object element : this.myArrayList) {
|
||||
if (element == null || JSONObject.NULL.equals(element)) {
|
||||
results.add(null);
|
||||
} else if (element instanceof JSONArray) {
|
||||
results.add(((JSONArray) element).toList());
|
||||
} else if (element instanceof JSONObject) {
|
||||
results.add(((JSONObject) element).toMap());
|
||||
} else {
|
||||
results.add(element);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.myArrayList.isEmpty();
|
||||
}
|
||||
|
||||
private void addAll(Collection<?> collection, boolean wrap) {
|
||||
this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size());
|
||||
if (wrap) {
|
||||
for (Object o: collection){
|
||||
this.put(JSONObject.wrap(o));
|
||||
}
|
||||
} else {
|
||||
for (Object o: collection){
|
||||
this.put(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addAll(Iterable<?> iter, boolean wrap) {
|
||||
if (wrap) {
|
||||
for (Object o: iter){
|
||||
this.put(JSONObject.wrap(o));
|
||||
}
|
||||
} else {
|
||||
for (Object o: iter){
|
||||
this.put(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addAll(Object array, boolean wrap) throws JSONException {
|
||||
if (array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
this.myArrayList.ensureCapacity(this.myArrayList.size() + length);
|
||||
if (wrap) {
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(JSONObject.wrap(Array.get(array, i)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(Array.get(array, i));
|
||||
}
|
||||
}
|
||||
} else if (array instanceof JSONArray) {
|
||||
// use the built in array list `addAll` as all object
|
||||
// wrapping should have been completed in the original
|
||||
// JSONArray
|
||||
this.myArrayList.addAll(((JSONArray)array).myArrayList);
|
||||
} else if (array instanceof Collection) {
|
||||
this.addAll((Collection<?>)array, wrap);
|
||||
} else if (array instanceof Iterable) {
|
||||
this.addAll((Iterable<?>)array, wrap);
|
||||
} else {
|
||||
throw new JSONException(
|
||||
"JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static JSONException wrongValueFormatException(
|
||||
int idx,
|
||||
String valueType,
|
||||
Throwable cause) {
|
||||
return new JSONException(
|
||||
"JSONArray[" + idx + "] is not a " + valueType + "."
|
||||
, cause);
|
||||
}
|
||||
|
||||
|
||||
private static JSONException wrongValueFormatException(
|
||||
int idx,
|
||||
String valueType,
|
||||
Object value,
|
||||
Throwable cause) {
|
||||
return new JSONException(
|
||||
"JSONArray[" + idx + "] is not a " + valueType + " (" + value + ")."
|
||||
, cause);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public interface JsonItem<T> {
|
||||
public interface JSONConvert<T extends JSONItem> {
|
||||
public T toJson();
|
||||
public void fromJson(T json);
|
||||
}
|
19
src/main/java/com/reandroid/lib/json/JSONException.java
Normal file
19
src/main/java/com/reandroid/lib/json/JSONException.java
Normal file
@ -0,0 +1,19 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public class JSONException extends IllegalArgumentException {
|
||||
/** Serialization ID */
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
public JSONException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public JSONException(final Throwable cause) {
|
||||
super(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
}
|
49
src/main/java/com/reandroid/lib/json/JSONItem.java
Normal file
49
src/main/java/com/reandroid/lib/json/JSONItem.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public abstract class JSONItem {
|
||||
public abstract Writer write(Writer writer, int indentFactor, int indent) throws JSONException;
|
||||
|
||||
public void write(File file) throws IOException{
|
||||
write(file, INDENT_FACTOR);
|
||||
}
|
||||
public void write(File file, int indentFactor) throws IOException{
|
||||
File dir=file.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream=new FileOutputStream(file);
|
||||
write(outputStream, indentFactor);
|
||||
outputStream.close();
|
||||
}
|
||||
public void write(OutputStream outputStream) throws IOException {
|
||||
write(outputStream, INDENT_FACTOR);
|
||||
}
|
||||
public void write(OutputStream outputStream, int indentFactor) throws IOException {
|
||||
Writer writer=new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
writer= write(writer, indentFactor, 0);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
public Writer write(Writer writer) throws JSONException {
|
||||
return this.write(writer, 0, 0);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return this.toString(0);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public String toString(int indentFactor) throws JSONException {
|
||||
StringWriter w = new StringWriter();
|
||||
synchronized (w.getBuffer()) {
|
||||
return this.write(w, indentFactor, 0).toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int INDENT_FACTOR=1;
|
||||
}
|
416
src/main/java/com/reandroid/lib/json/JSONML.java
Normal file
416
src/main/java/com/reandroid/lib/json/JSONML.java
Normal file
@ -0,0 +1,416 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public class JSONML {
|
||||
|
||||
private static Object parse(
|
||||
XMLTokener x,
|
||||
boolean arrayForm,
|
||||
JSONArray ja,
|
||||
boolean keepStrings
|
||||
) throws JSONException {
|
||||
String attribute;
|
||||
char c;
|
||||
String closeTag = null;
|
||||
int i;
|
||||
JSONArray newja = null;
|
||||
JSONObject newjo = null;
|
||||
Object token;
|
||||
String tagName = null;
|
||||
|
||||
// Test for and skip past these forms:
|
||||
// <!-- ... -->
|
||||
// <![ ... ]]>
|
||||
// <! ... >
|
||||
// <? ... ?>
|
||||
|
||||
while (true) {
|
||||
if (!x.more()) {
|
||||
throw x.syntaxError("Bad XML");
|
||||
}
|
||||
token = x.nextContent();
|
||||
if (token == XML.LT) {
|
||||
token = x.nextToken();
|
||||
if (token instanceof Character) {
|
||||
if (token == XML.SLASH) {
|
||||
|
||||
// Close tag </
|
||||
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw new JSONException(
|
||||
"Expected a closing name instead of '" +
|
||||
token + "'.");
|
||||
}
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return token;
|
||||
} else if (token == XML.BANG) {
|
||||
|
||||
// <!
|
||||
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
} else {
|
||||
x.back();
|
||||
}
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if (token.equals("CDATA") && x.next() == '[') {
|
||||
if (ja != null) {
|
||||
ja.put(x.nextCDATA());
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == XML.LT) {
|
||||
i += 1;
|
||||
} else if (token == XML.GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
}
|
||||
} else if (token == XML.QUEST) {
|
||||
|
||||
// <?
|
||||
|
||||
x.skipPast("?>");
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
|
||||
// Open tag <
|
||||
|
||||
} else {
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Bad tagName '" + token + "'.");
|
||||
}
|
||||
tagName = (String)token;
|
||||
newja = new JSONArray();
|
||||
newjo = new JSONObject();
|
||||
if (arrayForm) {
|
||||
newja.put(tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newja);
|
||||
}
|
||||
} else {
|
||||
newjo.put("tagName", tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newjo);
|
||||
}
|
||||
}
|
||||
token = null;
|
||||
for (;;) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (!(token instanceof String)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// attribute = value
|
||||
|
||||
attribute = (String)token;
|
||||
if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) {
|
||||
throw x.syntaxError("Reserved attribute.");
|
||||
}
|
||||
token = x.nextToken();
|
||||
if (token == XML.EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token));
|
||||
token = null;
|
||||
} else {
|
||||
newjo.accumulate(attribute, "");
|
||||
}
|
||||
}
|
||||
if (arrayForm && newjo.length() > 0) {
|
||||
newja.put(newjo);
|
||||
}
|
||||
|
||||
// Empty tag <.../>
|
||||
|
||||
if (token == XML.SLASH) {
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
}
|
||||
return newjo;
|
||||
}
|
||||
|
||||
// Content, between <...> and </...>
|
||||
|
||||
} else {
|
||||
if (token != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
closeTag = (String)parse(x, arrayForm, newja, keepStrings);
|
||||
if (closeTag != null) {
|
||||
if (!closeTag.equals(tagName)) {
|
||||
throw x.syntaxError("Mismatched '" + tagName +
|
||||
"' and '" + closeTag + "'");
|
||||
}
|
||||
tagName = null;
|
||||
if (!arrayForm && newja.length() > 0) {
|
||||
newjo.put("childNodes", newja);
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
}
|
||||
return newjo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ja != null) {
|
||||
ja.put(token instanceof String
|
||||
? keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token)
|
||||
: token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return (JSONArray)parse(new XMLTokener(string), true, null, false);
|
||||
}
|
||||
public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException {
|
||||
return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings);
|
||||
}
|
||||
public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException {
|
||||
return (JSONArray)parse(x, true, null, keepStrings);
|
||||
}
|
||||
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
|
||||
return (JSONArray)parse(x, true, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
|
||||
* Comments, prologs, DTDs, and <pre>{@code <[ [ ]]>}</pre> are ignored.
|
||||
* @param string The XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return (JSONObject)parse(new XMLTokener(string), false, null, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
|
||||
* Comments, prologs, DTDs, and <pre>{@code <[ [ ]]>}</pre> are ignored.
|
||||
* @param string The XML source text.
|
||||
* @param keepStrings If true, then values will not be coerced into boolean
|
||||
* or numeric values and will instead be left as strings
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
|
||||
return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
|
||||
* Comments, prologs, DTDs, and <pre>{@code <[ [ ]]>}</pre> are ignored.
|
||||
* @param x An XMLTokener of the XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
|
||||
return (JSONObject)parse(x, false, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
|
||||
* Comments, prologs, DTDs, and <pre>{@code <[ [ ]]>}</pre> are ignored.
|
||||
* @param x An XMLTokener of the XML source text.
|
||||
* @param keepStrings If true, then values will not be coerced into boolean
|
||||
* or numeric values and will instead be left as strings
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException {
|
||||
return (JSONObject)parse(x, false, null, keepStrings);
|
||||
}
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
int i;
|
||||
JSONObject jo;
|
||||
int length;
|
||||
Object object;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String tagName;
|
||||
|
||||
// Emit <tagName
|
||||
|
||||
tagName = ja.getString(0);
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
object = ja.opt(1);
|
||||
if (object instanceof JSONObject) {
|
||||
i = 2;
|
||||
jo = (JSONObject)object;
|
||||
|
||||
// Emit the attributes
|
||||
|
||||
// Don't use the new entrySet API to maintain Android support
|
||||
for (final String key : jo.keySet()) {
|
||||
final Object value = jo.opt(key);
|
||||
XML.noSpace(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value.toString()));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
}
|
||||
|
||||
// Emit content in body
|
||||
|
||||
length = ja.length();
|
||||
if (i >= length) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
do {
|
||||
object = ja.get(i);
|
||||
i += 1;
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject)object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray)object));
|
||||
} else {
|
||||
sb.append(object.toString());
|
||||
}
|
||||
}
|
||||
} while (i < length);
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i;
|
||||
JSONArray ja;
|
||||
int length;
|
||||
Object object;
|
||||
String tagName;
|
||||
Object value;
|
||||
|
||||
//Emit <tagName
|
||||
|
||||
tagName = jo.optString("tagName");
|
||||
if (tagName == null) {
|
||||
return XML.escape(jo.toString());
|
||||
}
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
//Emit the attributes
|
||||
|
||||
// Don't use the new entrySet API to maintain Android support
|
||||
for (final String key : jo.keySet()) {
|
||||
if (!"tagName".equals(key) && !"childNodes".equals(key)) {
|
||||
XML.noSpace(key);
|
||||
value = jo.opt(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value.toString()));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Emit content in body
|
||||
|
||||
ja = jo.optJSONArray("childNodes");
|
||||
if (ja == null) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
object = ja.get(i);
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject)object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray)object));
|
||||
} else {
|
||||
sb.append(object.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
1405
src/main/java/com/reandroid/lib/json/JSONObject.java
Normal file
1405
src/main/java/com/reandroid/lib/json/JSONObject.java
Normal file
File diff suppressed because it is too large
Load Diff
168
src/main/java/com/reandroid/lib/json/JSONPointer.java
Normal file
168
src/main/java/com/reandroid/lib/json/JSONPointer.java
Normal file
@ -0,0 +1,168 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class JSONPointer {
|
||||
|
||||
// used for URL encoding and decoding
|
||||
private static final String ENCODING = "utf-8";
|
||||
|
||||
public static class Builder {
|
||||
|
||||
// Segments for the eventual JSONPointer string
|
||||
private final List<String> refTokens = new ArrayList<String>();
|
||||
|
||||
public JSONPointer build() {
|
||||
return new JSONPointer(this.refTokens);
|
||||
}
|
||||
|
||||
public Builder append(String token) {
|
||||
if (token == null) {
|
||||
throw new NullPointerException("token cannot be null");
|
||||
}
|
||||
this.refTokens.add(token);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder append(int arrayIndex) {
|
||||
this.refTokens.add(String.valueOf(arrayIndex));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
// Segments for the JSONPointer string
|
||||
private final List<String> refTokens;
|
||||
|
||||
public JSONPointer(final String pointer) {
|
||||
if (pointer == null) {
|
||||
throw new NullPointerException("pointer cannot be null");
|
||||
}
|
||||
if (pointer.isEmpty() || pointer.equals("#")) {
|
||||
this.refTokens = Collections.emptyList();
|
||||
return;
|
||||
}
|
||||
String refs;
|
||||
if (pointer.startsWith("#/")) {
|
||||
refs = pointer.substring(2);
|
||||
try {
|
||||
refs = URLDecoder.decode(refs, ENCODING);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else if (pointer.startsWith("/")) {
|
||||
refs = pointer.substring(1);
|
||||
} else {
|
||||
throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'");
|
||||
}
|
||||
this.refTokens = new ArrayList<String>();
|
||||
int slashIdx = -1;
|
||||
int prevSlashIdx = 0;
|
||||
do {
|
||||
prevSlashIdx = slashIdx + 1;
|
||||
slashIdx = refs.indexOf('/', prevSlashIdx);
|
||||
if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) {
|
||||
// found 2 slashes in a row ( obj//next )
|
||||
// or single slash at the end of a string ( obj/test/ )
|
||||
this.refTokens.add("");
|
||||
} else if (slashIdx >= 0) {
|
||||
final String token = refs.substring(prevSlashIdx, slashIdx);
|
||||
this.refTokens.add(unescape(token));
|
||||
} else {
|
||||
// last item after separator, or no separator at all.
|
||||
final String token = refs.substring(prevSlashIdx);
|
||||
this.refTokens.add(unescape(token));
|
||||
}
|
||||
} while (slashIdx >= 0);
|
||||
// using split does not take into account consecutive separators or "ending nulls"
|
||||
//for (String token : refs.split("/")) {
|
||||
// this.refTokens.add(unescape(token));
|
||||
//}
|
||||
}
|
||||
|
||||
public JSONPointer(List<String> refTokens) {
|
||||
this.refTokens = new ArrayList<String>(refTokens);
|
||||
}
|
||||
|
||||
private static String unescape(String token) {
|
||||
return token.replace("~1", "/").replace("~0", "~")
|
||||
.replace("\\\"", "\"")
|
||||
.replace("\\\\", "\\");
|
||||
}
|
||||
|
||||
public Object queryFrom(Object document) throws JSONPointerException {
|
||||
if (this.refTokens.isEmpty()) {
|
||||
return document;
|
||||
}
|
||||
Object current = document;
|
||||
for (String token : this.refTokens) {
|
||||
if (current instanceof JSONObject) {
|
||||
current = ((JSONObject) current).opt(unescape(token));
|
||||
} else if (current instanceof JSONArray) {
|
||||
current = readByIndexToken(current, token);
|
||||
} else {
|
||||
throw new JSONPointerException(format(
|
||||
"value [%s] is not an array or object therefore its key %s cannot be resolved", current,
|
||||
token));
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException {
|
||||
try {
|
||||
int index = Integer.parseInt(indexToken);
|
||||
JSONArray currentArr = (JSONArray) current;
|
||||
if (index >= currentArr.length()) {
|
||||
throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken,
|
||||
Integer.valueOf(currentArr.length())));
|
||||
}
|
||||
try {
|
||||
return currentArr.get(index);
|
||||
} catch (JSONException e) {
|
||||
throw new JSONPointerException("Error reading value at index position " + index, e);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new JSONPointerException(format("%s is not an array index", indexToken), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder rval = new StringBuilder("");
|
||||
for (String token: this.refTokens) {
|
||||
rval.append('/').append(escape(token));
|
||||
}
|
||||
return rval.toString();
|
||||
}
|
||||
|
||||
private static String escape(String token) {
|
||||
return token.replace("~", "~0")
|
||||
.replace("/", "~1")
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
public String toURIFragment() {
|
||||
try {
|
||||
StringBuilder rval = new StringBuilder("#");
|
||||
for (String token : this.refTokens) {
|
||||
rval.append('/').append(URLEncoder.encode(token, ENCODING));
|
||||
}
|
||||
return rval.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public class JSONPointerException extends JSONException {
|
||||
private static final long serialVersionUID = 8872944667561856751L;
|
||||
|
||||
public JSONPointerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONPointerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/com/reandroid/lib/json/JSONPropertyIgnore.java
Normal file
14
src/main/java/com/reandroid/lib/json/JSONPropertyIgnore.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({METHOD})
|
||||
|
||||
public @interface JSONPropertyIgnore { }
|
17
src/main/java/com/reandroid/lib/json/JSONPropertyName.java
Normal file
17
src/main/java/com/reandroid/lib/json/JSONPropertyName.java
Normal file
@ -0,0 +1,17 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({METHOD})
|
||||
|
||||
public @interface JSONPropertyName {
|
||||
|
||||
String value();
|
||||
}
|
6
src/main/java/com/reandroid/lib/json/JSONString.java
Normal file
6
src/main/java/com/reandroid/lib/json/JSONString.java
Normal file
@ -0,0 +1,6 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public interface JSONString {
|
||||
|
||||
public String toJSONString();
|
||||
}
|
15
src/main/java/com/reandroid/lib/json/JSONStringer.java
Normal file
15
src/main/java/com/reandroid/lib/json/JSONStringer.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class JSONStringer extends JSONWriter {
|
||||
|
||||
public JSONStringer() {
|
||||
super(new StringWriter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
}
|
339
src/main/java/com/reandroid/lib/json/JSONTokener.java
Normal file
339
src/main/java/com/reandroid/lib/json/JSONTokener.java
Normal file
@ -0,0 +1,339 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
public class JSONTokener {
|
||||
/** current read character position on the current line. */
|
||||
private long character;
|
||||
/** flag to indicate if the end of the input has been found. */
|
||||
private boolean eof;
|
||||
/** current read index of the input. */
|
||||
private long index;
|
||||
/** current line of the input. */
|
||||
private long line;
|
||||
/** previous character read from the input. */
|
||||
private char previous;
|
||||
/** Reader for the input. */
|
||||
private final Reader reader;
|
||||
/** flag to indicate that a previous character was requested. */
|
||||
private boolean usePrevious;
|
||||
/** the number of characters read in the previous line. */
|
||||
private long characterPreviousLine;
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported()
|
||||
? reader
|
||||
: new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.characterPreviousLine = 0;
|
||||
this.line = 1;
|
||||
}
|
||||
public JSONTokener(InputStream inputStream) {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
public void back() throws JSONException {
|
||||
if (this.usePrevious || this.index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.decrementIndexes();
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
private void decrementIndexes() {
|
||||
this.index--;
|
||||
if(this.previous=='\r' || this.previous == '\n') {
|
||||
this.line--;
|
||||
this.character=this.characterPreviousLine ;
|
||||
} else if(this.character > 0){
|
||||
this.character--;
|
||||
}
|
||||
}
|
||||
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
public boolean more() throws JSONException {
|
||||
if(this.usePrevious) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
this.reader.mark(1);
|
||||
} catch (IOException e) {
|
||||
throw new JSONException("Unable to preserve stream position", e);
|
||||
}
|
||||
try {
|
||||
// -1 is EOF, but next() can not consume the null character '\0'
|
||||
if(this.reader.read() <= 0) {
|
||||
this.eof = true;
|
||||
return false;
|
||||
}
|
||||
this.reader.reset();
|
||||
} catch (IOException e) {
|
||||
throw new JSONException("Unable to read the next character from the stream", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
}
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
return 0;
|
||||
}
|
||||
this.incrementIndexes(c);
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
private void incrementIndexes(int c) {
|
||||
if(c > 0) {
|
||||
this.index++;
|
||||
if(c=='\r') {
|
||||
this.line++;
|
||||
this.characterPreviousLine = this.character;
|
||||
this.character=0;
|
||||
}else if (c=='\n') {
|
||||
if(this.previous != '\r') {
|
||||
this.line++;
|
||||
this.characterPreviousLine = this.character;
|
||||
}
|
||||
this.character=0;
|
||||
} else {
|
||||
this.character++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
if(n > 0) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" +
|
||||
n + "'");
|
||||
}
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw ''");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
public char nextClean() throws JSONException {
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
try {
|
||||
sb.append((char)Integer.parseInt(this.next(4), 16));
|
||||
} catch (NumberFormatException e) {
|
||||
throw this.syntaxError("Illegal escape.", e);
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
||||
c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = this.nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return this.nextString(c);
|
||||
case '{':
|
||||
this.back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
this.back();
|
||||
return new JSONArray(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or
|
||||
* null, or it can be a number. An implementation (such as this one)
|
||||
* is allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
if (!this.eof) {
|
||||
this.back();
|
||||
}
|
||||
|
||||
string = sb.toString().trim();
|
||||
if ("".equals(string)) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
long startIndex = this.index;
|
||||
long startCharacter = this.character;
|
||||
long startLine = this.line;
|
||||
this.reader.mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
// in some readers, reset() may throw an exception if
|
||||
// the remaining portion of the input is greater than
|
||||
// the mark size (1,000,000 above).
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return 0;
|
||||
}
|
||||
} while (c != to);
|
||||
this.reader.mark(1);
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this.toString());
|
||||
}
|
||||
|
||||
public JSONException syntaxError(String message, Throwable causedBy) {
|
||||
return new JSONException(message + this.toString(), causedBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " +
|
||||
this.line + "]";
|
||||
}
|
||||
}
|
219
src/main/java/com/reandroid/lib/json/JSONWriter.java
Normal file
219
src/main/java/com/reandroid/lib/json/JSONWriter.java
Normal file
@ -0,0 +1,219 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class JSONWriter {
|
||||
private static final int maxdepth = 200;
|
||||
|
||||
private boolean comma;
|
||||
|
||||
protected char mode;
|
||||
|
||||
private final JSONObject stack[];
|
||||
|
||||
private int top;
|
||||
|
||||
protected Appendable writer;
|
||||
|
||||
public JSONWriter(Appendable w) {
|
||||
this.comma = false;
|
||||
this.mode = 'i';
|
||||
this.stack = new JSONObject[maxdepth];
|
||||
this.top = 0;
|
||||
this.writer = w;
|
||||
}
|
||||
|
||||
private JSONWriter append(String string) throws JSONException {
|
||||
if (string == null) {
|
||||
throw new JSONException("Null pointer");
|
||||
}
|
||||
if (this.mode == 'o' || this.mode == 'a') {
|
||||
try {
|
||||
if (this.comma && this.mode == 'a') {
|
||||
this.writer.append(',');
|
||||
}
|
||||
this.writer.append(string);
|
||||
} catch (IOException e) {
|
||||
// Android as of API 25 does not support this exception constructor
|
||||
// however we won't worry about it. If an exception is happening here
|
||||
// it will just throw a "Method not found" exception instead.
|
||||
throw new JSONException(e);
|
||||
}
|
||||
if (this.mode == 'o') {
|
||||
this.mode = 'k';
|
||||
}
|
||||
this.comma = true;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Value out of sequence.");
|
||||
}
|
||||
|
||||
public JSONWriter array() throws JSONException {
|
||||
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
|
||||
this.push(null);
|
||||
this.append("[");
|
||||
this.comma = false;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Misplaced array.");
|
||||
}
|
||||
|
||||
private JSONWriter end(char m, char c) throws JSONException {
|
||||
if (this.mode != m) {
|
||||
throw new JSONException(m == 'a'
|
||||
? "Misplaced endArray."
|
||||
: "Misplaced endObject.");
|
||||
}
|
||||
this.pop(m);
|
||||
try {
|
||||
this.writer.append(c);
|
||||
} catch (IOException e) {
|
||||
// Android as of API 25 does not support this exception constructor
|
||||
// however we won't worry about it. If an exception is happening here
|
||||
// it will just throw a "Method not found" exception instead.
|
||||
throw new JSONException(e);
|
||||
}
|
||||
this.comma = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONWriter endArray() throws JSONException {
|
||||
return this.end('a', ']');
|
||||
}
|
||||
|
||||
public JSONWriter endObject() throws JSONException {
|
||||
return this.end('k', '}');
|
||||
}
|
||||
|
||||
public JSONWriter key(String string) throws JSONException {
|
||||
if (string == null) {
|
||||
throw new JSONException("Null key.");
|
||||
}
|
||||
if (this.mode == 'k') {
|
||||
try {
|
||||
JSONObject topObject = this.stack[this.top - 1];
|
||||
// don't use the built in putOnce method to maintain Android support
|
||||
if(topObject.has(string)) {
|
||||
throw new JSONException("Duplicate key \"" + string + "\"");
|
||||
}
|
||||
topObject.put(string, true);
|
||||
if (this.comma) {
|
||||
this.writer.append(',');
|
||||
}
|
||||
this.writer.append(JSONObject.quote(string));
|
||||
this.writer.append(':');
|
||||
this.comma = false;
|
||||
this.mode = 'o';
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
// Android as of API 25 does not support this exception constructor
|
||||
// however we won't worry about it. If an exception is happening here
|
||||
// it will just throw a "Method not found" exception instead.
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
throw new JSONException("Misplaced key.");
|
||||
}
|
||||
public JSONWriter object() throws JSONException {
|
||||
if (this.mode == 'i') {
|
||||
this.mode = 'o';
|
||||
}
|
||||
if (this.mode == 'o' || this.mode == 'a') {
|
||||
this.append("{");
|
||||
this.push(new JSONObject());
|
||||
this.comma = false;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Misplaced object.");
|
||||
|
||||
}
|
||||
private void pop(char c) throws JSONException {
|
||||
if (this.top <= 0) {
|
||||
throw new JSONException("Nesting error.");
|
||||
}
|
||||
char m = this.stack[this.top - 1] == null ? 'a' : 'k';
|
||||
if (m != c) {
|
||||
throw new JSONException("Nesting error.");
|
||||
}
|
||||
this.top -= 1;
|
||||
this.mode = this.top == 0
|
||||
? 'd'
|
||||
: this.stack[this.top - 1] == null
|
||||
? 'a'
|
||||
: 'k';
|
||||
}
|
||||
|
||||
private void push(JSONObject jo) throws JSONException {
|
||||
if (this.top >= maxdepth) {
|
||||
throw new JSONException("Nesting too deep.");
|
||||
}
|
||||
this.stack[this.top] = jo;
|
||||
this.mode = jo == null ? 'a' : 'k';
|
||||
this.top += 1;
|
||||
}
|
||||
|
||||
public static String valueToString(Object value) throws JSONException {
|
||||
if (value == null || value.equals(null)) {
|
||||
return "null";
|
||||
}
|
||||
if (value instanceof JSONString) {
|
||||
String object;
|
||||
try {
|
||||
object = ((JSONString) value).toJSONString();
|
||||
} catch (Exception e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
throw new JSONException("Bad value from toJSONString: " + object);
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
|
||||
final String numberAsString = JSONObject.numberToString((Number) value);
|
||||
if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) {
|
||||
// Close enough to a JSON number that we will return it unquoted
|
||||
return numberAsString;
|
||||
}
|
||||
// The Number value is not a valid JSON number.
|
||||
// Instead we will quote it as a string
|
||||
return JSONObject.quote(numberAsString);
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof JSONObject
|
||||
|| value instanceof JSONArray) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
Map<?, ?> map = (Map<?, ?>) value;
|
||||
return new JSONObject(map).toString();
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
Collection<?> coll = (Collection<?>) value;
|
||||
return new JSONArray(coll).toString();
|
||||
}
|
||||
if (value.getClass().isArray()) {
|
||||
return new JSONArray(value).toString();
|
||||
}
|
||||
if(value instanceof Enum<?>){
|
||||
return JSONObject.quote(((Enum<?>)value).name());
|
||||
}
|
||||
return JSONObject.quote(value.toString());
|
||||
}
|
||||
|
||||
public JSONWriter value(boolean b) throws JSONException {
|
||||
return this.append(b ? "true" : "false");
|
||||
}
|
||||
|
||||
public JSONWriter value(double d) throws JSONException {
|
||||
return this.value(Double.valueOf(d));
|
||||
}
|
||||
|
||||
public JSONWriter value(long l) throws JSONException {
|
||||
return this.append(Long.toString(l));
|
||||
}
|
||||
public JSONWriter value(Object object) throws JSONException {
|
||||
return this.append(valueToString(object));
|
||||
}
|
||||
}
|
@ -1,62 +1,36 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class JsonUtil {
|
||||
public static void writeJSONObject(JsonItem<JSONObject> jsonItem, File file) throws IOException{
|
||||
writeJSONObject(jsonItem, file, INDENT_FACTOR);
|
||||
|
||||
public static void readJSONObject(File file, JSONConvert<JSONObject> jsonConvert) throws IOException {
|
||||
FileInputStream inputStream=new FileInputStream(file);
|
||||
readJSONObject(inputStream, jsonConvert);
|
||||
inputStream.close();
|
||||
}
|
||||
public static void writeJSONObject(JsonItem<JSONObject> jsonItem, File file, int indentFactor) throws IOException{
|
||||
File dir=file.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream=new FileOutputStream(file);
|
||||
writeJSONObject(jsonItem, outputStream, indentFactor);
|
||||
public static void readJSONObject(InputStream inputStream, JSONConvert<JSONObject> jsonConvert){
|
||||
InputStreamReader reader=new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
readJSONObject(reader, jsonConvert);
|
||||
}
|
||||
public static void writeJSONObject(JsonItem<JSONObject> jsonItem, OutputStream outputStream) throws IOException{
|
||||
writeJSONObject(jsonItem, outputStream, INDENT_FACTOR);
|
||||
}
|
||||
public static void writeJSONObject(JsonItem<JSONObject> jsonItem, OutputStream outputStream, int indentFactor) throws IOException{
|
||||
Writer writer=new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
writer= writeJSONObject(jsonItem, writer, indentFactor);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
public static Writer writeJSONObject(JsonItem<JSONObject> jsonItem, Writer writer, int indentFactor){
|
||||
JSONObject jsonObject=jsonItem.toJson();
|
||||
return jsonObject.write(writer, indentFactor, 0);
|
||||
public static void readJSONObject(Reader reader, JSONConvert<JSONObject> jsonConvert){
|
||||
JSONObject jsonObject=new JSONObject(new JSONTokener(reader));
|
||||
jsonConvert.fromJson(jsonObject);
|
||||
}
|
||||
|
||||
|
||||
public static void writeJSONArray(JsonItem<JSONArray> jsonItem, File file) throws IOException{
|
||||
writeJSONArray(jsonItem, file, INDENT_FACTOR);
|
||||
public static void readJSONArray(File file, JSONConvert<JSONArray> jsonConvert) throws IOException {
|
||||
FileInputStream inputStream=new FileInputStream(file);
|
||||
readJSONArray(inputStream, jsonConvert);
|
||||
inputStream.close();
|
||||
}
|
||||
public static void writeJSONArray(JsonItem<JSONArray> jsonItem, File file, int indentFactor) throws IOException{
|
||||
File dir=file.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream=new FileOutputStream(file);
|
||||
writeJSONArray(jsonItem, outputStream, indentFactor);
|
||||
public static void readJSONArray(InputStream inputStream, JSONConvert<JSONArray> jsonConvert){
|
||||
InputStreamReader reader=new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
readJSONArray(reader, jsonConvert);
|
||||
}
|
||||
public static void writeJSONArray(JsonItem<JSONArray> jsonItem, OutputStream outputStream) throws IOException{
|
||||
writeJSONArray(jsonItem, outputStream, INDENT_FACTOR);
|
||||
}
|
||||
public static void writeJSONArray(JsonItem<JSONArray> jsonItem, OutputStream outputStream, int indentFactor) throws IOException{
|
||||
Writer writer=new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
|
||||
writer= writeJSONArray(jsonItem, writer, indentFactor);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
public static Writer writeJSONArray(JsonItem<JSONArray> jsonItem, Writer writer, int indentFactor){
|
||||
JSONArray jsonArray=jsonItem.toJson();
|
||||
return jsonArray.write(writer, indentFactor, 0);
|
||||
public static void readJSONArray(Reader reader, JSONConvert<JSONArray> jsonConvert){
|
||||
JSONArray jsonObject=new JSONArray(new JSONTokener(reader));
|
||||
jsonConvert.fromJson(jsonObject);
|
||||
}
|
||||
|
||||
private static final int INDENT_FACTOR=4;
|
||||
}
|
||||
|
35
src/main/java/com/reandroid/lib/json/Property.java
Normal file
35
src/main/java/com/reandroid/lib/json/Property.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
|
||||
public class Property {
|
||||
|
||||
public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException {
|
||||
// can't use the new constructor for Android support
|
||||
// JSONObject jo = new JSONObject(properties == null ? 0 : properties.size());
|
||||
JSONObject jo = new JSONObject();
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
Enumeration<?> enumProperties = properties.propertyNames();
|
||||
while(enumProperties.hasMoreElements()) {
|
||||
String name = (String)enumProperties.nextElement();
|
||||
jo.put(name, properties.getProperty(name));
|
||||
}
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
public static Properties toProperties(JSONObject jo) throws JSONException {
|
||||
Properties properties = new Properties();
|
||||
if (jo != null) {
|
||||
// Don't use the new entrySet API to maintain Android support
|
||||
for (final String key : jo.keySet()) {
|
||||
Object value = jo.opt(key);
|
||||
if (!JSONObject.NULL.equals(value)) {
|
||||
properties.put(key, value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
}
|
600
src/main/java/com/reandroid/lib/json/XML.java
Normal file
600
src/main/java/com/reandroid/lib/json/XML.java
Normal file
@ -0,0 +1,600 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Iterator;
|
||||
@SuppressWarnings("boxing")
|
||||
public class XML {
|
||||
|
||||
/** The Character '&'. */
|
||||
public static final Character AMP = '&';
|
||||
|
||||
/** The Character '''. */
|
||||
public static final Character APOS = '\'';
|
||||
|
||||
/** The Character '!'. */
|
||||
public static final Character BANG = '!';
|
||||
|
||||
/** The Character '='. */
|
||||
public static final Character EQ = '=';
|
||||
|
||||
/** The Character <pre>{@code '>'. }</pre>*/
|
||||
public static final Character GT = '>';
|
||||
|
||||
/** The Character '<'. */
|
||||
public static final Character LT = '<';
|
||||
|
||||
/** The Character '?'. */
|
||||
public static final Character QUEST = '?';
|
||||
|
||||
/** The Character '"'. */
|
||||
public static final Character QUOT = '"';
|
||||
|
||||
/** The Character '/'. */
|
||||
public static final Character SLASH = '/';
|
||||
|
||||
public static final String NULL_ATTR = "xsi:nil";
|
||||
|
||||
public static final String TYPE_ATTR = "xsi:type";
|
||||
|
||||
private static Iterable<Integer> codePointIterator(final String string) {
|
||||
return new Iterable<Integer>() {
|
||||
@Override
|
||||
public Iterator<Integer> iterator() {
|
||||
return new Iterator<Integer>() {
|
||||
private int nextIndex = 0;
|
||||
private int length = string.length();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.nextIndex < this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
int result = string.codePointAt(this.nextIndex);
|
||||
this.nextIndex += Character.charCount(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static String escape(String string) {
|
||||
StringBuilder sb = new StringBuilder(string.length());
|
||||
for (final int cp : codePointIterator(string)) {
|
||||
switch (cp) {
|
||||
case '&':
|
||||
sb.append("&");
|
||||
break;
|
||||
case '<':
|
||||
sb.append("<");
|
||||
break;
|
||||
case '>':
|
||||
sb.append(">");
|
||||
break;
|
||||
case '"':
|
||||
sb.append(""");
|
||||
break;
|
||||
case '\'':
|
||||
sb.append("'");
|
||||
break;
|
||||
default:
|
||||
if (mustEscape(cp)) {
|
||||
sb.append("&#x");
|
||||
sb.append(Integer.toHexString(cp));
|
||||
sb.append(';');
|
||||
} else {
|
||||
sb.appendCodePoint(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean mustEscape(int cp) {
|
||||
/* Valid range from https://www.w3.org/TR/REC-xml/#charsets
|
||||
*
|
||||
* #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
|
||||
*
|
||||
* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
|
||||
*/
|
||||
// isISOControl is true when (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F)
|
||||
// all ISO control characters are out of range except tabs and new lines
|
||||
return (Character.isISOControl(cp)
|
||||
&& cp != 0x9
|
||||
&& cp != 0xA
|
||||
&& cp != 0xD
|
||||
) || !(
|
||||
// valid the range of acceptable characters that aren't control
|
||||
(cp >= 0x20 && cp <= 0xD7FF)
|
||||
|| (cp >= 0xE000 && cp <= 0xFFFD)
|
||||
|| (cp >= 0x10000 && cp <= 0x10FFFF)
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
public static String unescape(String string) {
|
||||
StringBuilder sb = new StringBuilder(string.length());
|
||||
for (int i = 0, length = string.length(); i < length; i++) {
|
||||
char c = string.charAt(i);
|
||||
if (c == '&') {
|
||||
final int semic = string.indexOf(';', i);
|
||||
if (semic > i) {
|
||||
final String entity = string.substring(i + 1, semic);
|
||||
sb.append(XMLTokener.unescapeEntity(entity));
|
||||
// skip past the entity we just parsed.
|
||||
i += entity.length() + 1;
|
||||
} else {
|
||||
// this shouldn't happen in most cases since the parser
|
||||
// errors on unclosed entries.
|
||||
sb.append(c);
|
||||
}
|
||||
} else {
|
||||
// not part of an entity
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void noSpace(String string) throws JSONException {
|
||||
int i, length = string.length();
|
||||
if (length == 0) {
|
||||
throw new JSONException("Empty string.");
|
||||
}
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (Character.isWhitespace(string.charAt(i))) {
|
||||
throw new JSONException("'" + string
|
||||
+ "' contains a space character.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config)
|
||||
throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
JSONObject jsonObject = null;
|
||||
String string;
|
||||
String tagName;
|
||||
Object token;
|
||||
XMLXsiTypeConverter<?> xmlXsiTypeConverter;
|
||||
|
||||
// Test for and skip past these forms:
|
||||
// <!-- ... -->
|
||||
// <! ... >
|
||||
// <![ ... ]]>
|
||||
// <? ... ?>
|
||||
// Report errors for these forms:
|
||||
// <>
|
||||
// <=
|
||||
// <<
|
||||
|
||||
token = x.nextToken();
|
||||
|
||||
// <!
|
||||
|
||||
if (token == BANG) {
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
return false;
|
||||
}
|
||||
x.back();
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if ("CDATA".equals(token)) {
|
||||
if (x.next() == '[') {
|
||||
string = x.nextCDATA();
|
||||
if (string.length() > 0) {
|
||||
context.accumulate(config.getcDataTagName(), string);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == LT) {
|
||||
i += 1;
|
||||
} else if (token == GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
return false;
|
||||
} else if (token == QUEST) {
|
||||
|
||||
// <?
|
||||
x.skipPast("?>");
|
||||
return false;
|
||||
} else if (token == SLASH) {
|
||||
|
||||
// Close tag </
|
||||
|
||||
token = x.nextToken();
|
||||
if (name == null) {
|
||||
throw x.syntaxError("Mismatched close tag " + token);
|
||||
}
|
||||
if (!token.equals(name)) {
|
||||
throw x.syntaxError("Mismatched " + name + " and " + token);
|
||||
}
|
||||
if (x.nextToken() != GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (token instanceof Character) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
|
||||
// Open tag <
|
||||
|
||||
} else {
|
||||
tagName = (String) token;
|
||||
token = null;
|
||||
jsonObject = new JSONObject();
|
||||
boolean nilAttributeFound = false;
|
||||
xmlXsiTypeConverter = null;
|
||||
for (;;) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
// attribute = value
|
||||
if (token instanceof String) {
|
||||
string = (String) token;
|
||||
token = x.nextToken();
|
||||
if (token == EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
|
||||
if (config.isConvertNilAttributeToNull()
|
||||
&& NULL_ATTR.equals(string)
|
||||
&& Boolean.parseBoolean((String) token)) {
|
||||
nilAttributeFound = true;
|
||||
} else if(config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty()
|
||||
&& TYPE_ATTR.equals(string)) {
|
||||
xmlXsiTypeConverter = config.getXsiTypeMap().get(token);
|
||||
} else if (!nilAttributeFound) {
|
||||
jsonObject.accumulate(string,
|
||||
config.isKeepStrings()
|
||||
? ((String) token)
|
||||
: stringToValue((String) token));
|
||||
}
|
||||
token = null;
|
||||
} else {
|
||||
jsonObject.accumulate(string, "");
|
||||
}
|
||||
|
||||
} else if (token == SLASH) {
|
||||
// Empty tag <.../>
|
||||
if (x.nextToken() != GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (nilAttributeFound) {
|
||||
context.accumulate(tagName, JSONObject.NULL);
|
||||
} else if (jsonObject.length() > 0) {
|
||||
context.accumulate(tagName, jsonObject);
|
||||
} else {
|
||||
context.accumulate(tagName, "");
|
||||
}
|
||||
return false;
|
||||
|
||||
} else if (token == GT) {
|
||||
// Content, between <...> and </...>
|
||||
for (;;) {
|
||||
token = x.nextContent();
|
||||
if (token == null) {
|
||||
if (tagName != null) {
|
||||
throw x.syntaxError("Unclosed tag " + tagName);
|
||||
}
|
||||
return false;
|
||||
} else if (token instanceof String) {
|
||||
string = (String) token;
|
||||
if (string.length() > 0) {
|
||||
if(xmlXsiTypeConverter != null) {
|
||||
jsonObject.accumulate(config.getcDataTagName(),
|
||||
stringToValue(string, xmlXsiTypeConverter));
|
||||
} else {
|
||||
jsonObject.accumulate(config.getcDataTagName(),
|
||||
config.isKeepStrings() ? string : stringToValue(string));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (token == LT) {
|
||||
// Nested element
|
||||
if (parse(x, jsonObject, tagName, config)) {
|
||||
if (jsonObject.length() == 0) {
|
||||
context.accumulate(tagName, "");
|
||||
} else if (jsonObject.length() == 1
|
||||
&& jsonObject.opt(config.getcDataTagName()) != null) {
|
||||
context.accumulate(tagName, jsonObject.opt(config.getcDataTagName()));
|
||||
} else {
|
||||
context.accumulate(tagName, jsonObject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Object stringToValue(String string, XMLXsiTypeConverter<?> typeConverter) {
|
||||
if(typeConverter != null) {
|
||||
return typeConverter.convert(string);
|
||||
}
|
||||
return stringToValue(string);
|
||||
}
|
||||
|
||||
// To maintain compatibility with the Android API, this method is a direct copy of
|
||||
// the one in JSONObject. Changes made here should be reflected there.
|
||||
// This method should not make calls out of the XML object.
|
||||
public static Object stringToValue(String string) {
|
||||
if ("".equals(string)) {
|
||||
return string;
|
||||
}
|
||||
|
||||
// check JSON key words true/false/null
|
||||
if ("true".equalsIgnoreCase(string)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if ("false".equalsIgnoreCase(string)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
if ("null".equalsIgnoreCase(string)) {
|
||||
return JSONObject.NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it might be a number, try converting it. If a number cannot be
|
||||
* produced, then the value will just be a string.
|
||||
*/
|
||||
|
||||
char initial = string.charAt(0);
|
||||
if ((initial >= '0' && initial <= '9') || initial == '-') {
|
||||
try {
|
||||
return stringToNumber(string);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
private static Number stringToNumber(final String val) throws NumberFormatException {
|
||||
char initial = val.charAt(0);
|
||||
if ((initial >= '0' && initial <= '9') || initial == '-') {
|
||||
// decimal representation
|
||||
if (isDecimalNotation(val)) {
|
||||
// Use a BigDecimal all the time so we keep the original
|
||||
// representation. BigDecimal doesn't support -0.0, ensure we
|
||||
// keep that by forcing a decimal.
|
||||
try {
|
||||
BigDecimal bd = new BigDecimal(val);
|
||||
if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
|
||||
return Double.valueOf(-0.0);
|
||||
}
|
||||
return bd;
|
||||
} catch (NumberFormatException retryAsDouble) {
|
||||
// this is to support "Hex Floats" like this: 0x1.0P-1074
|
||||
try {
|
||||
Double d = Double.valueOf(val);
|
||||
if(d.isNaN() || d.isInfinite()) {
|
||||
throw new NumberFormatException("val ["+val+"] is not a valid number.");
|
||||
}
|
||||
return d;
|
||||
} catch (NumberFormatException ignore) {
|
||||
throw new NumberFormatException("val ["+val+"] is not a valid number.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// block items like 00 01 etc. Java number parsers treat these as Octal.
|
||||
if(initial == '0' && val.length() > 1) {
|
||||
char at1 = val.charAt(1);
|
||||
if(at1 >= '0' && at1 <= '9') {
|
||||
throw new NumberFormatException("val ["+val+"] is not a valid number.");
|
||||
}
|
||||
} else if (initial == '-' && val.length() > 2) {
|
||||
char at1 = val.charAt(1);
|
||||
char at2 = val.charAt(2);
|
||||
if(at1 == '0' && at2 >= '0' && at2 <= '9') {
|
||||
throw new NumberFormatException("val ["+val+"] is not a valid number.");
|
||||
}
|
||||
}
|
||||
// integer representation.
|
||||
// This will narrow any values to the smallest reasonable Object representation
|
||||
// (Integer, Long, or BigInteger)
|
||||
|
||||
// BigInteger down conversion: We use a similar bitLenth compare as
|
||||
// BigInteger#intValueExact uses. Increases GC, but objects hold
|
||||
// only what they need. i.e. Less runtime overhead if the value is
|
||||
// long lived.
|
||||
BigInteger bi = new BigInteger(val);
|
||||
if(bi.bitLength() <= 31){
|
||||
return Integer.valueOf(bi.intValue());
|
||||
}
|
||||
if(bi.bitLength() <= 63){
|
||||
return Long.valueOf(bi.longValue());
|
||||
}
|
||||
return bi;
|
||||
}
|
||||
throw new NumberFormatException("val ["+val+"] is not a valid number.");
|
||||
}
|
||||
|
||||
|
||||
private static boolean isDecimalNotation(final String val) {
|
||||
return val.indexOf('.') > -1 || val.indexOf('e') > -1
|
||||
|| val.indexOf('E') > -1 || "-0".equals(val);
|
||||
}
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return toJSONObject(string, XMLParserConfiguration.ORIGINAL);
|
||||
}
|
||||
|
||||
public static JSONObject toJSONObject(Reader reader) throws JSONException {
|
||||
return toJSONObject(reader, XMLParserConfiguration.ORIGINAL);
|
||||
}
|
||||
|
||||
public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws JSONException {
|
||||
if(keepStrings) {
|
||||
return toJSONObject(reader, XMLParserConfiguration.KEEP_STRINGS);
|
||||
}
|
||||
return toJSONObject(reader, XMLParserConfiguration.ORIGINAL);
|
||||
}
|
||||
|
||||
public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration config) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
XMLTokener x = new XMLTokener(reader);
|
||||
while (x.more()) {
|
||||
x.skipPast("<");
|
||||
if(x.more()) {
|
||||
parse(x, jo, null, config);
|
||||
}
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
|
||||
return toJSONObject(new StringReader(string), keepStrings);
|
||||
}
|
||||
|
||||
public static JSONObject toJSONObject(String string, XMLParserConfiguration config) throws JSONException {
|
||||
return toJSONObject(new StringReader(string), config);
|
||||
}
|
||||
|
||||
public static String toString(Object object) throws JSONException {
|
||||
return toString(object, null, XMLParserConfiguration.ORIGINAL);
|
||||
}
|
||||
|
||||
public static String toString(final Object object, final String tagName) {
|
||||
return toString(object, tagName, XMLParserConfiguration.ORIGINAL);
|
||||
}
|
||||
|
||||
public static String toString(final Object object, final String tagName, final XMLParserConfiguration config)
|
||||
throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
JSONArray ja;
|
||||
JSONObject jo;
|
||||
String string;
|
||||
|
||||
if (object instanceof JSONObject) {
|
||||
|
||||
// Emit <tagName>
|
||||
if (tagName != null) {
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
|
||||
// Loop thru the keys.
|
||||
// don't use the new entrySet accessor to maintain Android Support
|
||||
jo = (JSONObject) object;
|
||||
for (final String key : jo.keySet()) {
|
||||
Object value = jo.opt(key);
|
||||
if (value == null) {
|
||||
value = "";
|
||||
} else if (value.getClass().isArray()) {
|
||||
value = new JSONArray(value);
|
||||
}
|
||||
|
||||
// Emit content in body
|
||||
if (key.equals(config.getcDataTagName())) {
|
||||
if (value instanceof JSONArray) {
|
||||
ja = (JSONArray) value;
|
||||
int jaLength = ja.length();
|
||||
// don't use the new iterator API to maintain support for Android
|
||||
for (int i = 0; i < jaLength; i++) {
|
||||
if (i > 0) {
|
||||
sb.append('\n');
|
||||
}
|
||||
Object val = ja.opt(i);
|
||||
sb.append(escape(val.toString()));
|
||||
}
|
||||
} else {
|
||||
sb.append(escape(value.toString()));
|
||||
}
|
||||
|
||||
// Emit an array of similar keys
|
||||
|
||||
} else if (value instanceof JSONArray) {
|
||||
ja = (JSONArray) value;
|
||||
int jaLength = ja.length();
|
||||
// don't use the new iterator API to maintain support for Android
|
||||
for (int i = 0; i < jaLength; i++) {
|
||||
Object val = ja.opt(i);
|
||||
if (val instanceof JSONArray) {
|
||||
sb.append('<');
|
||||
sb.append(key);
|
||||
sb.append('>');
|
||||
sb.append(toString(val, null, config));
|
||||
sb.append("</");
|
||||
sb.append(key);
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append(toString(val, key, config));
|
||||
}
|
||||
}
|
||||
} else if ("".equals(value)) {
|
||||
sb.append('<');
|
||||
sb.append(key);
|
||||
sb.append("/>");
|
||||
|
||||
// Emit a new tag <k>
|
||||
|
||||
} else {
|
||||
sb.append(toString(value, key, config));
|
||||
}
|
||||
}
|
||||
if (tagName != null) {
|
||||
|
||||
// Emit the </tagName> close tag
|
||||
sb.append("</");
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
if (object != null && (object instanceof JSONArray || object.getClass().isArray())) {
|
||||
if(object.getClass().isArray()) {
|
||||
ja = new JSONArray(object);
|
||||
} else {
|
||||
ja = (JSONArray) object;
|
||||
}
|
||||
int jaLength = ja.length();
|
||||
// don't use the new iterator API to maintain support for Android
|
||||
for (int i = 0; i < jaLength; i++) {
|
||||
Object val = ja.opt(i);
|
||||
// XML does not have good support for arrays. If an array
|
||||
// appears in a place where XML is lacking, synthesize an
|
||||
// <array> element.
|
||||
sb.append(toString(val, tagName == null ? "array" : tagName, config));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
string = (object == null) ? "null" : escape(object.toString());
|
||||
return (tagName == null) ? "\"" + string + "\""
|
||||
: (string.length() == 0) ? "<" + tagName + "/>" : "<" + tagName
|
||||
+ ">" + string + "</" + tagName + ">";
|
||||
|
||||
}
|
||||
}
|
120
src/main/java/com/reandroid/lib/json/XMLParserConfiguration.java
Normal file
120
src/main/java/com/reandroid/lib/json/XMLParserConfiguration.java
Normal file
@ -0,0 +1,120 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@SuppressWarnings({""})
|
||||
public class XMLParserConfiguration {
|
||||
/** Original Configuration of the XML Parser. */
|
||||
public static final XMLParserConfiguration ORIGINAL
|
||||
= new XMLParserConfiguration();
|
||||
/** Original configuration of the XML Parser except that values are kept as strings. */
|
||||
public static final XMLParserConfiguration KEEP_STRINGS
|
||||
= new XMLParserConfiguration().withKeepStrings(true);
|
||||
|
||||
private boolean keepStrings;
|
||||
|
||||
|
||||
private String cDataTagName;
|
||||
|
||||
|
||||
private boolean convertNilAttributeToNull;
|
||||
|
||||
private Map<String, XMLXsiTypeConverter<?>> xsiTypeMap;
|
||||
|
||||
public XMLParserConfiguration () {
|
||||
this.keepStrings = false;
|
||||
this.cDataTagName = "content";
|
||||
this.convertNilAttributeToNull = false;
|
||||
this.xsiTypeMap = Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public XMLParserConfiguration (final boolean keepStrings) {
|
||||
this(keepStrings, "content", false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public XMLParserConfiguration (final String cDataTagName) {
|
||||
this(false, cDataTagName, false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) {
|
||||
this.keepStrings = keepStrings;
|
||||
this.cDataTagName = cDataTagName;
|
||||
this.convertNilAttributeToNull = false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
|
||||
this.keepStrings = keepStrings;
|
||||
this.cDataTagName = cDataTagName;
|
||||
this.convertNilAttributeToNull = convertNilAttributeToNull;
|
||||
}
|
||||
|
||||
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
|
||||
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap ) {
|
||||
this.keepStrings = keepStrings;
|
||||
this.cDataTagName = cDataTagName;
|
||||
this.convertNilAttributeToNull = convertNilAttributeToNull;
|
||||
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XMLParserConfiguration clone() {
|
||||
// future modifications to this method should always ensure a "deep"
|
||||
// clone in the case of collections. i.e. if a Map is added as a configuration
|
||||
// item, a new map instance should be created and if possible each value in the
|
||||
// map should be cloned as well. If the values of the map are known to also
|
||||
// be immutable, then a shallow clone of the map is acceptable.
|
||||
return new XMLParserConfiguration(
|
||||
this.keepStrings,
|
||||
this.cDataTagName,
|
||||
this.convertNilAttributeToNull,
|
||||
this.xsiTypeMap
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public boolean isKeepStrings() {
|
||||
return this.keepStrings;
|
||||
}
|
||||
|
||||
public XMLParserConfiguration withKeepStrings(final boolean newVal) {
|
||||
XMLParserConfiguration newConfig = this.clone();
|
||||
newConfig.keepStrings = newVal;
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
public String getcDataTagName() {
|
||||
return this.cDataTagName;
|
||||
}
|
||||
|
||||
public XMLParserConfiguration withcDataTagName(final String newVal) {
|
||||
XMLParserConfiguration newConfig = this.clone();
|
||||
newConfig.cDataTagName = newVal;
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
public boolean isConvertNilAttributeToNull() {
|
||||
return this.convertNilAttributeToNull;
|
||||
}
|
||||
|
||||
public XMLParserConfiguration withConvertNilAttributeToNull(final boolean newVal) {
|
||||
XMLParserConfiguration newConfig = this.clone();
|
||||
newConfig.convertNilAttributeToNull = newVal;
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
public Map<String, XMLXsiTypeConverter<?>> getXsiTypeMap() {
|
||||
return this.xsiTypeMap;
|
||||
}
|
||||
|
||||
public XMLParserConfiguration withXsiTypeMap(final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap) {
|
||||
XMLParserConfiguration newConfig = this.clone();
|
||||
Map<String, XMLXsiTypeConverter<?>> cloneXsiTypeMap = new HashMap<String, XMLXsiTypeConverter<?>>(xsiTypeMap);
|
||||
newConfig.xsiTypeMap = Collections.unmodifiableMap(cloneXsiTypeMap);
|
||||
return newConfig;
|
||||
}
|
||||
}
|
312
src/main/java/com/reandroid/lib/json/XMLTokener.java
Normal file
312
src/main/java/com/reandroid/lib/json/XMLTokener.java
Normal file
@ -0,0 +1,312 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
public class XMLTokener extends JSONTokener {
|
||||
|
||||
/** The table of entity values. It initially contains Character values for
|
||||
* amp, apos, gt, lt, quot.
|
||||
*/
|
||||
public static final java.util.HashMap<String, Character> entity;
|
||||
|
||||
static {
|
||||
entity = new java.util.HashMap<String, Character>(8);
|
||||
entity.put("amp", XML.AMP);
|
||||
entity.put("apos", XML.APOS);
|
||||
entity.put("gt", XML.GT);
|
||||
entity.put("lt", XML.LT);
|
||||
entity.put("quot", XML.QUOT);
|
||||
}
|
||||
|
||||
public XMLTokener(Reader r) {
|
||||
super(r);
|
||||
}
|
||||
|
||||
public XMLTokener(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public String nextCDATA() throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (more()) {
|
||||
c = next();
|
||||
sb.append(c);
|
||||
i = sb.length() - 3;
|
||||
if (i >= 0 && sb.charAt(i) == ']' &&
|
||||
sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
|
||||
sb.setLength(i);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
throw syntaxError("Unclosed CDATA");
|
||||
}
|
||||
public Object nextContent() throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == 0) {
|
||||
return null;
|
||||
}
|
||||
if (c == '<') {
|
||||
return XML.LT;
|
||||
}
|
||||
sb = new StringBuilder();
|
||||
for (;;) {
|
||||
if (c == 0) {
|
||||
return sb.toString().trim();
|
||||
}
|
||||
if (c == '<') {
|
||||
back();
|
||||
return sb.toString().trim();
|
||||
}
|
||||
if (c == '&') {
|
||||
sb.append(nextEntity(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
c = next();
|
||||
}
|
||||
}
|
||||
public Object nextEntity(@SuppressWarnings("unused") char ampersand) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
char c = next();
|
||||
if (Character.isLetterOrDigit(c) || c == '#') {
|
||||
sb.append(Character.toLowerCase(c));
|
||||
} else if (c == ';') {
|
||||
break;
|
||||
} else {
|
||||
throw syntaxError("Missing ';' in XML entity: &" + sb);
|
||||
}
|
||||
}
|
||||
String string = sb.toString();
|
||||
return unescapeEntity(string);
|
||||
}
|
||||
|
||||
|
||||
static String unescapeEntity(String e) {
|
||||
// validate
|
||||
if (e == null || e.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
// if our entity is an encoded unicode point, parse it.
|
||||
if (e.charAt(0) == '#') {
|
||||
int cp;
|
||||
if (e.charAt(1) == 'x' || e.charAt(1) == 'X') {
|
||||
// hex encoded unicode
|
||||
cp = Integer.parseInt(e.substring(2), 16);
|
||||
} else {
|
||||
// decimal encoded unicode
|
||||
cp = Integer.parseInt(e.substring(1));
|
||||
}
|
||||
return new String(new int[] {cp},0,1);
|
||||
}
|
||||
Character knownEntity = entity.get(e);
|
||||
if(knownEntity==null) {
|
||||
// we don't know the entity so keep it encoded
|
||||
return '&' + e + ';';
|
||||
}
|
||||
return knownEntity.toString();
|
||||
}
|
||||
public Object nextMeta() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Misshaped meta tag");
|
||||
case '<':
|
||||
return XML.LT;
|
||||
case '>':
|
||||
return XML.GT;
|
||||
case '/':
|
||||
return XML.SLASH;
|
||||
case '=':
|
||||
return XML.EQ;
|
||||
case '!':
|
||||
return XML.BANG;
|
||||
case '?':
|
||||
return XML.QUEST;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
throw syntaxError("Unterminated string");
|
||||
}
|
||||
if (c == q) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
default:
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (Character.isWhitespace(c)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Unterminated string");
|
||||
case '<':
|
||||
case '>':
|
||||
case '/':
|
||||
case '=':
|
||||
case '!':
|
||||
case '?':
|
||||
case '"':
|
||||
case '\'':
|
||||
back();
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public Object nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuilder sb;
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
switch (c) {
|
||||
case 0:
|
||||
throw syntaxError("Misshaped element");
|
||||
case '<':
|
||||
throw syntaxError("Misplaced '<'");
|
||||
case '>':
|
||||
return XML.GT;
|
||||
case '/':
|
||||
return XML.SLASH;
|
||||
case '=':
|
||||
return XML.EQ;
|
||||
case '!':
|
||||
return XML.BANG;
|
||||
case '?':
|
||||
return XML.QUEST;
|
||||
|
||||
// Quoted string
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuilder();
|
||||
for (;;) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
throw syntaxError("Unterminated string");
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
}
|
||||
if (c == '&') {
|
||||
sb.append(nextEntity(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
// Name
|
||||
|
||||
sb = new StringBuilder();
|
||||
for (;;) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
if (Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
return sb.toString();
|
||||
case '>':
|
||||
case '/':
|
||||
case '=':
|
||||
case '!':
|
||||
case '?':
|
||||
case '[':
|
||||
case ']':
|
||||
back();
|
||||
return sb.toString();
|
||||
case '<':
|
||||
case '"':
|
||||
case '\'':
|
||||
throw syntaxError("Bad character in a name");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The Android implementation of JSONTokener has a public method of public void skipPast(String to)
|
||||
// even though ours does not have that method, to have API compatibility, our method in the subclass
|
||||
// should match.
|
||||
public void skipPast(String to) {
|
||||
boolean b;
|
||||
char c;
|
||||
int i;
|
||||
int j;
|
||||
int offset = 0;
|
||||
int length = to.length();
|
||||
char[] circle = new char[length];
|
||||
|
||||
/*
|
||||
* First fill the circle buffer with as many characters as are in the
|
||||
* to string. If we reach an early end, bail.
|
||||
*/
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
return;
|
||||
}
|
||||
circle[i] = c;
|
||||
}
|
||||
|
||||
/* We will loop, possibly for all of the remaining characters. */
|
||||
|
||||
for (;;) {
|
||||
j = offset;
|
||||
b = true;
|
||||
|
||||
/* Compare the circle buffer with the to string. */
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (circle[j] != to.charAt(i)) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
j += 1;
|
||||
if (j >= length) {
|
||||
j -= length;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we exit the loop with b intact, then victory is ours. */
|
||||
|
||||
if (b) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the next character. If there isn't one, then defeat is ours. */
|
||||
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Shove the character in the circle buffer and advance the
|
||||
* circle offset. The offset is mod n.
|
||||
*/
|
||||
circle[offset] = c;
|
||||
offset += 1;
|
||||
if (offset >= length) {
|
||||
offset -= length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.reandroid.lib.json;
|
||||
|
||||
public interface XMLXsiTypeConverter<T> {
|
||||
T convert(String value);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user