V1.0.3 (to/from json convert)

This commit is contained in:
REAndroid 2022-12-01 14:37:12 -05:00
parent 1523b6341d
commit 3626e24c90
65 changed files with 5446 additions and 240 deletions

View File

@ -23,7 +23,6 @@ repositories {
dependencies {
compile(files("$rootProject.projectDir/libs/ArchiveUtil.jar"))
compile(files("$rootProject.projectDir/libs/json.jar"))
}
processResources {

Binary file not shown.

View File

@ -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);

View File

@ -12,4 +12,5 @@ public class ApkUtil {
}
return path;
}
public static final String JSON_FILE_EXTENSION=".a.json";
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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();
}

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
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";
}

View File

@ -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());
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){

View 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);
if(tag!=null){
resXmlElement.setTag(tag);
int lineNo=getStartElement().getLineNumber()+1;
startElement.setLineNumber(lineNo);
endElement.setLineNumber(lineNo);
}
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,16 +543,46 @@ 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);
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(){
ResXmlStartElement start = getStartElement();
@ -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";
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -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){

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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{

View File

@ -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() {

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}
}

View 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);
}
}

View File

@ -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);
}

View 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);
}
}

View 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;
}

View 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 &lt;[ [ ]]>}</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 &lt;[ [ ]]>}</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 &lt;[ [ ]]>}</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 &lt;[ [ ]]>}</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();
}
}

File diff suppressed because it is too large Load Diff

View 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);
}
}
}

View File

@ -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);
}
}

View 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 { }

View 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();
}

View File

@ -0,0 +1,6 @@
package com.reandroid.lib.json;
public interface JSONString {
public String toJSONString();
}

View 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;
}
}

View 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 + "]";
}
}

View 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));
}
}

View File

@ -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();
public static void readJSONObject(InputStream inputStream, JSONConvert<JSONObject> jsonConvert){
InputStreamReader reader=new InputStreamReader(inputStream, StandardCharsets.UTF_8);
readJSONObject(reader, jsonConvert);
}
FileOutputStream outputStream=new FileOutputStream(file);
writeJSONObject(jsonItem, outputStream, indentFactor);
}
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();
public static void readJSONArray(InputStream inputStream, JSONConvert<JSONArray> jsonConvert){
InputStreamReader reader=new InputStreamReader(inputStream, StandardCharsets.UTF_8);
readJSONArray(reader, jsonConvert);
}
FileOutputStream outputStream=new FileOutputStream(file);
writeJSONArray(jsonItem, outputStream, indentFactor);
}
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;
}

View 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;
}
}

View 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 '&amp;'. */
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 '&lt;'. */
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("&amp;");
break;
case '<':
sb.append("&lt;");
break;
case '>':
sb.append("&gt;");
break;
case '"':
sb.append("&quot;");
break;
case '\'':
sb.append("&apos;");
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 + ">";
}
}

View 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;
}
}

View 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;
}
}
}
}

View File

@ -0,0 +1,5 @@
package com.reandroid.lib.json;
public interface XMLXsiTypeConverter<T> {
T convert(String value);
}