docs: static public VoltXMLDiff computeDiff(VoltXMLElement before, VoltXMLElement after)
      {
          // Top level call needs both names to match (I think this makes sense)
          if (!before.getUniqueName().equals(after.getUniqueName())) {
              // not sure this is best behavior, ponder as progress is made
              return null;
          }
  
          VoltXMLDiff result = new VoltXMLDiff(before.getUniqueName());
          // Short-circuit check for any differences first.  Can return early if there are no changes
          if (before.toMinString().equals(after.toMinString())) {
              return result;
          }
  
          // Store the final desired element order
          for (int i = 0; i < after.children.size(); i++) {
              VoltXMLElement child = after.children.get(i);
              result.m_elementOrder.put(child.getUniqueName(), i);
          }
  
          // first, check the attributes
          Set<String> firstKeys = before.attributes.keySet();
          Set<String> secondKeys = new HashSet<>();
          secondKeys.addAll(after.attributes.keySet());
          // Do removed and changed attributes walking the first element's attributes
          for (String firstKey : firstKeys) {
              if (!secondKeys.contains(firstKey)) {
                  result.m_removedAttributes.add(firstKey);
              }
              else if (!(after.attributes.get(firstKey).equals(before.attributes.get(firstKey)))) {
                  result.m_changedAttributes.put(firstKey, after.attributes.get(firstKey));
              }
              // remove the firstKey from secondKeys to track things added
              secondKeys.remove(firstKey);
          }
          // everything in secondKeys should be something added
          for (String key : secondKeys) {
              result.m_addedAttributes.put(key, after.attributes.get(key));
          }
  
          // Now, need to check the children.  Each pair of children with the same names
          // need to be descended to look for changes
          // Probably more efficient ways to do this, but brute force it for now
          // Would be helpful if the underlying children objects were Maps rather than
          // Lists.
          Set<String> firstChildren = new HashSet<>();
          for (VoltXMLElement child : before.children) {
              firstChildren.add(child.getUniqueName());
          }
          Set<String> secondChildren = new HashSet<>();
          for (VoltXMLElement child : after.children) {
              secondChildren.add(child.getUniqueName());
          }
          Set<String> commonNames = new HashSet<>();
          for (VoltXMLElement firstChild : before.children) {
              if (!secondChildren.contains(firstChild.getUniqueName())) {
                  // Need to duplicate the
                  result.m_removedElements.add(firstChild);
              }
              else {
                  commonNames.add(firstChild.getUniqueName());
              }
          }
          for (VoltXMLElement secondChild : after.children) {
              if (!firstChildren.contains(secondChild.getUniqueName())) {
                  result.m_addedElements.add(secondChild);
              }
              else {
                  assert(commonNames.contains(secondChild.getUniqueName()));
              }
          }
  
          // This set contains uniquenames
          for (String name : commonNames) {
              VoltXMLDiff childDiff = computeDiff(before.findChild(name), after.findChild(name));
              if (!childDiff.isEmpty()) {
                  result.m_changedElements.put(name, childDiff);
              }
          }
  
          return result;
      }