Introducing the Zipper data structure 2 – Arrays, collections and data structures


The remaining code handles the initialization of children in a lazy fashion (on demand):

  @Override
  public Collection<? extends Zippable> getChildren() {
    lazyGetChildren();
    return (children != null) ?
      new LinkedList<>(Arrays.asList(children)) : null;
  }   
  
  // return the original node
  public T unwrap() {
    return node;
  }
  
  public boolean isLeaf() {      
    lazyGetChildren();
    return children == null || children.length == 0;
  }
 
  public boolean hasChildren() {
    lazyGetChildren();      
    return children != null && children.length > 0;
  }
 
  protected Zippable[] children() {
    lazyGetChildren();
    return children;
  }
        
  protected ZipNode<T> replaceNode(final T node) {
    lazyGetChildren();
    return new ZipNode<>(node, children);
  }      
  // lazy initialization of children
  private void lazyGetChildren() {
      
    if (children == DUMMY) {         
      Collection<? extends Zippable> nodeChildren
        = node.getChildren();
      children = (nodeChildren == null) ?
        null : nodeChildren.toArray(Zippable[]::new);
    }
  }
  @Override
  public String toString() {      
    return node.toString(); // call the original toString()
  }
}

All the Zipper operations act on a ZipNode not on a Node.Next, we have the Zipper range implementation which basically defines the gray part from figure 2.26. We have the parent node and the left/right siblings of the current range.

final class ZipperRange {
  
  private final ZipperRange parentRange;
  private final ZipNode<?> parentZipNode;
  private final Zippable[] leftSiblings;
  private final Zippable[] rightSiblings;
  protected ZipperRange(final ZipNode<?> parentZipNode,
      final ZipperRange parentRange, final Zippable[]
      leftSiblings, final Zippable[] rightSiblings) {
    this.parentZipNode = parentZipNode;
    this.parentRange = parentRange;
    this.leftSiblings = (leftSiblings == null) ?
      new Zippable[0] : leftSiblings;
    this.rightSiblings = (rightSiblings == null) ?
      new Zippable[0] : rightSiblings;
  } 
  // getters omitted for brevity
}

The ZipperRange works in tandem with the Cursor with contains the implementation of the Zipper actions (down(), up(), left(), right(), rightMost(), leftMost(), clear(), add(), addAll(), insertLeft(), insertRight(), remove(), removeLeft(), removeRight(), and so on:

public final class Cursor<T extends Zippable> {
  private final ZipNode<T> zipNode;
  private final ZipperRange range;
  protected Cursor(final ZipNode<T> zipNode,
                   final ZipperRange range) {
    this.zipNode = zipNode;
    this.range = range;
  }
  …
}

Since this code is significantly large it was skipped here. You can find it in the bundled code.Finally, we have the Zipper class. This class is used for creating a Zipper via the createZipper() method. It is also used for recreating/updating the tree based on the modifications done via the Zipper. This is done in the unwrapZipper() method as follows:

public final class Zipper {
  public static <T extends Zippable>
        Cursor<T> createZipper(final T node) {
    return new Cursor<>(new ZipNode<>(node),
      new ZipperRange(null, null, null, null)); // root range
  }
  public static <T extends Zippable> T unwrapZipper(
        final Cursor<T> tree) {
    return Zipper.<T>unwrapZipper(tree.root().zipNode());
  }
  private static <T extends Zippable> T unwrapZipper(
        final Zippable node) {
    if (node instanceof ZipNode<?>) {
      ZipNode<T> zipNode = (ZipNode<T>) node;
      T original = zipNode.unwrap();
      if (!zipNode.isLeaf()) {
        Collection<T> children
          = (Collection<T>) original.getChildren();
        original.getChildren().clear();
        for (Zippable zipped : zipNode.children()) {
          children.add((T) unwrapZipper(zipped));
        }
      }
    return original;
    } else {
      return (T) node;
    }
  }
}

In the bundled code you can find the complete implementation and an example of using the Zipper on a given tree.

Leave a Reply

Your email address will not be published. Required fields are marked *