Last active
August 29, 2015 14:10
-
-
Save cab404/8c9f3639e4046c9b66f4 to your computer and use it in GitHub Desktop.
JSONTemplate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cab404.jsontemplate; | |
import org.json.JSONArray; | |
import org.json.JSONException; | |
import org.json.JSONObject; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.List; | |
/** | |
* Pretty simple but powerful and useful JSON template thingy for org.json library. | |
* Usage is simple: | |
* <p/> | |
* <pre> | |
* | |
* JSONTemplate template = new JSONTemplate( | |
* "{ | |
* 'auth': '%%%', | |
* 'data': [ | |
* {'section1': '%%%'}, | |
* {'section2': '%%%'} | |
* ] | |
* }"); | |
* System.out.println(template.build("test", 12, 23)); | |
* | |
* > {"data":[{"section1":23},{"section2":12}],"auth":"test"} | |
* </pre> | |
* <p/> | |
* Yeah, just replace changeable parts with %%%, or whatever you want (just change {@link JSONTemplate#REPLACE} in your copy). | |
* So, that's it. | |
* <p/> | |
* The MIT License (MIT) | |
* <p/> | |
* Copyright (c) 2014 cab404 (cab404.ru) | |
* <p/> | |
* Permission is hereby granted, free of charge, to any person obtaining a copy<br/> | |
* of this software and associated documentation files (the "Software"), to deal<br/> | |
* in the Software without restriction, including without limitation the rights<br/> | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br/> | |
* copies of the Software, and to permit persons to whom the Software is<br/> | |
* furnished to do so, subject to the following conditions: | |
* <p/> | |
* The above copyright notice and this permission notice shall be included in<br/> | |
* all copies or substantial portions of the Software. | |
* <p/> | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br/> | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br/> | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br/> | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br/> | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br/> | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN<br/> | |
* THE SOFTWARE. | |
* <p/> | |
* Created at 3:44 on 07-12-2014 | |
* | |
* @author cab404 | |
*/ | |
public class JSONTemplate { | |
/** | |
* Node of a target address. | |
*/ | |
protected interface JSONAddressNode { | |
/** | |
* Retrieves an object this node pointing at. | |
* | |
* @param jsonObject from what to retrieve an object. | |
*/ | |
public Object move(Object jsonObject); | |
/** | |
* Sets value of something this node pointing at | |
* | |
* @param target where to set a value. | |
* @param value what to set | |
*/ | |
public void set(Object target, Object value); | |
} | |
/** | |
* JSONArray index address node | |
*/ | |
protected static class JSONArrayAddressNode implements JSONAddressNode { | |
protected final int index; | |
public JSONArrayAddressNode(int index) { | |
this.index = index; | |
} | |
@Override | |
public Object move(Object jsonObject) { | |
try { | |
return ((JSONArray) jsonObject).get(index); | |
} catch (JSONException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public void set(Object target, Object value) { | |
try { | |
((JSONArray) target).put(index, value); | |
} catch (JSONException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public String toString() { | |
return "Array:" + index; | |
} | |
} | |
/** | |
* JSONObject key address node | |
*/ | |
protected static class JSONObjectAddressNode implements JSONAddressNode { | |
protected final String key; | |
public JSONObjectAddressNode(String key) { | |
this.key = key; | |
} | |
@Override | |
public Object move(Object jsonObject) { | |
try { | |
return ((JSONObject) jsonObject).get(key); | |
} catch (JSONException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public void set(Object target, Object value) { | |
try { | |
((JSONObject) target).put(key, value); | |
} catch (JSONException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public String toString() { | |
return "Object:" + key; | |
} | |
} | |
/** | |
* What do we treat as a candidate for replacement | |
*/ | |
protected final static String REPLACE = "%%%", SECONDARY = "$?$?$"; | |
protected final JSONObject template_json; | |
/** | |
* Addresses to replace indexes | |
*/ | |
protected final List<List<JSONAddressNode>> targets; | |
/** | |
* Creates JSONObject template from a string. | |
* | |
* @see JSONTemplate | |
*/ | |
public JSONTemplate(String template) { | |
try { | |
/* Just testing. */ | |
new JSONObject(template); | |
this.targets = new ArrayList<>(); | |
StringBuilder temp_rebuild = new StringBuilder(template); | |
int count = 0; | |
int index, s_index = 0; | |
/* We are replacing all markers with indexed markers, | |
* so we won't have problems with order change in JSON */ | |
while ((index = temp_rebuild.indexOf(REPLACE, s_index)) != -1) { | |
/* Checking if we are in string element. */ | |
char s_char = temp_rebuild.charAt(index - 1); | |
char e_char = temp_rebuild.charAt(index + REPLACE.length()); | |
/* This might be escape character, so we're checking this too*/ | |
char s_e_char = temp_rebuild.charAt(index - 2); | |
if (s_char == e_char && (s_char == '"' || s_char == '\'') && s_e_char != '\\') { | |
temp_rebuild.replace(index, index + REPLACE.length(), SECONDARY + count); | |
count++; | |
} | |
s_index = index + 1; | |
} | |
template = temp_rebuild.toString(); | |
for (int i = 0; i < count; i++) | |
targets.add(null); | |
recurseThroughObject(new ArrayList<JSONAddressNode>(), template_json = new JSONObject(template)); | |
} catch (JSONException e) { | |
throw new RuntimeException("Template is not valid json string.", e); | |
} | |
} | |
protected void recurseThroughArray(List<JSONAddressNode> cur_path, JSONArray object) throws JSONException { | |
for (int key = 0; key < object.length(); key++) { | |
Object o = object.get(key); | |
if (o instanceof String) | |
if (((String) o).startsWith(SECONDARY)) { | |
int index = Integer.parseInt(((String) o).substring(SECONDARY.length())); | |
List<JSONAddressNode> copied_path = new ArrayList<>(cur_path); | |
copied_path.add(new JSONArrayAddressNode(key)); | |
targets.set(index, copied_path); | |
} else | |
continue; | |
if (o instanceof JSONArray) { | |
List<JSONAddressNode> copied_path = new ArrayList<>(cur_path); | |
copied_path.add(new JSONArrayAddressNode(key)); | |
recurseThroughArray(copied_path, (JSONArray) o); | |
} | |
if (o instanceof JSONObject) { | |
List<JSONAddressNode> copied_path = new ArrayList<>(cur_path); | |
copied_path.add(new JSONArrayAddressNode(key)); | |
recurseThroughObject(copied_path, (JSONObject) o); | |
} | |
} | |
} | |
protected void recurseThroughObject(List<JSONAddressNode> cur_path, JSONObject object) throws JSONException { | |
JSONArray names = object.names(); | |
ArrayList<String> names_array = new ArrayList<>(); | |
for (int $index = 0; $index < names.length(); $index++) { | |
names_array.add((String) names.get($index)); | |
} | |
for (String key : names_array) { | |
Object o = object.get(key); | |
if (o instanceof String) | |
if (((String) o).startsWith(SECONDARY)) { | |
int index = Integer.parseInt(((String) o).substring(SECONDARY.length())); | |
List<JSONAddressNode> copied_path = new ArrayList<>(cur_path); | |
copied_path.add(new JSONObjectAddressNode(key)); | |
targets.set(index, copied_path); | |
} | |
if (o instanceof JSONArray) { | |
List<JSONAddressNode> copied_path = new ArrayList<>(cur_path); | |
copied_path.add(new JSONObjectAddressNode(key)); | |
recurseThroughArray(copied_path, (JSONArray) o); | |
} | |
if (o instanceof JSONObject) { | |
List<JSONAddressNode> copied_path = new ArrayList<>(cur_path); | |
copied_path.add(new JSONObjectAddressNode(key)); | |
recurseThroughObject(copied_path, (JSONObject) o); | |
} | |
} | |
} | |
/** | |
* Creates deep copy of JSONArray | |
*/ | |
protected static JSONArray recursiveCloneJsonArray(JSONArray array) throws JSONException { | |
JSONArray copy = new JSONArray(); | |
for (int i = 0; i < array.length(); i++) { | |
Object obj = array.opt(i); | |
if (obj instanceof JSONArray) | |
copy.put(i, recursiveCloneJsonArray(((JSONArray) obj))); | |
else if (obj instanceof JSONObject) | |
copy.put(i, recursiveCloneJsonObject(((JSONObject) obj))); | |
else | |
copy.put(i, obj); | |
} | |
return copy; | |
} | |
/** | |
* Creates deep copy of JSONObject | |
*/ | |
protected static JSONObject recursiveCloneJsonObject(JSONObject object) throws JSONException { | |
JSONObject copy = new JSONObject(); | |
for (String i : getIterable(object.keys())) { | |
Object obj = object.opt(i); | |
if (obj instanceof JSONArray) | |
copy.put(i, recursiveCloneJsonArray(((JSONArray) obj))); | |
else if (obj instanceof JSONObject) | |
copy.put(i, recursiveCloneJsonObject(((JSONObject) obj))); | |
else | |
copy.put(i, obj); | |
} | |
return copy; | |
} | |
/** | |
* Wraps iterator into iterable | |
*/ | |
protected static <T> Iterable<T> getIterable(final Iterator<T> i) { | |
return new Iterable<T>() { | |
@Override public Iterator<T> iterator() { | |
return i; | |
} | |
}; | |
} | |
/** | |
* Creates a copy of a template | |
*/ | |
protected JSONObject obtainTemplateCopy() { | |
try { | |
return recursiveCloneJsonObject(template_json); | |
} catch (JSONException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
/** | |
* Builds JSONObject from template and parameters. | |
* | |
* @throws java.lang.RuntimeException if numbers of method and template parameters are not matching. | |
* @see JSONTemplate | |
*/ | |
public JSONObject build(Object... toApply) { | |
/* Checking if length of parameter array is matching such in our template */ | |
if (toApply.length != targets.size()) | |
throw new RuntimeException( | |
"You must supply exactly " + | |
targets.size() + " object" + (targets.size() == 1 ? "" : "s") | |
+ " to this template!" | |
); | |
JSONObject result = obtainTemplateCopy(); | |
for (int $target = 0; $target < toApply.length; $target++) { | |
List<JSONAddressNode> address = targets.get($target); | |
Object current_leaf = result; | |
/* Going down to out destination */ | |
for (int $deep = 0; $deep < address.size() - 1; $deep++) | |
current_leaf = address.get($deep).move(current_leaf); | |
/* Getting last piece of address and setting value it's pointing to given parameter*/ | |
address.get(address.size() - 1).set(current_leaf, toApply[$target]); | |
} | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment