package org.smartly.commons.io.jsonrepository;
import org.json.JSONArray;
import org.json.JSONObject;
import org.smartly.commons.lang.CharEncoding;
import org.smartly.commons.logging.Level;
import org.smartly.commons.logging.Logger;
import org.smartly.commons.logging.util.LoggingUtils;
import org.smartly.commons.util.*;
import java.io.File;
import java.util.*;
public class JsonRepository {
private static final String NAME = "idx_name";
private static final String VALUE = "idx_value";
private static final String WILD_CHAR = "*.json";
private static final String FLD_ID = "_id";
private static final String FLD_PARENT = "_parent";
private static final String CHARSET = CharEncoding.getDefault();
private final String _root;
private final JSONObject _indexes;
private final Map<String, Object> _cache;
private boolean _useCache;
public JsonRepository(final String root) throws Exception {
_root = root;
_indexes = new JSONObject();
_cache = new HashMap<String, Object>();
this.load();
}
public Object get(final String path) {
return this.lookupValue(path);
}
public List<JSONObject> getList(final String path) {
final List<JSONObject> result = new LinkedList<JSONObject>();
final Object data = this.get(path);
if (data instanceof JSONObject) {
final JSONObject jsonData = (JSONObject) data;
final Iterator keys = jsonData.keys();
while (keys.hasNext()) {
final String key = keys.next().toString();
final Object value = jsonData.opt(key);
if (value instanceof JSONObject) {
result.add((JSONObject) value);
}
}
}
return result;
}
//-- utils --//
public JSONObject getJSONObject(final String path) {
final Object result = this.get(path);
if (result instanceof JSONObject) {
return (JSONObject) result;
}
return null;
}
public JSONArray getJSONArray(final String path) {
final Object result = this.get(path);
if (result instanceof JSONArray) {
return (JSONArray) result;
}
return null;
}
public String getString(final String path) {
return this.getString(path, "");
}
public String getString(final String path, final String def) {
final Object value = this.get(path);
return null != value ? (String) value : def;
}
public String[] getStringArray(final String path) {
final Object value = this.get(path);
return (value instanceof JSONArray) ? JsonWrapper.toArrayOfString((JSONArray) value) : new String[0];
}
public boolean getBoolean(final String path) {
return this.getBoolean(path, false);
}
public boolean getBoolean(final String path, final boolean def) {
final Object value = this.get(path);
return ConversionUtils.toBoolean(value, def);
}
public int getInt(final String path) {
return this.getInt(path, 0);
}
public int getInt(final String path, final int def) {
final Object value = this.get(path);
return ConversionUtils.toInteger(value, def);
}
public double getDouble(final String path) {
return this.getDouble(path, 0.0);
}
public double getDouble(final String path, final double def) {
final Object value = this.get(path);
return ConversionUtils.toDouble(value, -1, def);
}
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
private Logger getLogger() {
return LoggingUtils.getLogger(this);
}
private Object cacheGet(final String key) {
synchronized (_cache) {
return _cache.get(key);
}
}
private void cacheAdd(final String key, final Object value) {
synchronized (_cache) {
_cache.put(key, value);
}
}
//-- Methods for index initialization --//
private void load() throws Exception {
final List<File> files = new LinkedList<File>();
FileUtils.listFiles(files, new File(_root), WILD_CHAR);
for (final File file : files) {
this.load(file);
}
}
private void load(final File file) {
if (null != file) {
final String path = PathUtils.subtract(_root, PathUtils.validateFolderSeparator(file.getAbsolutePath()));
final String[] tokens = StringUtils.split(path, "/", true, true, false);
JSONObject parent = _indexes;
for (final String token : tokens) {
parent = this.load(parent, token, file);
}
}
}
private JSONObject load(final JSONObject parent, final String path, final File file) {
final String ext = PathUtils.getFilenameExtension(path);
final JSONObject result;
if (StringUtils.hasText(ext)) {
// file
result = this.loadFile(parent, path, file);
} else {
// directory
result = this.loadDir(parent, path, file);
}
return result;
}
private JSONObject loadDir(final JSONObject parent, final String path, final File file) {
final String name = PathUtils.getFilename(path, false);
JSONObject result = parent.optJSONObject(name);
if (null == result) {
result = new JSONObject();
this.setValue(result, NAME, name);
// add node to parent
this.setValue(parent, name, result);
}
return result;
}
private JSONObject loadFile(final JSONObject parent, final String path, final File file) {
final String name = PathUtils.getFilename(path, false);
final String parentName = parent.optString(NAME);
if (name.equalsIgnoreCase(parentName)) {
this.setValue(parent, VALUE, file.getAbsolutePath());
return parent;
}
final JSONObject result = new JSONObject();
this.setValue(result, NAME, name);
this.setValue(result, VALUE, file.getAbsolutePath());
// add node to parent
this.setValue(parent, name, result);
return result;
}
private void setValue(final JSONObject object, final String key,
final Object value) {
try {
object.put(key, value);
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
}
private JSONObject getIndex(final String indexName) {
synchronized (_indexes) {
return JsonWrapper.getJSON(_indexes, indexName);
}
}
//-- Methods for values --//
private Object lookupValue(final String path) {
Object result = null;
if (_useCache) {
result = this.cacheGet(path);
}
if (null == result) {
result = this.loadValue(path);
if (_useCache && null != result) {
this.cacheAdd(path, result);
}
}
return result;
}
private Object loadValue(final String path) {
final int dotcount = StringUtils.countOccurrencesOf(path, ".");
if (dotcount == 0) {
final JSONObject index = this.getIndex(path);
return this.loadItemFromIndex(index);
} else {
for (int i = dotcount; i > 0; i--) {
final String[] tokens = StringUtils.splitAt(i, path, ".");
if (tokens.length == 2) {
final JSONObject index = this.getIndex(tokens[0]);
if (null != index) {
final JsonDataManager data = this.loadDataFromIndex(index);
return data.get(tokens[1]);
}
}
}
}
return null;
}
private JsonDataManager loadDataFromIndex(final JSONObject index) {
if (null != index) {
return new JsonDataManager(index);
}
return null;
}
private JSONObject loadItemFromIndex(final JSONObject index) {
final JsonDataManager data = this.loadDataFromIndex(index);
return null != data ? data.getAll() : null;
}
private class JsonDataManager {
private final JSONObject _metadata;
private final JSONObject _data;
public JsonDataManager(final JSONObject metadata) {
_metadata = metadata;
_data = new JSONObject();
this.init();
}
public JSONObject getAll() {
return _data;
}
public Object get(final String path) {
return JsonWrapper.get(_data, path);
}
public String getID() {
if (null != _data) {
return _data.optString(FLD_ID, null);
}
return null;
}
public String getPARENT() {
if (null != _data) {
return _data.optString(FLD_PARENT, null);
}
return null;
}
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
private Logger getLogger() {
return LoggingUtils.getLogger();
}
private void init() {
this.init(_data, _metadata);
}
private void init(final JSONObject parent, final JSONObject metadata) {
final Iterator keys = metadata.keys();
while (keys.hasNext()) {
final String key = keys.next().toString();
if (!NAME.equalsIgnoreCase(key)) {
// creates node or load details
if (VALUE.equalsIgnoreCase(key)) {
final String value = metadata.optString(key);
this.loadData(parent, value);
//-- _ID --//
this.set_ID(parent, metadata.optString(NAME));
} else {
// add new property
final JSONObject itemData = new JSONObject();
JsonWrapper.put(parent, key, itemData);
// retrieve metadata
final JSONObject itemMetadata = metadata.optJSONObject(key);
if (null != itemMetadata) {
this.init(itemData, itemMetadata);
}
//-- _PARENT --//
this.set_PARENT(itemData, metadata.optString(NAME));
}
}
}
}
private void set_ID(final JSONObject itemData, final String id) {
try {
if (!itemData.has(FLD_ID)) {
itemData.putOpt(FLD_ID, id);
}
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
}
private void set_PARENT(final JSONObject itemData, final String id) {
try {
if (!itemData.has(FLD_PARENT)) {
itemData.putOpt(FLD_PARENT, id);
}
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
}
private void loadData(final JSONObject data, final String filename) {
try {
final String jsontext = new String(FileUtils.copyToByteArray(new File(filename)), CHARSET);
if (StringUtils.hasText(jsontext)) {
final JSONObject json = new JSONObject(jsontext);
JsonWrapper.extend(data, json);
}
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
}
}
}