@Override
  public void copy(URI fromURI, URI toURI) {
    // TODO - Determine how copy should work. Currently copy and move are
    // different. Copy works like an OS where "if destination exists, put
    // it underneath. else name if for destination. IE cp a/b n would create
    // just n... or if n already exists, then it would create n/b.
    // I don't think FS2 needs this level of complexity. Move is implemented
    // more simply, fast fail if destination exists.

    // DEEP copy
    boolean toRootPreExists = exists(toURI);

    URI newRootURI = CoreFS2Utils.createCopiedURI(toRootPreExists, fromURI, toURI, fromURI);

    // copy root
    copySingleNode(fromURI, newRootURI);

    // copy descendants (tree-walking order does not matter for memory
    // implementation)
    Set<URI> descendants = listDescendants(fromURI);
    for (URI descendant : descendants) {
      // there is some complexity determining the new location, based around
      // whether the target pre-exists
      URI descendantToURI =
          CoreFS2Utils.createCopiedURI(toRootPreExists, fromURI, toURI, descendant);
      copySingleNode(descendant, descendantToURI);
    }
  }
  @Override
  public FS2MetaSnapshot createObjectEntry(URI uri, String jsonMeta, InputStream payload) {
    try {

      FS2MetaSnapshot m = CoreFS2Utils.fromJSON(jsonMeta, FS2MetaSnapshotImpl.class);
      MemoryMetadataImpl mem = new MemoryMetadataImpl(m);

      FS2InMemoryObjectContainer c = new FS2InMemoryObjectContainer();
      c.metadata = mem;
      if (null != payload) {
        c.payload = CoreFS2Utils.streamToBytes(payload);
      }
      objectResolver.put(uri, c);
      return c.metadata;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  @Override
  public URI listParent(URI uri) {
    URI parent = CoreFS2Utils.parseParentURI(uri);

    // rather than simply parse, check for existence
    try {
      return objectResolver.get(parent).metadata.getURI();
    } catch (Exception e) {
      throw new RuntimeException("Unable to find parent.", e);
    }
  }
  @Override
  public Set<URI> listChilden(URI uri) {

    // the length of this uri
    int ulen = CoreFS2Utils.getPathAsArray(uri).length;

    // remove descendants
    Set<URI> children = new HashSet<URI>();

    for (URI candidate : objectResolver.keySet()) {
      if (!candidate.equals(uri) && candidate.toString().startsWith(uri.toString())) {
        // also needs to be direct child
        int clen = CoreFS2Utils.getPathAsArray(candidate).length;
        if (clen == ulen + 1) {
          children.add(candidate);
        }
      }
    }
    return children;
  }