/**
  * Pretty prints the given node
  *
  * @param node the node, usually a document, to be printed
  * @param prefs the formatting preferences
  * @param style the formatting style to use
  * @param lineSeparator the line separator to use, or null to use the default
  * @param endWithNewline if true, ensure that the printed output ends with a newline
  * @return a formatted string
  */
 @NonNull
 public static String prettyPrint(
     @NonNull Node node,
     @NonNull XmlFormatPreferences prefs,
     @NonNull XmlFormatStyle style,
     @Nullable String lineSeparator,
     boolean endWithNewline) {
   XmlPrettyPrinter printer = new XmlPrettyPrinter(prefs, style, lineSeparator);
   printer.setEndWithNewline(endWithNewline);
   StringBuilder sb = new StringBuilder(1000);
   printer.prettyPrint(-1, node, null, null, sb, false /*openTagOnly*/);
   String xml = sb.toString();
   if (node.getNodeType() == Node.DOCUMENT_NODE && !xml.startsWith("<?")) { // $NON-NLS-1$
     xml = XML_PROLOG + xml;
   }
   return xml;
 }
 /**
  * Pretty-prints the given XML document, which must be well-formed. If it is not, the original
  * unformatted XML document is returned
  *
  * @param xml the XML content to format
  * @param prefs the preferences to format with
  * @param style the style to format with
  * @param lineSeparator the line separator to use, such as "\n" (can be null, in which case the
  *     system default is looked up via the line.separator property)
  * @return the formatted document (or if a parsing error occurred, returns the unformatted
  *     document)
  */
 @NonNull
 public static String prettyPrint(
     @NonNull String xml,
     @NonNull XmlFormatPreferences prefs,
     @NonNull XmlFormatStyle style,
     @Nullable String lineSeparator) {
   Document document = XmlUtils.parseDocumentSilently(xml, true);
   if (document != null) {
     XmlPrettyPrinter printer = new XmlPrettyPrinter(prefs, style, lineSeparator);
     printer.setEndWithNewline(xml.endsWith(printer.getLineSeparator()));
     StringBuilder sb = new StringBuilder(3 * xml.length() / 2);
     printer.prettyPrint(-1, document, null, null, sb, false /*openTagOnly*/);
     return sb.toString();
   } else {
     // Parser error: just return the unformatted content
     return xml;
   }
 }
  private static void formatFile(@NonNull XmlFormatPreferences prefs, File file, boolean stdout) {
    if (file.isDirectory()) {
      File[] files = file.listFiles();
      if (files != null) {
        for (File child : files) {
          formatFile(prefs, child, stdout);
        }
      }
    } else if (file.isFile() && SdkUtils.endsWithIgnoreCase(file.getName(), DOT_XML)) {
      XmlFormatStyle style = null;
      if (file.getName().equals(SdkConstants.ANDROID_MANIFEST_XML)) {
        style = XmlFormatStyle.MANIFEST;
      } else {
        File parent = file.getParentFile();
        if (parent != null) {
          String parentName = parent.getName();
          ResourceFolderType folderType = ResourceFolderType.getFolderType(parentName);
          if (folderType == ResourceFolderType.LAYOUT) {
            style = XmlFormatStyle.LAYOUT;
          } else if (folderType == ResourceFolderType.VALUES) {
            style = XmlFormatStyle.RESOURCE;
          }
        }
      }

      try {
        String xml = Files.toString(file, Charsets.UTF_8);
        Document document = XmlUtils.parseDocumentSilently(xml, true);
        if (document == null) {
          System.err.println("Could not parse " + file);
          System.exit(1);
          return;
        }

        if (style == null) {
          style = XmlFormatStyle.get(document);
        }
        boolean endWithNewline = xml.endsWith("\n");
        int firstNewLine = xml.indexOf('\n');
        String lineSeparator =
            firstNewLine > 0 && xml.charAt(firstNewLine - 1) == '\r' ? "\r\n" : "\n";
        String formatted =
            XmlPrettyPrinter.prettyPrint(document, prefs, style, lineSeparator, endWithNewline);
        if (stdout) {
          System.out.println(formatted);
        } else {
          Files.write(formatted, file, Charsets.UTF_8);
        }
      } catch (IOException e) {
        System.err.println("Could not read " + file);
        System.exit(1);
      }
    }
  }
  public void testRenderDrawable() throws Exception {
    VirtualFile file =
        myFixture.copyFileToProject(
            "drawables/progress_horizontal.xml", "res/drawable/progress_horizontal.xml");
    assertNotNull(file);
    RenderService service = getRenderService(file);
    assertNotNull(service);
    ILayoutPullParser parser = LayoutPullParserFactory.create(service);
    assertTrue(parser instanceof DomPullParser);
    Element root = ((DomPullParser) parser).getRoot();

    String layout = XmlPrettyPrinter.prettyPrint(root, true);
    assertEquals(
        "<ImageView\n"
            + "xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
            + "layout_width=\"fill_parent\"\n"
            + "layout_height=\"fill_parent\"\n"
            + "src=\"@drawable/progress_horizontal\" />\n",
        layout);

    checkRendering(service, "drawable/progress_horizontal.png");
  }
  @Override
  protected void postWriteAction() throws ConsumerException {

    // now write the values files.
    for (String key : mValuesResMap.keySet()) {
      // the key is the qualifier.

      // check if we have to write the file due to deleted values.
      // also remove it from that list anyway (to detect empty qualifiers later).
      boolean mustWriteFile = mQualifierWithDeletedValues.remove(key);

      // get the list of items to write
      List<ResourceItem> items = mValuesResMap.get(key);

      // now check if we really have to write it
      if (!mustWriteFile) {
        for (ResourceItem item : items) {
          if (item.isTouched()) {
            mustWriteFile = true;
            break;
          }
        }
      }

      if (mustWriteFile) {
        String folderName =
            key.isEmpty()
                ? ResourceFolderType.VALUES.getName()
                : ResourceFolderType.VALUES.getName() + RES_QUALIFIER_SEP + key;

        try {
          File valuesFolder = new File(getRootFolder(), folderName);
          createDir(valuesFolder);
          File outFile = new File(valuesFolder, FN_VALUES_XML);

          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          factory.setNamespaceAware(true);
          factory.setValidating(false);
          factory.setIgnoringComments(true);
          DocumentBuilder builder;

          builder = factory.newDocumentBuilder();
          Document document = builder.newDocument();

          Node rootNode = document.createElement(TAG_RESOURCES);
          document.appendChild(rootNode);

          Collections.sort(items);

          ResourceFile currentFile = null;
          for (ResourceItem item : items) {
            ResourceFile source = item.getSource();
            if (source != currentFile) {
              currentFile = source;
              rootNode.appendChild(document.createTextNode("\n"));
              File file = source.getFile();
              rootNode.appendChild(document.createComment(createPathComment(file)));
              rootNode.appendChild(document.createTextNode("\n"));
            }
            Node adoptedNode = NodeUtils.adoptNode(document, item.getValue());
            rootNode.appendChild(adoptedNode);
          }

          String content;
          try {
            content = XmlPrettyPrinter.prettyPrint(document, true);
          } catch (Throwable t) {
            content = XmlUtils.toXml(document, false);
          }

          Files.write(content, outFile, Charsets.UTF_8);
        } catch (Throwable t) {
          throw new ConsumerException(t);
        }
      }
    }

    // now remove empty values files.
    for (String key : mQualifierWithDeletedValues) {
      String folderName =
          key != null && !key.isEmpty()
              ? ResourceFolderType.VALUES.getName() + RES_QUALIFIER_SEP + key
              : ResourceFolderType.VALUES.getName();

      removeOutFile(folderName, FN_VALUES_XML);
    }
  }