/*
 * Copyright 2012-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.facebook.buck.command;

import com.facebook.buck.util.BuckConstant;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;

class CompilerXml {

  private static final ImmutableList<String> WILDCARD_RESOURCES = ImmutableList.of(
      "dtd",
      "ftl",
      "gif",
      "html",
      "jpeg",
      "jpg",
      "png",
      "properties",
      "tld",
      "xml",
      "json",
      "txt"
      );

  private final List<Module> modules;

  CompilerXml(List<Module> modules) {
    this.modules = Lists.newArrayList(modules);

    // Sort the list of modules so that the XML generated by this class is consistent even if the
    // modules are read in in a different order between runs.
    Collections.sort(modules);
  }

  private String generateXml() {
    StringBuilder buffer = new StringBuilder();

    buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    buffer.append("<project version=\"4\">\n");
    buffer.append("  <component name=\"CompilerConfiguration\">\n");
    buffer.append("    <option name=\"DEFAULT_COMPILER\" value=\"Javac\" />\n");

    // List all files that should be excluded from compilation.
    buffer.append("    <excludeFromCompile>\n");

    // Exclude directories that include files generated by Buck.
    buffer.append("      <directory url=\"file://$PROJECT_DIR$/" +
        BuckConstant.BUCK_OUTPUT_DIRECTORY +
        "\" includeSubdirectories=\"true\" />\n");

    // Exclude several files associated with each module.
    for (Module module : modules) {
      String url = "file://$PROJECT_DIR$/" + module.getModuleDirectoryPathWithSlash();
      buffer.append("      <file url=\"" + url + "BUCK\" />\n");

      if (module.isAndroidModule()) {
        buffer.append("      <directory url=\"" + url + "gen\" includeSubdirectories=\"true\" />\n");
        buffer.append("      <file url=\"" + url + "project.properties\" />\n");
      }
    }
    buffer.append("    </excludeFromCompile>\n");

    buffer.append("    <resourceExtensions />\n");
    buffer.append("    <wildcardResourcePatterns>\n");
    for (String extension : WILDCARD_RESOURCES) {
      buffer.append("      <entry name=\"?*." + extension + "\" />\n");
    }
    buffer.append("    </wildcardResourcePatterns>\n");

    buffer.append("  </component>\n");
    buffer.append("</project>\n");

    return buffer.toString();
  }

  /**
   * Writes the {@code compilerXml} file if and only if its content needs to be updated.
   * @return true if {@code compilerXml} was written
   */
  boolean write(File compilerXml) throws IOException {
    final Charset charset = Charsets.US_ASCII;

    String existingXml = null;
    if (compilerXml.exists()) {
      existingXml = Files.toString(compilerXml, charset);
    }

    String newXml = generateXml();
    boolean compilerXmlNeedsToBeWritten = !newXml.equals(existingXml);

    if (compilerXmlNeedsToBeWritten) {
      Files.write(newXml, compilerXml, charset);
    }

    return compilerXmlNeedsToBeWritten;
  }
}

@@ -768,16 +768,63 @@
 leList;%0A
+import com.google.common.collect.ImmutableSet;%0A
 import c
@@ -851,24 +851,24 @@
 lect.Lists;%0A
-
 import com.g
@@ -1307,16 +1307,279 @@
     );%0A%0A
+  /**%0A   * Exclude most directories that contain files generated by Buck.%0A   */%0A  private static final ImmutableSet%3CString%3E TOP_LEVEL_EXCLUDE_DIRS = ImmutableSet.of(%0A      BuckConstant.ANDROID_GEN_DIR,%0A      BuckConstant.BIN_DIR,%0A      BuckConstant.GEN_DIR%0A  );%0A%0A
   privat
@@ -2362,69 +2362,61 @@
     
-// Exclude directories that include files generated by Buck.%0A
+for (String excludeDir : TOP_LEVEL_EXCLUDE_DIRS) %7B%0A  
     
@@ -2490,45 +2490,25 @@
     
-BuckConstant.BUCK_OUTPUT_DIRECTORY
+  excludeDir
  +%0A
+  
     
@@ -2545,32 +2545,38 @@
 %5C%22true%5C%22 /%3E%5Cn%22);
+%0A    %7D
 %0A%0A    // Exclude

Stop excluding annotation-processor-generated files