All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.futuresight.util.mystique.JsonMystique Maven / Gradle / Ivy

/*
 * Copyright (c) Balajee TM 2016.
 * All rights reserved.
 * License -  @see 
 */

/*
 * Created on 25 Aug, 2016 by balajeetm
 */
package com.futuresight.util.mystique;

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 javax.annotation.PostConstruct;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;

import com.futuresight.util.mystique.lever.GsonConvertor;
import com.futuresight.util.mystique.lever.MysCon;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;

/**
 * The Class JsonMystique.
 *
 * @author balajeetm
 */
@Component
public class JsonMystique {

	/** The logger. */
	protected Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	/** The factory. */
	@Autowired
	private MystiqueFactory factory;

	/** The json lever. */
	@Autowired
	private JsonLever jsonLever;

	/** The gson convertor. */
	@Autowired
	private GsonConvertor gsonConvertor;

	/** The mystiques. */
	private Map>> mystiques;

	/** The instance. */
	private static JsonMystique INSTANCE;

	/**
	 * Instantiates a new json genie.
	 */
	private JsonMystique() {
		mystiques = new HashMap<>();
	}

	/**
	 * Inits the.
	 */
	@PostConstruct
	protected void init() {
		String locationPattern = "classpath:jsonmystique/**/*.mys";
		ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
		Resource[] resources = null;
		try {
			resources = resourceResolver.getResources(locationPattern);
		}
		catch (IOException e) {
			logger.error(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(MysCon.DEPS, depsList);
						tarotMap.put(MysCon.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) {
						logger.error(String.format(
								"Unable to load mystiques %s from %s - %s. Trying to load other mystiques if any",
								specName, locationPattern, exception.getMessage()), exception);
						continue;
					}
				}
			}
		}
		else {
			logger.warn(String.format("No mystiques found @ %s for transformation", locationPattern));
		}
		INSTANCE = this;
	}

	/**
	 * Gets the single instance of JsonMystique.
	 *
	 * @return single instance of JsonMystique
	 */
	public static JsonMystique getInstance() {
		return INSTANCE;
	}

	/**
	 * Transform.
	 *
	 * @param inputJson the input json
	 * @param specName the spec name
	 * @return the json element
	 */
	public JsonElement transform(String inputJson, String specName) {
		JsonElement source = jsonLever.getJsonParser().parse(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 = jsonLever.getJsonParser().parse(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.getAsJsonObject(deps, new JsonObject());
		if (null != map) {
			List depList = map.get(MysCon.DEPS);
			updateDependencies(source, depList, deps);
			List tarotList = map.get(MysCon.TAROTS);
			transform = transform(source, tarotList, deps, aces);
		}

		if (jsonLever.isNull(transform)) {
			logger.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(MysCon.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();
					jsonLever.getUpdatedAces(source, aces, dependencies);
					jsonLever.simpleMerge(aces, parentAces);
					return aces;
				}).exceptionally(e -> {
					String msg = String.format("Error updating aces for turn %s - %s", turn, e.getMessage());
					logger.info(msg, e);
					return parentAces;
				});

				CompletableFuture transformAsync = getAces.thenApplyAsync((aces) -> {
					JsonElement transform = JsonNull.INSTANCE;
					Spell spell = getSpell(source, 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());
							logger.info(msg, e);
							return JsonNull.INSTANCE;
						});

				CompletableFuture setResult = getAces.thenCombine(
						transformAsync,
						(aces, transform) -> jsonLever.setField(resultWrapper, tarot.getTo(), transform, aces,
								tarot.getOptional())).exceptionally(e -> {
					String msg = String.format("Error setting output for turn %s - %s", turn, e.getMessage());
					logger.info(msg, e);
					return resultWrapper;
				});

				cfs.add(setResult);
			}

			for (CompletableFuture completableFuture : cfs) {
				completableFuture.join();
			}
		}
		else {
			logger.info(String.format("Invalid tarots. Tarots cannot be empty"));
		}
		return resultWrapper.get(MysCon.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.getAsJsonObject(transform(source, deps, dependencies),
						new JsonObject());
				jsonLever.simpleMerge(dependencies, transformJson);
			}
			catch (RuntimeException e) {
				logger.info(String.format("Could not update dependencies : %s", e.getMessage()));
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy