com.balajeetm.mystique.core.JsonMystique Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of json-mystique Show documentation
Show all versions of json-mystique Show documentation
Json Utility to transform Jsons
The newest version!
/*
* Copyright (c) Balajee TM 2016.
* All rights reserved.
* License - @see
*/
/*
* Created on 25 Aug, 2016 by balajeetm
* http://www.balajeetm.com
*/
package com.balajeetm.mystique.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import com.balajeetm.mystique.core.lever.MystiqueLever;
import com.balajeetm.mystique.core.util.MystiqueConstants;
import com.balajeetm.mystique.util.gson.convertor.GsonConvertor;
import com.balajeetm.mystique.util.json.convertor.JsonConvertor;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import lombok.extern.slf4j.Slf4j;
/**
* The Class JsonMystique.
*
* @author balajeetm
*/
/** The Constant log. */
@Slf4j
public class JsonMystique {
/** The factory. */
private MystiqueFactory factory;
/** The json lever. */
private MystiqueLever jsonLever;
/** The gson convertor. */
private JsonConvertor gsonConvertor;
/** The mystiques. */
private Map>> mystiques;
/** Instantiates a new json genie. */
private JsonMystique() {
mystiques = new HashMap<>();
gsonConvertor = GsonConvertor.getInstance();
jsonLever = MystiqueLever.getInstance();
factory = MystiqueFactory.getInstance();
init();
}
/**
* Gets the single instance of JsonMystique.
*
* @return single instance of JsonMystique
*/
public static JsonMystique getInstance() {
return Creator.INSTANCE;
}
// Efficient Thread safe Lazy Initialization
// works only if the singleton constructor is non parameterized
/** The Class Creator. */
private static class Creator {
/** The instance. */
private static JsonMystique INSTANCE = new JsonMystique();
}
/** Inits the. */
protected void init() {
/* get all mystique rule files in the classpath */
String locationPattern = "classpath*:jsonmystique/**/*.mys";
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = null;
try {
resources = resourceResolver.getResources(locationPattern);
} catch (IOException e) {
log.info(
String.format("Error loading mystiques from %s - %s", locationPattern, e.getMessage()),
e);
return;
}
if (!ArrayUtils.isEmpty(resources)) {
for (Resource resource : resources) {
if (resource.exists()) {
String specName = FilenameUtils.removeExtension(resource.getFilename());
try {
List tarotList =
gsonConvertor.deserializeList(resource.getInputStream(), Tarot.class);
Map> tarotMap = new HashMap<>();
List depsList = new ArrayList<>();
List ruleList = new ArrayList<>();
tarotMap.put(MystiqueConstants.DEPS, depsList);
tarotMap.put(MystiqueConstants.TAROTS, ruleList);
mystiques.put(specName, tarotMap);
for (Tarot tarot : tarotList) {
List deps = tarot.getDeps();
if (CollectionUtils.isNotEmpty(deps)) {
depsList.addAll(deps);
}
tarot.setDeps(null);
ruleList.add(tarot);
}
} catch (JsonSyntaxException | IllegalArgumentException | IOException exception) {
log.info(
String.format(
"Unable to load mystiques %s from %s - %s. Trying to load other mystiques if any",
specName, locationPattern, exception.getMessage()),
exception);
continue;
}
}
}
} else {
log.warn(String.format("No mystiques found @ %s for transformation", locationPattern));
}
}
/**
* Transform.
*
* @param inputJson the input json
* @param specName the spec name
* @return the json element
*/
public JsonElement transform(String inputJson, String specName) {
JsonElement source = JsonParser.parseString(inputJson);
return transform(source, specName);
}
/**
* Transform.
*
* @param source the source
* @param specName the spec name
* @return the json element
*/
public JsonElement transform(JsonElement source, String specName) {
return transform(source, specName, new JsonObject());
}
/**
* Transform.
*
* @param inputJson the input json
* @param specName the spec name
* @param deps the deps
* @return the json element
*/
public JsonElement transform(String inputJson, String specName, JsonObject deps) {
JsonElement source = JsonParser.parseString(inputJson);
return transform(source, specName, deps);
}
/**
* Transform.
*
* @param source the source
* @param specName the spec name
* @param deps the deps
* @return the json element
*/
public JsonElement transform(JsonElement source, String specName, JsonObject deps) {
return transform(source, specName, deps, null);
}
/**
* Transform.
*
* @param source the source
* @param specName the spec name
* @param deps the deps
* @param aces the aces
* @return the json element
*/
public JsonElement transform(
JsonElement source, String specName, JsonObject deps, JsonObject aces) {
JsonElement transform = JsonNull.INSTANCE;
Map> map = mystiques.get(specName);
deps = jsonLever.asJsonObject(deps, new JsonObject());
if (null != map) {
List depList = map.get(MystiqueConstants.DEPS);
updateDependencies(source, depList, deps);
List tarotList = map.get(MystiqueConstants.TAROTS);
transform = transform(source, tarotList, deps, aces);
}
if (jsonLever.isNull(transform)) {
log.info(String.format("Transformed value for spec %s is null", specName));
}
return transform;
}
/**
* Transform to string.
*
* @param inputJson the input json
* @param specName the spec name
* @return the string
*/
public String transformToString(String inputJson, String specName) {
return String.valueOf(transform(inputJson, specName));
}
/**
* Transform to string.
*
* @param inputJson the input json
* @param specName the spec name
* @param deps the deps
* @return the string
*/
public String transformToString(String inputJson, String specName, JsonObject deps) {
return String.valueOf(transform(inputJson, specName, deps));
}
/**
* Transform to string.
*
* @param source the source
* @param specName the spec name
* @return the string
*/
public String transformToString(JsonElement source, String specName) {
return String.valueOf(transform(source, specName));
}
/**
* Transform to string.
*
* @param source the source
* @param specName the spec name
* @param deps the deps
* @return the string
*/
public String transformToString(JsonElement source, String specName, JsonObject deps) {
return String.valueOf(transform(source, specName, deps));
}
/**
* Transform.
*
* @param source the source
* @param tarotList the tarot list
* @param dependencies the dependencies
* @return the json element
*/
private JsonElement transform(
JsonElement source, List tarotList, JsonObject dependencies) {
return transform(source, tarotList, dependencies, null);
}
/**
* Transform.
*
* @param source the source
* @param tarotList the tarot list
* @param dependencies the dependencies
* @param parentAces the parent aces
* @return the json element
*/
private JsonElement transform(
JsonElement source, List tarotList, JsonObject dependencies, JsonObject parentAces) {
JsonObject resultWrapper = new JsonObject();
resultWrapper.add(MystiqueConstants.RESULT, JsonNull.INSTANCE);
if (CollectionUtils.isNotEmpty(tarotList)) {
List> cfs = new ArrayList<>();
for (Tarot tarot : tarotList) {
JsonObject turn = tarot.getTurn();
CompletableFuture getAces =
CompletableFuture.supplyAsync(
() -> {
JsonObject aces = tarot.getAces();
JsonObject updatedAces =
jsonLever.getUpdatedAces(
source, aces, dependencies, jsonLever.deepClone(aces));
jsonLever.simpleMerge(updatedAces, parentAces);
return updatedAces;
})
.exceptionally(
e -> {
String msg =
String.format(
"Error updating aces for turn %s - %s", turn, e.getMessage());
log.info(msg, e);
return parentAces;
});
CompletableFuture transformAsync =
getAces
.thenApplyAsync(
(aces) -> {
JsonElement transform = JsonNull.INSTANCE;
Spell spell =
getSpell(
source,
jsonLever.getJpath(tarot.getFrom()),
dependencies,
aces,
turn,
resultWrapper);
MystTurn mystique = factory.getMystTurn(turn);
transform = spell.cast(mystique);
return transform;
})
.exceptionally(
(e) -> {
String msg =
String.format(
"Error transforming input with specification for turn %s - %s",
turn, e.getMessage());
log.info(msg, e);
return JsonNull.INSTANCE;
});
CompletableFuture setResult =
getAces
.thenCombine(
transformAsync,
(aces, transform) ->
jsonLever.set(
resultWrapper,
jsonLever.getJpath(tarot.getTo()),
transform,
aces,
tarot.getOptional()))
.exceptionally(
e -> {
String msg =
String.format(
"Error setting output for turn %s - %s", turn, e.getMessage());
log.info(msg, e);
return resultWrapper;
});
cfs.add(setResult);
}
for (CompletableFuture completableFuture : cfs) {
completableFuture.join();
}
} else {
log.info(String.format("Invalid tarots. Tarots cannot be empty"));
}
return resultWrapper.get(MystiqueConstants.RESULT);
}
/**
* Gets the spell.
*
* @param source the source
* @param from the from
* @param dependencies the dependencies
* @param aces the aces
* @param turn the turn
* @param resultWrapper the result wrapper
* @return the spell
*/
private Spell getSpell(
JsonElement source,
JsonArray from,
JsonObject dependencies,
JsonObject aces,
JsonObject turn,
JsonObject resultWrapper) {
List fields = new ArrayList<>();
Boolean isFromLoopy = getFields(source, dependencies, aces, from, fields);
// Ideally isDeps should never be loopy
Spell spell =
isFromLoopy
? new LoopySpell(fields, dependencies, aces, turn, resultWrapper)
: new SimpleSpell(fields, dependencies, aces, turn, resultWrapper);
return spell;
}
/**
* Gets the fields.
*
* @param source the source
* @param dependencies the dependencies
* @param aces the aces
* @param path the path
* @param fields the fields
* @return the fields
*/
private Boolean getFields(
JsonElement source,
JsonObject dependencies,
JsonObject aces,
JsonArray path,
List fields) {
Boolean isLoopy = Boolean.FALSE;
if (null != path) {
if (path.size() > 0) {
for (JsonElement jsonElement : path) {
if (jsonElement.isJsonArray()) {
JsonArray fromArray = jsonElement.getAsJsonArray();
isLoopy =
isLoopy || jsonLever.updateFields(source, dependencies, aces, fields, fromArray);
// Once isloopy, the loop doesn't execute anymore
} else {
isLoopy = isLoopy || jsonLever.updateFields(source, dependencies, aces, fields, path);
break;
}
}
} else {
isLoopy = isLoopy || jsonLever.updateFields(source, dependencies, aces, fields, path);
}
}
return isLoopy;
}
/**
* Update dependencies.
*
* @param source the source
* @param deps the deps
* @param dependencies the dependencies
*/
private void updateDependencies(JsonElement source, List deps, JsonObject dependencies) {
if (CollectionUtils.isNotEmpty(deps)) {
try {
JsonObject transformJson =
jsonLever.asJsonObject(transform(source, deps, dependencies), new JsonObject());
jsonLever.simpleMerge(dependencies, transformJson);
} catch (RuntimeException e) {
log.info(String.format("Could not update dependencies : %s", e.getMessage()));
}
}
}
/**
* Register a custom turn.
*
* @param turn the turn
*/
public void register(MystTurn turn) {
factory.register(turn);
}
}