docs: public boolean copyChanges(final TreeRule rule, final boolean overwrite) {
      if (rule == null) {
        throw new IllegalArgumentException("Cannot copy a null rule");
      }
      if (tree_id != rule.tree_id) {
        throw new IllegalArgumentException("Tree IDs do not match");
      }
      if (level != rule.level) {
        throw new IllegalArgumentException("Levels do not match");
      }
      if (order != rule.order) {
        throw new IllegalArgumentException("Orders do not match");
      }
      
      if (overwrite || (rule.changed.get("type") && type != rule.type)) {
        type = rule.type;
        changed.put("type", true);
      }
      if (overwrite || (rule.changed.get("field") && !field.equals(rule.field))) {
        field = rule.field;
        changed.put("field", true);
      }
      if (overwrite || (rule.changed.get("custom_field") && 
          !custom_field.equals(rule.custom_field))) {
        custom_field = rule.custom_field;
        changed.put("custom_field", true);
      }
      if (overwrite || (rule.changed.get("regex") && !regex.equals(rule.regex))) {
        // validate and compile via the setter
        setRegex(rule.regex);
      }
      if (overwrite || (rule.changed.get("separator") && 
          !separator.equals(rule.separator))) {
        separator = rule.separator;
        changed.put("separator", true);
      }
      if (overwrite || (rule.changed.get("description") &&
          !description.equals(rule.description))) {
        description = rule.description;
        changed.put("description", true);
      }
      if (overwrite || (rule.changed.get("notes") && !notes.equals(rule.notes))) {
        notes = rule.notes;
        changed.put("notes", true);
      }
      if (overwrite || (rule.changed.get("regex_group_idx") && 
          regex_group_idx != rule.regex_group_idx)) {
        regex_group_idx = rule.regex_group_idx;
        changed.put("regex_group_idx", true);
      }
      if (overwrite || (rule.changed.get("display_format") && 
          !display_format.equals(rule.display_format))) {
        display_format = rule.display_format;
        changed.put("display_format", true);
      }
      for (boolean has_changes : changed.values()) {
        if (has_changes) {
          return true;
        }
      }
      return false;
    }