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

com.arextest.diff.handler.keycompute.ListKeyProcess Maven / Gradle / Ivy

There is a newer version: 0.2.15
Show newest version
package com.arextest.diff.handler.keycompute;

import com.arextest.diff.model.enumeration.Constant;
import com.arextest.diff.model.exception.ListKeyCycleException;
import com.arextest.diff.model.key.ListSortEntity;
import com.arextest.diff.model.key.ReferenceEntity;
import com.arextest.diff.model.log.LogEntity;
import com.arextest.diff.model.log.NodeEntity;
import com.arextest.diff.utils.JacksonHelperUtil;
import com.arextest.diff.utils.ListUti;
import com.arextest.diff.utils.StringUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ListKeyProcess {

  private static final Logger LOGGER = LoggerFactory.getLogger(ListKeyProcess.class);
  boolean useFirstElementKey = false;
  private List currentNodePath = new ArrayList<>();
  private List logs = new ArrayList<>();
  private List responseReferences;
  private List allListKeys;
  // 
  private HashMap> referenceKeys = new HashMap<>();
  // >
  private HashMap, HashMap> listIndexKeys = new HashMap<>();
  private List currentParentPath;

  public ListKeyProcess(List responseReferences,
      List allListKeys) {
    this.responseReferences = responseReferences;
    this.allListKeys = allListKeys;
  }

  public List getLogs() {
    return logs;
  }

  public HashMap, HashMap> getListIndexKeys() {
    return listIndexKeys;
  }

  /**
   * Get the listKey that needs to be calculated first
   *
   * @param responseReferences
   * @param allListKeys
   * @return
   * @throws ListKeyCycleException
   */
  private LinkedList computeReferencedListPriority(
      List responseReferences,
      List allListKeys) throws ListKeyCycleException {
    if (responseReferences == null || responseReferences.isEmpty()) {
      return new LinkedList<>();
    }

    Map> listKeysMap = this.getListKeysMap(allListKeys);

    // the collection of fkPaths
    Set fkPaths = new HashSet<>();
    // the collection of pkNodeListPaths
    Set pkListPaths = new HashSet<>();

    // fkPath -> the collection of pkNodeListPaths
    Map> relations = new HashMap<>();
    for (ReferenceEntity re : responseReferences) {
      String fkPath = ListUti.convertToString2(re.getFkNodePath());
      String pkListPath = ListUti.convertToString2(re.getPkNodeListPath());
      fkPaths.add(fkPath);
      pkListPaths.add(pkListPath);
      relations.computeIfAbsent(fkPath, k -> new HashSet<>()).add(pkListPath);
    }

    // all traversed node collections
    Set traversedSet = new HashSet<>();
    LinkedList queue = new LinkedList<>();

    this.doPriorityReferences(queue, pkListPaths, new LinkedList<>(), traversedSet, relations,
        fkPaths,
        listKeysMap);

    LinkedList listSortEntityQueue = new LinkedList<>();
    String path;
    while ((path = queue.poll()) != null) {
      for (int i = 0; i < allListKeys.size(); i++) {
        ListSortEntity entity = allListKeys.get(i);
        if (ListUti.convertToString2(entity.getListNodepath()).equals(path)) {
          listSortEntityQueue.add(entity);
          break;
        }
      }
    }

    return listSortEntityQueue;
  }

  /**
   * find all fkPaths in pkNodeListPath
   *
   * @param fkPaths        the collection of fkPaths
   * @param pkNodeListPath pkNodeListPath
   * @return
   */
  private Set findFkPathInListKey(Set fkPaths, String pkNodeListPath,
      Map> listKeysMap) {
    List keyPaths = listKeysMap.get(pkNodeListPath);
    if (keyPaths == null || keyPaths.isEmpty()) {
      return Collections.emptySet();
    }

    Set matchPath = new HashSet<>(keyPaths.size());
    for (String keyPath : keyPaths) {
      if (fkPaths.contains(keyPath)) {
        matchPath.add(keyPath);
      }
    }
    return matchPath;
  }

  private Map> getListKeysMap(List allListKeys) {
    if (allListKeys == null || allListKeys.isEmpty()) {
      return Collections.emptyMap();
    }

    Map> listKeysMap = new HashMap<>();
    for (ListSortEntity listSortEntity : allListKeys) {
      String listPath = ListUti.convertToString2(listSortEntity.getListNodepath());
      List keyPaths = new ArrayList<>();
      for (int i = 0; i < listSortEntity.getKeys().size(); i++) {
        String listKeyPath = ListUti
            .convertToString2(
                mergePath(listSortEntity.getListNodepath(), listSortEntity.getKeys().get(i)));
        keyPaths.add(listKeyPath);
      }
      listKeysMap.put(listPath, keyPaths);
    }
    return listKeysMap;
  }

  private void doPriorityReferences(LinkedList queue, Set refLinkNodes,
      LinkedList singleLinkAllNodeSet, Set traversedSet,
      Map> relations,
      Set fkPaths, Map> listKeysMap) throws ListKeyCycleException {
    if (refLinkNodes == null || refLinkNodes.isEmpty()) {
      return;
    }

    for (String refLinkNode : refLinkNodes) {
      if (singleLinkAllNodeSet.contains(refLinkNode)) {
        throw new ListKeyCycleException(
            String.format("an infinite loop occurs, path: %s", singleLinkAllNodeSet));
      }

      if (traversedSet.contains(refLinkNode)) {
        continue;
      }

      queue.addLast(refLinkNode);
      traversedSet.add(refLinkNode);

      Set refFkNodePaths = findFkPathInListKey(fkPaths, refLinkNode, listKeysMap);

      for (String refFkNode : refFkNodePaths) {
        singleLinkAllNodeSet.addLast(refLinkNode);
        Set refPkListPaths = relations.get(refFkNode);
        this.doPriorityReferences(queue, refPkListPaths, singleLinkAllNodeSet, traversedSet,
            relations, fkPaths,
            listKeysMap);
        singleLinkAllNodeSet.removeLast();
      }
    }
  }

  // add node name
  private String getKeyValueByPath(List relativePath, Object obj) {
    String result = null;
    if (relativePath == null || relativePath.isEmpty()
        || (relativePath.size() == 1 && relativePath.get(0).equals("%value%"))) {
      if (obj != null && !(obj instanceof NullNode) && !"".equals(((JsonNode) obj).asText())) {

        String value;
        List referencePaths = new ArrayList<>();
        for (List list : getReferencePath(currentParentPath)) {
          referencePaths.add(ListUti.convertToString2(list));
        }

        value = ((JsonNode) obj).asText();
        if (value.matches("\\d+\\.0+")) {
          value = value.substring(0, value.lastIndexOf('.'));
        }

        if (!referencePaths.isEmpty() && !value.equals("0")) {
          int cnt = 0;
          for (String referencePath : referencePaths) {
            String refKey = null;
            if (referenceKeys.containsKey(referencePath)) {
              refKey = referenceKeys.get(referencePath).get(value);
            }
            if (refKey != null) {
              cnt++;
              value = refKey;
            }
          }

          if (cnt == 0) {
            LogEntity log = new LogEntity(
                "The referenced node could not be found or the referenced List does not have listKey, fkNodePath: "
                    + ListUti.convertToString2(currentParentPath) + ", fkNodeValue: " + value);
            logs.add(log);
          }
          if (cnt > 1) {
            LogEntity log = new LogEntity("More than one referenced node, fkNodePath: "
                + ListUti.convertToString2(currentParentPath) + ", fkNodeValue: " + value);
            logs.add(log);
          }
        }

        String nodeName = currentParentPath.get(currentParentPath.size() - 1);
        StringBuilder sb = new StringBuilder();
        if (nodeName != null) {
          sb.append("(").append(nodeName).append(":").append(value).append(")");
        } else {
          sb.append("(").append(value).append(")");
        }
        result = sb.toString();
      }
      return replaceArexPrefix(result);
    }

    if (obj instanceof ObjectNode) {
      String path = relativePath.get(0);
      ObjectNode jsonObj = (ObjectNode) obj;

      if (path.contains("[first]")) {
        useFirstElementKey = true;
        path = path.substring(0, path.indexOf("[first]"));
      } else if (path.contains("[combination]")) {
        useFirstElementKey = false;
        path = path.substring(0, path.indexOf("[combination]"));
      } else {
        useFirstElementKey = false;
      }

      currentParentPath.add(path);
      Object subObj = null;
      if (path.equals(Constant.DYNAMIC_PATH)) {
        List names = JacksonHelperUtil.getNames(jsonObj);
        int size = names.size();
        if (size > 0) {
          List rList = new ArrayList<>(size);
          StringBuilder sb = new StringBuilder();
          sb.append("{");
          for (String name : names) {
            subObj = jsonObj.get(name);
            if (subObj != null) {
              ListUti.removeLast(currentParentPath);
              currentParentPath.add(name);
              result = getKeyValueByPath(relativePath.subList(1, relativePath.size()), subObj);
              if (result != null) {
                rList.add(result);
              }
            }
          }
          for (String r : rList) {
            sb.append(r);
          }
          sb.append("}");
          result = sb.toString();
        }
      } else {
        subObj = jsonObj.get(path);
        if (subObj != null) {
          result = getKeyValueByPath(relativePath.subList(1, relativePath.size()), subObj);
        }
      }
      currentParentPath.remove(currentParentPath.size() - 1);

    } else if (obj instanceof ArrayNode) {
      ArrayNode jsonArray = (ArrayNode) obj;
      int len = jsonArray.size();

      if (len > 0) {
        List list = new ArrayList<>(len);
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (useFirstElementKey) {
          currentParentPath.add(null);
          String elementKey = getKeyValueByPath(relativePath, jsonArray.get(0));
          if (elementKey != null) {
            list.add(elementKey);
          }
          currentParentPath.remove(currentParentPath.size() - 1);
          useFirstElementKey = false;
        } else {// combination default
          for (int i = 0; i < len; i++) {
            currentParentPath.add(null);
            String elementKey = getKeyValueByPath(relativePath, jsonArray.get(i));
            if (elementKey != null) {
              list.add(elementKey);
            }
            currentParentPath.remove(currentParentPath.size() - 1);
          }
        }

        if (list.size() > 1) {
          Collections.sort(list);
        }
        for (String s : list) {
          sb.append(s);
        }
        sb.append("]");
        result = sb.toString();
      }
    }
    return result;
  }

  public void computeAllListKey(Object obj) throws ListKeyCycleException {

    LinkedList priorityListSortEntities =
        this.computeReferencedListPriority(responseReferences, allListKeys);

    // priority list keys
    ListSortEntity listSortEntity;

    try {
      while ((listSortEntity = priorityListSortEntities.poll()) != null) {

        List listNodePath = listSortEntity.getListNodepath();
        List> keys = listSortEntity.getKeys();
        List referenceNodeRelativePath = listSortEntity.getReferenceNodeRelativePath();

        Object object = getObject(obj, listNodePath);

        if (!JacksonHelperUtil.isArrayNode(object)) {
          LOGGER.warn("list node path is not array node, path: {}", listNodePath);
          continue;
        }
        ArrayNode listObj = (ArrayNode) object;

        HashMap referenceKeyValue = new HashMap<>();
        referenceKeys.put(
            ListUti.convertToString2(mergePath(listNodePath, referenceNodeRelativePath)),
            referenceKeyValue);

        // traverse list elements, compute pKkey -> listkey
        for (int i = 0; i < listObj.size(); i++) {
          StringBuilder fullKey = new StringBuilder();
          Object listElement = listObj.get(i);

          Object refObj = getObject(listElement, referenceNodeRelativePath);
          String refValue = refObj == null ? null : StringUtil.objectToString(refObj);

          if (keys == null || keys.isEmpty()) {
            throw new RuntimeException("ref list node don't have listkey!");
          }
          for (List path : keys) {
            currentParentPath = ListUti.deepCopy(listNodePath);
            currentParentPath.add(null);

            String currentKey = getKeyValueByPath(path, listElement);
            if (currentKey != null) {
              fullKey.append(currentKey);
            }
          }
          // if refValue repeat, log
          referenceKeyValue.put(refValue, fullKey.toString());
        }
      }
    } catch (Throwable throwable) {
      LOGGER.error("computePriorityListSort error", throwable);
    }

    // other normal list keys,traverse the whole tree
    // currentNodePath.add(new NodeEntity("root", 0));

    computeNormalListKey(obj);

  }

  public void computeNormalListKey(Object obj) {

    if (obj instanceof ObjectNode) {
      ObjectNode jsonObj = (ObjectNode) obj;
      List names = JacksonHelperUtil.getNames(jsonObj);
      for (String fieldName : names) {
        currentNodePath.add(new NodeEntity(fieldName, 0));
        Object objFieldValue = jsonObj.get(fieldName);
        computeNormalListKey(objFieldValue);
        currentNodePath.remove(currentNodePath.size() - 1);
      }

    } else if (obj instanceof ArrayNode) {
      ArrayNode objArray = (ArrayNode) obj;

      ListSortEntity listSortEntity = findListKeys(currentNodePath);
      if (listSortEntity != null) {

        HashMap indexKeys = new HashMap<>();
        for (int i = 0; i < objArray.size(); i++) {

          StringBuilder fullKey = new StringBuilder();
          currentNodePath.add(new NodeEntity(null, i));

          Object listElement = objArray.get(i);

          for (List path : listSortEntity.getKeys()) {
            currentParentPath = ListUti.deepCopy(listSortEntity.getListNodepath());
            currentParentPath.add(null);

            String currentKey = getKeyValueByPath(path, listElement);
            if (currentKey != null) {
              fullKey.append(currentKey);
            }
          }

          indexKeys.put(i, fullKey.toString());
          computeNormalListKey(listElement);

          currentNodePath.remove(currentNodePath.size() - 1);
        }

        if (!indexKeys.isEmpty()) {
          listIndexKeys.put(new ArrayList<>(currentNodePath), indexKeys);
        }

      } else {
        for (int i = 0; i < objArray.size(); i++) {
          currentNodePath.add(new NodeEntity(null, i));
          Object listElement = objArray.get(i);
          computeNormalListKey(listElement);
          currentNodePath.remove(currentNodePath.size() - 1);
        }
      }

    }
  }

  private Object getObject(Object obj, List listNodePath) {
    for (String s : listNodePath) {
      if (!(JacksonHelperUtil.isObjectNode(obj))) {
        return null;
      }
      obj = ((ObjectNode) obj).get(s);
    }
    return obj;
  }

  private List mergePath(List path1, List path2) {
    List newPath = new ArrayList<>();
    newPath.addAll(path1);
    newPath.addAll(path2);
    return newPath;
  }

  private List> getReferencePath(List currentPath) {
    List> refPkPaths = new ArrayList<>();

    List paths = new ArrayList<>();
    for (String s : currentPath) {
      if (s != null) {
        paths.add(s);
      }
    }

    List list;
    for (ReferenceEntity responseReference : responseReferences) {
      List fkNodePath = responseReference.getFkNodePath();
      if ("%value%".equals(fkNodePath.get(fkNodePath.size() - 1))) {
        fkNodePath = fkNodePath.subList(0, fkNodePath.size() - 1);
      }
      // support dynamic path
      if (ListUti.stringListEqualsOnWildcard(paths, fkNodePath)) {
        list = responseReference.getPkNodePath();
        refPkPaths.add(list);
      }
    }
    return refPkPaths;
  }

  private ListSortEntity findListKeys(List path) {
    List pathWithoutIndex = new ArrayList<>();
    for (NodeEntity nodeEntity : path) {
      if (nodeEntity.getNodeName() != null) {
        pathWithoutIndex.add(nodeEntity.getNodeName());
      }
    }
    if (pathWithoutIndex.isEmpty()) {
      pathWithoutIndex.add("");
    }
    for (ListSortEntity listSortEntity : allListKeys) {
      // support dynamic path
      if (ListUti.stringListEqualsOnWildcard(listSortEntity.getListNodepath(), pathWithoutIndex)) {
        return listSortEntity;
      }
    }
    return null;
  }

  private String replaceArexPrefix(String key) {
    if (StringUtil.isEmpty(key)) {
      return key;
    }
    for (String prefix : Constant.PREFIX_SET) {
      key = key.replace(prefix, "");
    }
    return key;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy