Package foodev.jsondiff

Source Code of foodev.jsondiff.Leaf

package foodev.jsondiff;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import foodev.jsondiff.jsonwrap.JzonArray;
import foodev.jsondiff.jsonwrap.JzonElement;
import foodev.jsondiff.jsonwrap.JzonObject;
import foodev.jsondiff.jsonwrap.Wrapper;

class Leaf implements Comparable<Leaf> {

  Wrapper factory;

  final Node parent;
  JzonElement val;
  Oper oper;

  List<Leaf> children = new LinkedList<Leaf>();
  List<Leaf> newStructure = new LinkedList<Leaf>();

  Leaf(Node parent, JzonElement val) {
    this.parent = parent;
    this.val = val;
    this.parent.leaf = this;
  }

  boolean attach(Leaf leaf, Leaf at) {
    Leaf attach = this;
    int myIndex = (parent.parent == null) ? 0 : exactIndex(parent.parent.leaf.newStructure, this);
    int atIndex = (at == this) ? 0 : exactIndex(newStructure, at) + 1;
    while (leaf.oper != Oper.DELETE && attach.oper == Oper.DELETE && myIndex > 0) {
      myIndex--;
      attach = parent.parent.leaf.newStructure.get(myIndex);
      atIndex = attach.newStructure.size();
    }
    if (leaf.parent.parentHashCode == attach.parent.hashCode) {
      // direct attachment
      if (leaf.oper != Oper.DELETE && attach.oper == Oper.DELETE) {
        return attach.parent.parent.leaf.attach(leaf, this);
      }
      if (leaf.oper == null && (leaf.val.isJsonPrimitive() || leaf.val.isJsonNull())) {
        leaf.oper = Oper.SET;
      }
      attach.newStructure.add(atIndex, leaf);
      leaf.rehash(attach);
      if (JsonDiff.LOG.isLoggable(Level.FINE))
        JsonDiff.LOG.info("ATT " + leaf + " @" + this);
      return true;
    }
    if (parent.parent == null) {
      return false;
    }
    return parent.parent.leaf.attach(leaf, this);
  }

  boolean cancelDelete(Leaf deleted, Leaf with) {
    if (deleted.parent.hashCode == with.parent.hashCode) {
      if (JsonDiff.LOG.isLoggable(Level.FINE))
        JsonDiff.LOG.info("SET " + deleted + " @" + with);
      with.newStructure.clear();
      deleted.oper = Oper.SET;
      deleted.val = with.val;
      Leaf newParent = deleted.parent.parent.leaf;
      // recover deleted children (orphans)
      for (Leaf orphan : deleted.children) {
        orphan.parent.parent = deleted.parent;
        deleted.newStructure.add(orphan);
      }
      deleted.rehash(newParent);
      return true;
    }
    return false;
  }

  void rehash(Leaf newParent) {
    parent.rehash(newParent.parent);
    for (Leaf child : newStructure) {
      child.rehash(this);
    }
  }

  Leaf checkCancelation(Leaf possibleCancellation) {
    for (Iterator<Leaf> iterator2 = newStructure.iterator(); iterator2.hasNext();) {
      Leaf check = iterator2.next();
      if (check != possibleCancellation) {
        if (!check.parent.getClass().equals(possibleCancellation.parent.getClass())) {
          // type node change: cancellation
          return check;
        }
        if (possibleCancellation.parent instanceof ObjNode && check.parent.hashCode == possibleCancellation.parent.hashCode) {
          return check;
        }
      }
    }
    return null;
  }

  void checkCancellations() {
    for (Iterator<Leaf> it = newStructure.iterator(); it.hasNext();) {
      Leaf child = it.next();
      if (child.oper == Oper.DELETE) {
        Leaf cancelled = checkCancelation(child);
        if (cancelled != null) {
          if (cancelled.newStructure.isEmpty()) {
            cancelled.oper = Oper.SET;
          }
          it.remove();
        }
      }
    }
  }

  @Override
  public int compareTo(Leaf o) {
    return hashCode() - o.hashCode();
  }

  JsonDiff visitor;

  JzonArray createPatch(JzonObject patch) {
    JzonArray instructions = factory.createJsonArray();
    if (oper != Oper.DELETE) {
      checkCancellations();
      int i = 0, deletes = 0;
      for (Iterator<Leaf> it = newStructure.iterator(); it.hasNext();) {
        Leaf child = it.next();
        String key = child.parent.toString();
        String reIndexedKey = key;
        if (child.parent instanceof ArrNode) {
          ((ArrNode) child.parent).index = i - deletes;
          reIndexedKey = child.parent.toString();
        }
        JzonObject insert = factory.createJsonObject();
        boolean deeper = true;
        if (child.oper == Oper.INSERT) {
          insert.add("+" + reIndexedKey, child.val);
          instructions.insert(instructions.size(), insert);
          deeper = false;
        } else if (child.oper == Oper.SET) {
          insert.add(reIndexedKey, child.val);
          instructions.insert(instructions.size(), insert);
          deeper = false;
        } else if (child.oper == Oper.DELETE) {
          insert.addProperty("-" + reIndexedKey, 0);
          instructions.insert(instructions.size(), insert);
          deeper = false;
        }
        if (deeper) {
          JzonObject childPatch = factory.createJsonObject();
          JzonArray childInstructions = child.createPatch(childPatch);
          if (childInstructions.size() > 0) {
            if (visitor != null && !child.val.isJsonPrimitive() && !visitor.accept(child, childInstructions, childPatch)) {
              continue;
            }
            patch.add("~" + key, childInstructions);
          }
          if (!childPatch.entrySet().isEmpty()) {
            patch.add(key, childPatch);
          }
        }
        if (child.oper == Oper.DELETE) {
          deletes++;
        }
        i++;
      }
    } else {
      newStructure.clear();
    }
    return instructions;
  }

  void delete(Leaf leaf, Leaf at) {
    if (JsonDiff.LOG.isLoggable(Level.FINE))
      JsonDiff.LOG.info("DELETE " + leaf + " @" + this);
    leaf.oper = Oper.DELETE;
    for (Leaf orphan : leaf.newStructure) {
      orphan.parent.orphan();
    }
    leaf.newStructure.clear();
  }

  @Override
  public boolean equals(Object obj) {
    return hashCode() == ((Leaf) obj).hashCode();
  }

  @Override
  public int hashCode() {
    int i = parent.hashCode;
    if (val.isJsonArray()) {
      // for arr and obj we must hash in a type qualifier
      // since otherwise changes between these kinds of
      // nodes will be considered equal
      i = i * 31 + ArrNode.class.hashCode();
    } else if (val.isJsonObject()) {
      i = i * 31 + ObjNode.class.hashCode();
    } else {
      i = i * 31 + (val.isJsonPrimitive() || val.isJsonNull() ? val.hashCode() : 0);
    }
    return i;
  }

  void init() {
    this.parent.hashCode = this.parent.doHash(false);
    this.parent.parentHashCode = (this.parent.parent == null) ? 0 : this.parent.parent.doHash(false);
    this.newStructure.addAll(children);
  }

  void insert(Leaf leaf, Leaf where) {
    int hashCode = parent.hashCode;
    int insCode = leaf.parent.parent.hashCode;
    if (hashCode == 0 || insCode == hashCode) {
      // eligible for insertion - check for sets after building the new graph
      leaf.oper = Oper.INSERT;
      leaf.parent.parent = parent;
      leaf.newStructure.clear();
      if (where != null) {
        int insertAt = exactIndex(newStructure, where) + 1;
        newStructure.add(insertAt, leaf);
      } else {
        // direct insertion
        newStructure.add(0, leaf);
      }
      if (JsonDiff.LOG.isLoggable(Level.FINE))
        JsonDiff.LOG.info("INSERTed " + leaf + " @" + this);
    } else {
      orphans(where);
      parent.parent.leaf.insert(leaf, this);
    }
  }

  boolean isOrphan() {
    return parent.hashCode != 0 && parent.isOrphan();
  }

  void orphans(Leaf where) {
    List<Leaf> orphans = null;
    int insertDeletionsIndex = 0;
    if ((where == null && !newStructure.isEmpty()) || newStructure.size() == 1) {
      orphans = newStructure;
    } else if (newStructure.size() > 1) {
      insertDeletionsIndex = exactIndex(newStructure, where) + 1;
      orphans = newStructure.subList(insertDeletionsIndex, newStructure.size());
    }
    if (orphans != null) {
      List<Leaf> newOrphans = new ArrayList<Leaf>();
      for (Leaf orphan : orphans) {
        if (orphan.oper != Oper.DELETE) {
          orphan.parent.parent = null;
          Node clone = orphan.parent.clone();
          Leaf leafClone = new Leaf(clone, orphan.val);
          leafClone.visitor = visitor;
          clone.leaf = leafClone;
          leafClone.oper = Oper.DELETE;
          newOrphans.add(leafClone);
        } else {
          newOrphans.add(orphan);
        }
      }
      orphans.clear();
      newStructure.addAll(insertDeletionsIndex, newOrphans);
    }
  }

  JzonObject patch() {
    checkCancellations();
    JzonObject patch = factory.createJsonObject();
    if (oper == Oper.INSERT) {
      patch.add("+" + parent.toString(), val);
    } else if (oper == Oper.SET) {
      patch.add(parent.toString(), val);
    } else if (oper == Oper.DELETE) {
      patch.add("-" + parent.toString(), val);
    } else {
      JzonArray childInstructions = createPatch(patch);
      if (childInstructions.size() > 0) {
        patch.add("~", childInstructions);
      }
    }
    return patch;
  }

  void print() {
    print(0);
  }

  void print(int tab) {
    for (Leaf lEntry : newStructure) {
      for (int i = 0; i < tab; i++) {
        System.out.print("\t");
      }
      System.out.println(lEntry);
      lEntry.print(tab + 1);
    }
  }

  protected static int exactIndex(Collection<Leaf> c, Leaf check) {
    int i = -1;
    for (Leaf l : c) {
      i++;
      if (l == check) {
        return i;
      }
    }
    return i;
  }

  void recover(List<Leaf> fromLeaves) {
    if (isOrphan()) {
      int thisIndex = exactIndex(fromLeaves, this);
      recover(thisIndex, fromLeaves);
    }
    if (parent.parent != null) {
      parent.parent.leaf.recover(fromLeaves);
    }
  }

  void recover(int thisIndex, List<Leaf> fromLeaves) {
    if (isOrphan()) {
      Leaf newParent = null;
      while (newParent == null || (oper != Oper.DELETE && newParent.oper == Oper.DELETE)) {
        thisIndex--;
        newParent = fromLeaves.get(thisIndex);
        if (newParent.isOrphan()) {
          newParent.recover(thisIndex, fromLeaves);
        }
      }
      while (newParent.parent.parent != null && newParent.parent.hashCode != parent.parentHashCode) {
        newParent = newParent.parent.parent.leaf;
        if (newParent.isOrphan()) {
          newParent.recover(fromLeaves);
        }
      }
      if (newParent.oper == Oper.DELETE) {
        return;
      }
      if (newParent.attach(this, null)) {
        if (JsonDiff.LOG.isLoggable(Level.FINE))
          JsonDiff.LOG.info("RECOVERed " + this + " @" + newParent);
      } else {
        recover(thisIndex, fromLeaves);
      }
    }
  }

  @Override
  public String toString() {

    StringBuilder bld = new StringBuilder(newStructure.size() + "->");

    bld.append("LEAF");
    if (parent != null && parent instanceof ArrNode) {
      bld.append(parent.toString());
    }
    bld.append("<");
    if (oper != null) {
      bld.append(oper);
      bld.append("_");
    }
    if (val.isJsonPrimitive() || val.isJsonNull()) {
      bld.append("{");
      bld.append(val);
      bld.append("}");
    } else {
      bld.append(parent.toString());
      bld.append(":");
      bld.append(val);
    }
    bld.append("_");
    bld.append(hashCode());
    if (parent.isOrphan()) {
      bld.append("_ORPHAN");
    }
    bld.append(">");
    bld.append("\n");

    return bld.toString();

  }

}
TOP

Related Classes of foodev.jsondiff.Leaf

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.