private static UpgradeStringResult transformXml(
      String xml, String newFormatVersion, UpgradeOp... ops) {
    try {

      DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder;
      builder = builderFactory.newDocumentBuilder();
      Document document = builder.parse(new InputSource(new StringReader(xml)));

      // Check that this is a NodeBox document and set the new formatVersion.
      Element root = document.getDocumentElement();
      checkArgument(root.getTagName().equals("ndbx"), "This is not a valid NodeBox document.");
      root.setAttribute("formatVersion", newFormatVersion);

      // Loop through all upgrade operations.
      ArrayList<String> warnings = new ArrayList<String>();
      for (UpgradeOp op : ops) {
        op.start(root);
        transformXmlRecursive(document.getDocumentElement(), op);
        op.end(root);
        warnings.addAll(op.getWarnings());
      }

      TransformerFactory transformerFactory = TransformerFactory.newInstance();
      Transformer transformer = transformerFactory.newTransformer();
      DOMSource source = new DOMSource(document);
      StringWriter sw = new StringWriter();
      StreamResult result = new StreamResult(sw);
      transformer.transform(source, result);
      return new UpgradeStringResult(sw.toString(), warnings);
    } catch (Exception e) {
      throw new RuntimeException("Error while upgrading to " + newFormatVersion + ".", e);
    }
  }
 private static void transformXmlRecursive(Element e, UpgradeOp op) {
   op.apply(e);
   for (Element child : childElements(e)) {
     transformXmlRecursive(child, op);
   }
 }