/**
   * Appends the index source map to the given buffer.
   *
   * @param out The stream to which the map will be appended.
   * @param name The name of the generated source file that this source map represents.
   * @param sections An ordered list of map sections to include in the index.
   * @throws IOException
   */
  @Override
  public void appendIndexMapTo(Appendable out, String name, List<SourceMapSection> sections)
      throws IOException {
    // Add the header fields.
    out.append("{\n");
    appendFirstField(out, "version", "3");
    appendField(out, "file", escapeString(name));

    // Add the line character maps.
    appendFieldStart(out, "sections");
    out.append("[\n");
    boolean first = true;
    for (SourceMapSection section : sections) {
      if (first) {
        first = false;
      } else {
        out.append(",\n");
      }
      out.append("{\n");
      appendFirstField(out, "offset", offsetValue(section.getLine(), section.getColumn()));
      if (section.getSectionType() == SourceMapSection.SectionType.URL) {
        appendField(out, "url", escapeString(section.getSectionValue()));
      } else if (section.getSectionType() == SourceMapSection.SectionType.MAP) {
        appendField(out, "map", section.getSectionValue());
      } else {
        throw new IOException("Unexpected section type");
      }
      out.append("\n}");
    }

    out.append("\n]");
    appendFieldEnd(out);

    out.append("\n}\n");
  }
  public void testWriteMetaMap2() throws IOException {
    StringWriter out = new StringWriter();
    String name = "./app.js";
    List<SourceMapSection> appSections =
        Lists.newArrayList(
            // Map and URLs can be mixed.
            SourceMapSection.forMap(getEmptyMapFor("./part.js"), 0, 0),
            SourceMapSection.forURL("src2", 100, 10));

    SourceMapGeneratorV3 generator = new SourceMapGeneratorV3();
    generator.appendIndexMapTo(out, name, appSections);

    assertEquals(
        "{\n"
            + "\"version\":3,\n"
            + "\"file\":\"./app.js\",\n"
            + "\"sections\":[\n"
            + "{\n"
            + "\"offset\":{\n"
            + "\"line\":0,\n"
            + "\"column\":0\n"
            + "},\n"
            + "\"map\":{\n"
            + "\"version\":3,\n"
            + "\"file\":\"./part.js\",\n"
            + "\"lineCount\":1,\n"
            + "\"mappings\":\";\",\n"
            + "\"sources\":[],\n"
            + "\"names\":[]\n"
            + "}\n"
            + "\n"
            + "},\n"
            + "{\n"
            + "\"offset\":{\n"
            + "\"line\":100,\n"
            + "\"column\":10\n"
            + "},\n"
            + "\"url\":\"src2\"\n"
            + "}\n"
            + "]\n"
            + "}\n",
        out.toString());
  }
  public void testWriteMetaMap() throws IOException {
    StringWriter out = new StringWriter();
    String name = "./app.js";
    List<SourceMapSection> appSections =
        Lists.newArrayList(
            SourceMapSection.forURL("src1", 0, 0),
            SourceMapSection.forURL("src2", 100, 10),
            SourceMapSection.forURL("src3", 150, 5));

    SourceMapGeneratorV3 generator = new SourceMapGeneratorV3();
    generator.appendIndexMapTo(out, name, appSections);

    assertEquals(
        "{\n"
            + "\"version\":3,\n"
            + "\"file\":\"./app.js\",\n"
            + "\"sections\":[\n"
            + "{\n"
            + "\"offset\":{\n"
            + "\"line\":0,\n"
            + "\"column\":0\n"
            + "},\n"
            + "\"url\":\"src1\"\n"
            + "},\n"
            + "{\n"
            + "\"offset\":{\n"
            + "\"line\":100,\n"
            + "\"column\":10\n"
            + "},\n"
            + "\"url\":\"src2\"\n"
            + "},\n"
            + "{\n"
            + "\"offset\":{\n"
            + "\"line\":150,\n"
            + "\"column\":5\n"
            + "},\n"
            + "\"url\":\"src3\"\n"
            + "}\n"
            + "]\n"
            + "}\n",
        out.toString());
  }
  public void testParseSourceMetaMap() throws Exception {
    final String INPUT1 = "file1";
    final String INPUT2 = "file2";
    LinkedHashMap<String, String> inputs = Maps.newLinkedHashMap();
    inputs.put(INPUT1, "var __FOO__ = 1;");
    inputs.put(INPUT2, "var __BAR__ = 2;");
    RunResult result1 = compile(inputs.get(INPUT1), INPUT1);
    RunResult result2 = compile(inputs.get(INPUT2), INPUT2);

    final String MAP1 = "map1";
    final String MAP2 = "map2";
    final LinkedHashMap<String, String> maps = Maps.newLinkedHashMap();
    maps.put(MAP1, result1.sourceMapFileContent);
    maps.put(MAP2, result2.sourceMapFileContent);

    List<SourceMapSection> sections = Lists.newArrayList();

    StringBuilder output = new StringBuilder();
    FilePosition offset = appendAndCount(output, result1.generatedSource);
    sections.add(SourceMapSection.forURL(MAP1, 0, 0));
    output.append(result2.generatedSource);
    sections.add(SourceMapSection.forURL(MAP2, offset.getLine(), offset.getColumn()));

    SourceMapGeneratorV3 generator = new SourceMapGeneratorV3();
    StringBuilder mapContents = new StringBuilder();
    generator.appendIndexMapTo(mapContents, "out.js", sections);

    check(
        inputs,
        output.toString(),
        mapContents.toString(),
        new SourceMapSupplier() {
          @Override
          public String getSourceMap(String url) {
            return maps.get(url);
          }
        });
  }