gcube-cms-suite/geoportal-common/src/main/java/org/gcube/application/geoportal/common/model/JSONPathWrapper.java

226 lines
7.9 KiB
Java

package org.gcube.application.geoportal.common.model;
import com.jayway.jsonpath.*;
import com.mongodb.util.JSON;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.gcube.application.geoportal.common.JSONSerializationProvider;
import org.reflections.Reflections;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class JSONPathWrapper {
public static Configuration JSON_PATH_ALWAYS_LIST_CONFIG=null;
public static Configuration JSON_PATH_PATHS_CONFIGURATION=null;
static {
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.forPackage("org.gcube.application")
.filterInputsBy(new FilterBuilder().includePackage("org.gcube.application")));
reflections.getSubTypesOf(JSONSerializationProvider.class).iterator().forEachRemaining(providerClass->{
if(!providerClass.isInterface()){
try {
log.warn("Loading JSON Provider {} ",providerClass);
JSONSerializationProvider provider = providerClass.newInstance();
provider.setJSONWrapperDefaults();
}catch (Throwable t){
log.error("Unable to instantiate provider "+providerClass,t);
}
}
});
JSON_PATH_ALWAYS_LIST_CONFIG= Configuration.builder().options(Option.ALWAYS_RETURN_LIST,Option.SUPPRESS_EXCEPTIONS,Option.DEFAULT_PATH_LEAF_TO_NULL).build();
JSON_PATH_PATHS_CONFIGURATION = Configuration.builder().options(Option.AS_PATH_LIST,Option.SUPPRESS_EXCEPTIONS,Option.DEFAULT_PATH_LEAF_TO_NULL).build();
}
@Getter
DocumentContext valueCTX =null;
DocumentContext pathsCTX =null;
public JSONPathWrapper(String json) {
valueCTX =JsonPath.using(JSON_PATH_ALWAYS_LIST_CONFIG).parse(json);
pathsCTX =JsonPath.using(JSON_PATH_PATHS_CONFIGURATION).parse(json);
}
public List<Object> getByPath(String path){
return getByPath(path,Object.class);
}
public List<String> getMatchingPaths(String path){return pathsCTX.read(path); }
public <T> List<T> getByPath(String path,Class<T> clazz){
List<T> l= valueCTX.read(path, new TypeRef<List<T>>() {});
l.removeIf(p->p==null);
return l;
}
/**
* Changes the value of an existent field at @path
*
* @param path
* @param toSet
* @return
*/
public JSONPathWrapper setElement(String path, Object toSet){
log.debug("Setting Path {} = {}",path,toSet);
JsonPath jPath=JsonPath.compile(path);
if(jPath.isDefinite()){
valueCTX.set(path,toSet);
} else{
log.debug("Path is not definte, evaluating matching paths..");
for(String p : getMatchingPaths(path)){
log.debug("Actually setting {} as {} ",p,toSet);
setElement(p,toSet);
}
}
return this;
}
/**
* Creates a new element @elementName == @toSet as child of all matching @path
*
* @param path
* @param elementName
* @param toSet
* @return
*/
public JSONPathWrapper putElement(String path,String elementName, Object toSet){
log.debug("Putting {} = {} at Path {}",elementName,toSet,path);
JsonPath jPath=JsonPath.compile(path);
if(jPath.isDefinite()){
valueCTX.put(path,elementName,toSet);
} else{
log.debug("Path is not definte, evaluating matching paths..");
for(String p : getMatchingPaths(path)){
putElement(p,elementName,toSet);
}
}
return this;
}
/**
* Appends a new element @toSet as child of all arrays matching @path
*
* @param path
* @param toAdd
* @return
*/
public JSONPathWrapper addElementToArray(String path, Object toAdd){
log.debug("Setting Path {} = {}",path,toAdd);
JsonPath jPath=JsonPath.compile(path);
if(jPath.isDefinite()){
valueCTX.add(path,toAdd);
} else{
log.debug("Path is not definte, evaluating matching paths..");
for(String p : getMatchingPaths(path)){
addElementToArray(p,toAdd);
}
}
return this;
}
//***** EXTENSIONS
private static Pattern DOTTED_PATH_PATTERN=Pattern.compile("(\\$?\\.\\w*(\\[((\\?\\(.*\\))|(\\d+|\\*))\\])?)(?=(\\.|$)?)");
private static Pattern BRACKET_PATH_PATTERN=Pattern.compile("(\\$?\\['\\w*'\\](\\[((\\?\\(.*\\))|(\\d+|\\*))\\])?)");
/* private static final DocumentContext addElement(DocumentContext ctx,String path,Object value) throws JsonPathException{
log.debug("Inserting object at {}",path);
JsonPath jPath=JsonPath.compile(path);
if(jPath.isDefinite()) {
List<String> tokens=tokenizePath(path);
DocumentContext currentCtx=ctx;
String parentPath = null;
for(int i = 0; i<tokens.size();i++){
String s= tokens.get(i);
log.trace("Managing {}",s);
if(parentPath== null) parentPath="$";
else parentPath +=s;
String elementName = extractFieldNameFromPathElement(s);
if(i==tokens.size()-1) {
// IF LEAF SET
log.trace("{} is leaf, setting as {}", s, value);
currentCtx.put("$",elementName,value);
}else {
// Intermediate element
List<String> foundElements = currentCtx.read(s);
foundElements.removeIf(v->(v==null));
if(foundElements.isEmpty()){
Document newElement=null;
log.trace("{} not found..");
// add child
if (s.matches("")){
log.trace("{} is array, checking existence",s,value);
// ARRAY
// if array exists add elements else create new
List<?> foundArray= currentCtx.read(elementName,List.class);
throw new RuntimeException("Implement array support");
} else {
// Map
newElement = new Document();
}
currentCtx.put("$",elementName,newElement);
log.trace("Set {}, json is {}",s,currentCtx.jsonString());
currentCtx= JsonPath.parse(newElement.toJson());
}else currentCtx = JsonPath.parse(currentCtx.read(s,Document.class));
}
} return ctx;
}else throw new JsonPathException("Unable to initialize non-definite path : "+path);
}
*/
static List<String> tokenizePath(String path){
List<String> toReturn=null;
log.debug("Tokenizing JSON Path {} ",path);
// if path is $.element.child
if(path.matches("(\\$\\.)?\\w.*"))
toReturn= getByRegex(path, DOTTED_PATH_PATTERN);
else if (path.matches("(\\$)?\\[\\'.*"))
toReturn = getByRegex(path, BRACKET_PATH_PATTERN);
log.debug("Path {} as tokens {}",path,toReturn);
return toReturn;
}
private static List<String> getByRegex(String s,Pattern p){
ArrayList<String> toReturn = new ArrayList<>();
Matcher m = p.matcher(s);
log.trace("Groups from {} with {} : ",s,p.pattern());
while(m.find()) {
String found=m.group();
toReturn.add(found);
}
return toReturn;
}
static String extractFieldNameFromPathElement(String p){
Matcher m=Pattern.compile("\\$?\\.?(\\[')?([a-zA-Z_]+)").matcher(p);
m.find();
return m.group(2);
}
}