DVPageDocumentDiff.between constructor

DVPageDocumentDiff.between(
  1. DVPageDocument? before,
  2. DVPageDocument after
)

What after changes relative to before. A null before is a page with nothing published: every top-level node is an addition.

Implementation

factory DVPageDocumentDiff.between(
  DVPageDocument? before,
  DVPageDocument after,
) {
  final Map<String, _Placed> old = before == null
      ? <String, _Placed>{}
      : _index(before.root, null, before);
  final Map<String, _Placed> next = _index(after.root, null, after);
  final List<DVPageNodeChange> changes = <DVPageNodeChange>[];

  // Newer version first, in document order, so the list reads top to bottom
  // the way the page does.
  void walk(DVPageNode node) {
    final _Placed here = next[node.id]!;
    final _Placed? was = old[node.id];
    final bool isRoot = node.id == after.root.id;
    if (was == null && !isRoot) {
      changes.add(
        DVPageNodeChange(
          kind: DVPageChangeKind.added,
          nodeId: node.id,
          label: here.label,
          summary: _summary(node),
          descendants: _count(node),
        ),
      );
      return; // Its children came with it.
    }
    if (was != null) {
      final List<DVPagePropertyChange> properties = _propertyChanges(
        was.node,
        node,
      );
      final bool moved = !isRoot && _moved(was, here, old, next);
      if (moved) {
        changes.add(
          DVPageNodeChange(
            kind: DVPageChangeKind.moved,
            nodeId: node.id,
            label: here.label,
            summary: _summary(node),
            properties: properties,
            fromParent: was.parent == null ? null : old[was.parent]!.label,
            toParent: here.parent == null ? null : next[here.parent]!.label,
          ),
        );
      } else if (properties.isNotEmpty) {
        changes.add(
          DVPageNodeChange(
            kind: DVPageChangeKind.changed,
            nodeId: node.id,
            label: here.label,
            summary: _summary(node),
            properties: properties,
          ),
        );
      }
    }
    for (final DVPageNode child in node.children) {
      walk(child);
    }
  }

  walk(after.root);

  // Then removals, top-most only: a removed container took its children.
  if (before != null) {
    void removed(DVPageNode node) {
      if (!next.containsKey(node.id)) {
        changes.add(
          DVPageNodeChange(
            kind: DVPageChangeKind.removed,
            nodeId: node.id,
            label: old[node.id]!.label,
            summary: _summary(node),
            descendants: _count(node),
          ),
        );
        return;
      }
      for (final DVPageNode child in node.children) {
        removed(child);
      }
    }

    removed(before.root);
  }

  return DVPageDocumentDiff._(
    title: before == null || before.title == after.title
        ? null
        : DVPagePropertyChange('title', before.title, after.title),
    nodes: List<DVPageNodeChange>.unmodifiable(changes),
  );
}