MongoDBEmitter.java

package de.dlr.shepard.search;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.dlr.shepard.exceptions.ShepardParserException;
import de.dlr.shepard.util.Constants;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

public class MongoDBEmitter {

  private static final List<String> booleanOperators = List.of(
    Constants.JSON_AND,
    Constants.JSON_OR,
    Constants.JSON_NOT
  );
  private static final List<String> opAttributes = List.of(
    Constants.OP_PROPERTY,
    Constants.OP_VALUE,
    Constants.OP_OPERATOR
  );

  private MongoDBEmitter() {}

  public static String emitMongoDB(String query) {
    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode jsonNode = null;
    try {
      jsonNode = objectMapper.readValue(query, JsonNode.class);
    } catch (JsonProcessingException e) {
      throw new ShepardParserException("error while reading JSON\n" + e.getMessage());
    }
    return emitMongoDB(jsonNode);
  }

  private static String emitMongoDB(JsonNode rootNode) {
    String operator = "";
    try {
      operator = rootNode.fieldNames().next();
    } catch (NoSuchElementException e) {
      throw new ShepardParserException("error in parsing" + e.getMessage());
    }

    if (opAttributes.contains(operator)) {
      return emitPrimitiveClause(rootNode);
    } else if (booleanOperators.contains(operator)) {
      return emitComplexClause(rootNode, operator);
    } else {
      throw new ShepardParserException("unknown operator: " + operator);
    }
  }

  private static String emitOperatorString(JsonNode node) {
    String operator = node.textValue();
    return switch (operator) {
      case Constants.JSON_GT -> "$gt";
      case Constants.JSON_LT -> "$lt";
      case Constants.JSON_GE -> "$gte";
      case Constants.JSON_LE -> "$lte";
      case Constants.JSON_EQ -> "$eq";
      case Constants.JSON_IN -> "$in";
      case Constants.JSON_NE -> "$ne";
      default -> throw new ShepardParserException("unknown comparison operator " + operator);
    };
  }

  private static String emitComplexClause(JsonNode node, String operator) {
    if (operator.equals(Constants.JSON_NOT)) {
      return emitNegatedClause(node.get(Constants.JSON_NOT));
    } else {
      return emitMultaryClause(node, operator);
    }
  }

  private static String emitNegatedClause(JsonNode node) {
    String operator = node.fieldNames().next();
    if (opAttributes.contains(operator)) {
      return emitNegatedPrimitiveClause(node);
    } else if (booleanOperators.contains(operator)) {
      return emitNegatedComplexClause(node, operator);
    } else {
      throw new ShepardParserException("unknown operator: " + operator);
    }
  }

  private static String emitNegatedComplexClause(JsonNode node, String operator) {
    if (operator.equals(Constants.JSON_NOT)) return emitMongoDB(node.get(Constants.JSON_NOT));
    else return emitNegatedMultaryClause(node, operator);
  }

  private static String emitNegatedPrimitiveClause(JsonNode node) {
    String ret = "";
    ret = ret + node.get(Constants.OP_PROPERTY).textValue() + ": {";
    ret = ret + "$not: {";
    ret = ret + emitOperatorString(node.get(Constants.OP_OPERATOR)) + ": ";
    ret = ret + node.get(Constants.OP_VALUE) + "}}";
    return ret;
  }

  private static String emitPrimitiveClause(JsonNode node) {
    String ret = "";
    String property = node.get(Constants.OP_PROPERTY).textValue();
    ret = ret + property + ": {";
    ret = ret + emitOperatorString(node.get(Constants.OP_OPERATOR)) + ": ";
    ret = ret + node.get(Constants.OP_VALUE);
    ret = ret + "}";
    return ret;
  }

  private static String emitMultaryClause(JsonNode node, String operator) {
    String ret = "";
    Iterator<JsonNode> argumentsArray = node.get(operator).elements();
    ret = ret + emitBooleanOperator(operator) + " [{";
    String firstArgument = emitMongoDB(argumentsArray.next());
    ret = ret + firstArgument;
    while (argumentsArray.hasNext()) {
      ret = ret + "}, {" + emitMongoDB(argumentsArray.next());
    }
    ret = ret + "}]";
    return ret;
  }

  private static String emitNegatedMultaryClause(JsonNode node, String operator) {
    String ret = "";
    Iterator<JsonNode> argumentsArray = node.get(operator).elements();
    ret = ret + emitNegatedBooleanOperator(operator) + " [{";
    String firstArgument = emitNegatedClause(argumentsArray.next());
    ret = ret + firstArgument;
    while (argumentsArray.hasNext()) {
      ret = ret + "}, {" + emitNegatedClause(argumentsArray.next());
    }
    ret = ret + "}]";
    return ret;
  }

  private static String emitBooleanOperator(String operator) {
    return switch (operator) {
      case Constants.JSON_AND -> "$and:";
      case Constants.JSON_OR -> "$or:";
      default -> throw new ShepardParserException("unknown operator: " + operator);
    };
  }

  private static String emitNegatedBooleanOperator(String operator) {
    return switch (operator) {
      case Constants.JSON_AND -> "$or:";
      case Constants.JSON_OR -> "$and:";
      default -> throw new ShepardParserException("unknown operator: " + operator);
    };
  }
}