Data.Storage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of DataStorage Show documentation
Show all versions of DataStorage Show documentation
single container for different data formats. json, xml, Array List, HashMap
package Data;
import DataTools.ConvertObjectToJson;
import DataTools.Utils;
import StorageHelper.Converter;
import StorageHelper.CreatorHelper;
import StorageHelper.PathParser;
import StorageHelper.RetrieveByPath;
import StorageHelper.Update;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import org.dom4j.Attribute;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.sip.message.Response;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
Copyright 2016 Alianza Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* A place to hold data that endpoints will need to interact with the system
* can handle different formats of data
*/
public class Storage {
private final HashMap storage;
/**
* default constructor
*/
public Storage() {
storage = new HashMap<>();
}
/**
* constructor that populates the collections with given data
* @param json JSONObject of data
*/
public Storage(JSONObject json) {
this();
addJson(json);
}
/**
* constructor that populates the collections with given data
* @param json JSONArray of data
*/
public Storage(JSONArray json) {
this();
addJson(json);
}
/**
* constructor that populates the collections with given data
* saves html data as a string with the key html
* @param data String of data or json String
*/
public Storage(String data) {
storage = CreatorHelper.createDataStorage(data).toMap();
}
/**
* constructor that populates the collections with given sip data response
* @param sipResponse SIPResponse from a sip request
*/
public Storage(Response sipResponse) {
this();
addSip(sipResponse);
}
/**
* constructor that populates the collections with given data
* @param model ax model of data
*/
public Storage(T model) {
this();
addModel(model);
}
/**
* constructor that populates the collections with given data
* @param dataMap hash map of data
*/
public Storage(HashMap dataMap) {
this();
addMap(dataMap);
}
/**
* constructor that populates the collections with given data
* @param array of data
*/
public Storage(ArrayList array) {
this();
addArray(array);
}
/**
* adding an item to the collection
* @param key index item is stored by
* @param value actual data being stored
*/
public void put(String key, Object value) {
//make sure it is an object and not something else, force polymorphism
if (value instanceof DateTime) {
put(key, (DateTime) value);
} else if (value instanceof String) {
put(key, value.toString());
} else if (value instanceof Storage) {
put(key, (Storage) value);
} else if (value instanceof Double) {
//does it have to be a double?
int iValue = ((Double) value).intValue();
int maxValue = new Double((Double) value + 0.99999).intValue();
if (iValue < maxValue) {
storage.put(key, value);
} else {
put(key, iValue);
}
} else {
ConvertObjectToJson jsonConverter = new ConvertObjectToJson();
JSONObject jsonValue = jsonConverter.convertToJson(value);
if (jsonValue == null) {
storage.put(key, value);
} else {
storage.put(key, jsonValue);
}
}
}
/**
* adding a date time item to the collection
* @param key index item is stored at
* @param value actual date being stored, a date time object
*/
public void put(String key, DateTime value) {
storage.put(key, ConvertObjectToJson.cleanupDate(value));
}
/**
* adding an item to the collection, if its string see if it is json string
* saves html data as a string
* @param key index item is stored by
* @param value string of the data, can be json string or just a string
*/
public void put(String key, String value) {
String tempValue = value.trim();
try {
if (Utils.isJsonArray(tempValue))
storage.put(key, new JSONArray(tempValue));
else if (Utils.isJSONObject(tempValue))
storage.put(key, new JSONObject(tempValue));
else if (Utils.isXml(tempValue)) {
String xmlValue = Converter.cleanXmlHeader(tempValue);
if (xmlValue.startsWith("") && xmlValue.endsWith("")) {
storage.put(key, xmlValue);
} else{
String xmlString = "<" + key + ">" + xmlValue + "" + key + ">";
xmlString = Converter.cleanXml(xmlString);
addXml(xmlString);
}
}
else
storage.put(key, value);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* adding a data Storage to the collection
* @param key index item is stored by
* @param value actual data storage data to be stored
*/
public void put(String key, Storage value) {
try {
//find out if it is an array or json
//if the collection is empty then hashmap
boolean isArray = true;
for(String storageKey : value.keys())
for(char k : storageKey.toCharArray())
//if there is just one key not a digit then not array
if (!Character.isDigit(k))
isArray = false;
//if the collection is empty then hashmap
if (value.keys().size() > 0 && isArray){
HashMap newJson = new HashMap<>();
newJson.put(key, value.toJsonArray());
addMap(newJson);
}
else {
JSONObject newJson = new JSONObject();
newJson.put(key, value.toJson());
addJson(newJson);
}
} catch (JSONException e) {
Utils.debug("unable to add to Data Storage:\nStorage: " + toString() + "\nAdding: " + value.toString(), "error");
e.printStackTrace();
}
}
/**
* adding an item to the collection that is from a path, and value is a string
* @param keys index path, item is stored by
* @param value actual data being stored, if string can format it as json or xml
*/
public void put(String[] keys, String value) {
Object parsedValue = ConvertObjectToJson.convertStringToObject(value);
put(keys, parsedValue);
}
/**
* adding an item to the collection that is from a path
* @param keys index path, item is stored by
* @param value actual data being stored
*/
public void put(String[] keys, Object value) {
//if it is a datetime object it needs to be formatted correctly
if (value instanceof DateTime) {
value = ConvertObjectToJson.cleanupDate((DateTime) value);
}
//only one item then use the normal puts
if (keys.length == 1) {
put(keys[0], value);
return;
}
{
//make sure path exists
RetrieveByPath pathSearch = new RetrieveByPath(this);
pathSearch.assurePathExists(keys);
}
//remove the last key of the path
String lastKey = keys[keys.length - 1];
String[] path = Utils.copyArrayExceptLast(keys);
//find the containing object
Object item = findElementByPath(path);
try {
Update update = new Update(item);
update.performUpdate(lastKey, value);
} catch (IllegalAccessException | InvocationTargetException e) {
Utils.debug("PATH INVALID:\nPATH: " + path + "\nDATA: " + toJson(), "error");
e.printStackTrace();
}
}
/**
* add an attribute, best use with xml
* @param path the path of the element
* @param attrKey the key of the attribute being added
* @param attrValue value of the attribute being added
*/
public void putAttribute(String[] path, String attrKey, String attrValue) {
int lastIndex = path.length - 1;
String lastKey = path[lastIndex];
//don't do anything if its an array, the key would be a number if an array
boolean isNumber = true;
for (char c : lastKey.toCharArray()) {
if (!Character.isDigit(c)){
isNumber = false;
}
}
//not supporting array attributes or adding attributes to non existent elements
if (isNumber|| !has(path)) return;
path[lastIndex] = lastKey + Converter.attributesId;
//create attributes json
if (!has(path)) {
put(path, new JSONObject());
}
//create new array
String[] attrPath = new String[path.length + 1];
//populate the path to the attribute
for (int i = 0; i < path.length; ++i) {
attrPath[i] = path[i];
}
attrPath[path.length] = attrKey;
put(attrPath, attrValue);
//change the data back to how it was
path[lastIndex] = lastKey;
}
/**
* remove item from collection
* @param key key of item to remove
*/
public void remove(String key) {
storage.remove(key);
}
/**
* remove item from collection found by path
* @param keys an array of the path to delete the item
*/
public void remove(String[] keys) {
//remove the last key of the path
String lastKey = keys[keys.length - 1];
String[] path = Utils.copyArrayExceptLast(keys);
//find the containing object
Object item = findElementByPath(path);
//perform the remove operation, casting to what it is to allow remove to be called
try { //set
Update update = new Update(item);
update.performRemove(lastKey);
} catch (Exception e) {
Utils.debug("PATH INVALID:\nPATH: " + path + "\nDATA: " + toJson(), "error");
e.printStackTrace();
}
}
/**
* get a single item from collection
* @param key key to find the stored item
* @return T the found item cast to expected type
*/
public T get(String key) {
return (T) storage.get(key);
}
/**
* get a single item from collection as an int
* @param key key to find stored item
* @return int the item as an int
*/
public int getInt(String key) {
//get the int value of doubles
String intValue = getString(key).split("\\.")[0];
return Integer.parseInt(intValue);
}
/**
* get a single item from collection as a String
* @param key key to find the stored item
* @return String the item as a string
*/
public String getString(String key) {
return storage.get(key).toString();
}
/**
* get a single item from collection
* @param key index to find the stored item
* @return T the found item cast to expected type
*/
public T get(int key) {
return get(String.valueOf(key));
}
/**
* get an object in collection that might be a sub object
* enter the path in an array, index of array is parent to child order
* index 0 is parent index 1 is child
* if it is an array just put the index as a string "0" will return first item in array
* example:
* {'results':[{'item':12}]}
* {"results", "0", "item"} returns 12
* @param path string array of the path
* @return Object the found item from the path
*/
public T get(String[] path) {
return (T) findElementByPath(path);
}
/**
* get an object by path like other get but returns a string
* @param keys the keys in order for the path to retrieve
* @return
*/
public String getString(String[] keys) {
return get(keys).toString();
}
/**
* get an object by path like other get but returns an int
* @param keys the keys in order for the path to retrieve
* @return
*/
public int getInt(String[] keys) {
String foundInt = getString(keys).split("\\.")[0];
return Integer.parseInt(foundInt);
}
/**
* get a saved attribute, best used with xml
* @param path path of the element where attribute is
* @param key key of desired attribute
* @return String value of attribute
*/
public String getAttribute(String[] path, String key) {
return getAttributes(path).get(key).toString();
}
/**
* get all the attributes for that key
* @param path the path to the key
* @return HashMap of the attributes
*/
public HashMap getAttributes(String[] path) {
int lastIndex = path.length - 1;
String lastKey = path[lastIndex];
path[lastIndex] = lastKey + Converter.attributesId;
return getAsDataStorage(path).toMap();
}
/**
* get object from collection as a Data Storage object
* give the path as an array like previous get(String[])
* @param path string array of the path
* @return DataStorage object populated with found object
*/
public Storage getAsDataStorage(String[] path) {
Object found = findElementByPath(path);
return CreatorHelper.createDataStorage(found);
}
/**
* get object from collection as a Data Storage object
* @param key string key of object, top level
* @return DataStorage object populated with found object
*/
public Storage getAsDataStorage(String key) {
Object found = get(key);
return CreatorHelper.createDataStorage(found);
}
/**
* get object from collection as a Data Storage object
* @param key index of object, top level
* @return DataStorage object populated with found object
*/
public Storage getAsDataStorage(int key) {
Object found = get(key);
return CreatorHelper.createDataStorage(found);
}
/**
* extract array from collection
* if the collection is not an array it will be empty
* @return ArrayList from the collection
*/
public ArrayList toArray() {
ArrayList items = new ArrayList();
for(int i = 0; has(String.valueOf(i)); ++i)
items.add(get(i));
return items;
}
/**
* get the size of the collection
* @return int number of items in top level of collection
*/
public int size() {
return storage.size();
}
/**
* see if collection has this key
* @param key the key we are looking for to exist
* @return boolean if it found the key in the collection
*/
public boolean has(String key) {
return storage.containsKey(key);
}
/**
* See if collection has a key based on a path
* @param path Path to the key to check
* @return boolean if it found the key in the collection
*/
public boolean has(String[] path) {
String key = path[path.length - 1];
if (key != null) {
for (String[] p : findPaths(key)) {
if (Arrays.equals(p, path)) {
return true;
}
}
}
return false;
}
/**
* gets a set of all available keys at highest level
* @return Set a set of the keys
*/
public Set keys() {
return storage.keySet();
}
/**
* add a json object to the collection
* converts the json and adds to the current collection
* @param json JSONObject you want to add to the collection
*/
public void addJson(JSONObject json) {
//gson setup
Gson converter = new Gson();
Type hashmapType = new TypeToken>() {}.getType();
HashMap converted = converter.fromJson(json.toString(), hashmapType);
addMap(converted);
}
/**
* add a json object to the collection
* converts the json and adds to the current collection
* @param json JSONArray you want to add to the collection
*/
public void addJson(JSONArray json) {
for(int i = 0; i < json.length(); ++i)
try {
Object nextItem = json.get(i);
if (nextItem.getClass().getTypeName().contains("JSONObject"))
nextItem = new Storage((JSONObject)nextItem).toMap();
put(String.valueOf(i), nextItem);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* add a xml to the collection, in the form of a string
* converts the xml and adds to the current collection
* @param xml String that is in xml format
*/
public void addXml(String xml) {
try{
addXml(Converter.xmlStringToElement(xml));
} catch (DocumentException e) {
//the xml thing just didn't work out, its not you its me
storage.put("data", xml);
}
}
/**
* add a xml to the collection, as an xml node
* @param xmlFile org.w3c.dom.Node object, parent node of xml
*/
public void addXml(Element xmlFile) {
ArrayList path = new ArrayList<>();
recurseOverElements(path, xmlFile);
}
/**
* add a model to the collection
* @param model model to get data from, example com.alianza.az.model.Account
* @param the type the model is
*/
public void addModel(T model) {
if (model instanceof Storage) {
addModel((Storage) model);
} else {
ConvertObjectToJson converter = new ConvertObjectToJson();
JSONObject jsonObject = converter.convertToJson(model);
addJson(jsonObject);
}
}
/**
* add a model to the collection
* @param model model to get data from, example com.alianza.az.model.Account
* @param the type the model is
*/
public void addModel(Storage model) {
addMap(model.toMap());
}
/**
* add a HashMap of data to the current collection
* @param adding hashmap of data
*/
public void addMap(HashMap adding) {
for (String key : adding.keySet()) {
put(key, adding.get(key));
}
}
/**
* add an array of data to the current collection
* @param array ArrayList of data to add
*/
public void addArray(List array) {
for(int i = 0; i < array.size(); ++i) {
put(String.valueOf(i), array.get(i));
}
}
/**
* take the response from sip and format to save data
* @param sipResponse the response from the sip request
*/
public void addSip(Response sipResponse) {
HashMap sipData = new HashMap<>();
String[] allSip = sipResponse.toString().split("\r\n");
//first line is the status, response code
sipData.put("statusCode", allSip[0]);
//get the response data, first line is the status
for(int i = 1; i < allSip.length; ++i) {
String[] rowData = allSip[i].split(": ");
//if : is not splitting them then it must be the body
//use = to split
if (rowData.length < 2)
rowData = allSip[i].split("=");
//sometimes empty space just skip those
if (rowData.length >= 2)
sipData.put(rowData[0], rowData[1]);
}
addMap(sipData);
}
/**
* get the full collection in json form
* @return JSONObject entire collection as json, or null if error
*/
public JSONObject toJson() {
Converter convert = new Converter(storage);
return convert.convertToJson();
}
/**
* get the an array in json array form
* @return JSONArray the collection as an array, will only contain array items
*/
public JSONArray toJsonArray() {
Converter converter = new Converter(storage);
return converter.convertToJsonArray();
}
/**
* get the full collection in xml form as a string
* @return String entire collection as xml string
*/
public String toXml() {
Converter converter = new Converter(storage);
return converter.convertToXml();
}
/**
* get the full collection as a hashMap
* @return HashMap(String, Object) the full collection in hash map form
*/
public HashMap toMap() {
return new HashMap<>(storage);
}
/**
* convert the storage object to a pojo object using faster xml object mapper
* @param valueType the type of object you want it to be mapped to
* @param
* @return T object of type T with data mapped as it is in the storage object
*/
public T toObject(Class valueType) {
return ConvertObjectToJson.convertToObject(toString(), valueType);
}
/**
* find all paths leading to a specific key in the collection
* find any strings that match that key name and give a full path to get to them
* @param searchKey string key looking for in the collection
* @return ArrayList of String[], each entry in array list is a path to the key searched for
*/
public ArrayList findPaths(String searchKey) {
Converter convert = new Converter(storage);
JSONObject json = convert.convertToJson();
PathParser pathParser = new PathParser(json);
return pathParser.findPaths(searchKey);
}
@Override
public String toString() {
return toJson().toString();
}
@Override
public boolean equals(Object otherStorage) {
//first check to see if they are even right types
if (!(otherStorage instanceof Storage) || ((Storage) otherStorage).toMap().size() != storage.size()) {
return false;
}
boolean same = true;
HashMap other = ((Storage)otherStorage).toMap();
for (Map.Entry entry : storage.entrySet()) {
//because of json objects we need to test by string too
if (other.get(entry.getKey()) != entry.getValue() &&
!other.get(entry.getKey()).toString().equals(entry.getValue().toString())) {
same = false;
}
}
return same;
}
private Object findElementByPath(String[] path) {
if (path.length < 1) {
return this;
}
RetrieveByPath pathSearch = new RetrieveByPath(this);
return pathSearch.findElementByPath(path);
}
/**
* helper for adding xml
* @param path where the xml will be added
* @param xmlData the data as an xml Element
*/
private void addXmlWithPath(ArrayList path, Element xmlData) {
boolean isXml = xmlData.elements().size() > 0;
//set the key
String[] thePath = new String[path.size()];
path.toArray(thePath);
//put either the value or an imbeded object
if (isXml && !has(thePath)) {
put(thePath, new Storage());
} else {
put(thePath, xmlData.getStringValue());
}
for (int i = 0; i < xmlData.attributeCount(); ++i) {
//get attribute data
Attribute attr = xmlData.attribute(i);
//save attribute
putAttribute(thePath, attr.getName(), attr.getValue());
}
//if it is xml need to see all children
if (isXml) {
recurseOverElements(path, xmlData);
}
}
/**
* helper for adding xml, goes over each element and add it to the storage, recursively
* @param path current path to save items
* @param xmlData data being saved as an xml Element
*/
private void recurseOverElements(ArrayList path, Element xmlData) {
String[] thePath = new String[path.size()];
path.toArray(thePath);
List elements = (List)xmlData.elements();
//different if an array
boolean isArray = false;
if (elements.size() > 1)
isArray = elements.get(0).getName().equals(elements.get(1).getName());
//if its an array then it should be saved as an array
if (isArray && getAsDataStorage(thePath).toArray().size() < 1) {
put(thePath, new JSONArray());
}
int i = 0;
for (Element element : elements) {
String key = isArray ? String.valueOf(i++) : element.getName();
ArrayList tempPath = (ArrayList) path.clone();
tempPath.add(key.replace(Converter.nsReplacer, ":"));
addXmlWithPath(tempPath, element);
}
}
}