/**
   * add FlagImangesSet to Properties, which needn't to change the dir level, just ouput to the
   * ouput dir.
   *
   * @param prop job configuration
   * @param key list name
   * @param set relative flag image files
   */
  private void addFlagImagesSetToProperties(
      final Job prop, final String key, final Set<String> set) {
    String value = null;
    final Set<String> newSet = new LinkedHashSet<String>(INT_128);
    for (final String file : set) {
      if (new File(file).isAbsolute()) {
        // no need to append relative path before absolute paths
        newSet.add(FileUtils.normalize(file));
      } else {
        // In ant, all the file separator should be slash, so we need to
        // replace all the back slash with slash.
        newSet.add(
            FileUtils.separatorsToUnix(
                FileUtils.normalize(new StringBuffer().append(file).toString())));
      }
    }

    // write list attribute to file
    final String fileKey = key.substring(0, key.lastIndexOf("list")) + "file";
    prop.setProperty(fileKey, key.substring(0, key.lastIndexOf("list")) + ".list");
    final File list = new File(tempDir, prop.getProperty(fileKey));
    Writer bufferedWriter = null;
    try {
      bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(list)));
      final Iterator<String> it = newSet.iterator();
      while (it.hasNext()) {
        bufferedWriter.write(it.next());
        if (it.hasNext()) {
          bufferedWriter.write("\n");
        }
      }
      bufferedWriter.flush();
      bufferedWriter.close();
    } catch (final FileNotFoundException e) {
      logger.logException(e);
    } catch (final IOException e) {
      logger.logException(e);
    } finally {
      if (bufferedWriter != null) {
        try {
          bufferedWriter.close();
        } catch (final IOException e) {
          logger.logException(e);
        }
      }
    }

    value = StringUtils.assembleString(newSet, COMMA);

    prop.setProperty(key, value);

    // clear set
    set.clear();
    newSet.clear();
  }
 /**
  * Add key definition to job configuration
  *
  * @param prop job configuration
  * @param key list name
  * @param set key defintions to add
  */
 private void addKeyDefSetToProperties(
     final Job prop, final String key, final Collection<KeyDef> set) {
   // update value
   final Collection<KeyDef> updated = new ArrayList<KeyDef>(set.size());
   for (final KeyDef file : set) {
     String keys = FileUtils.separatorsToUnix(FileUtils.normalize(prefix + file.keys));
     String href = file.href;
     String source = file.source;
     if (prefix.length() != 0) {
       // cases where keymap is in map ancestor folder
       keys = keys.substring(prefix.length());
       if (href == null) {
         // href = FileUtils.separatorsToUnix(FileUtils.normalize(prefix));
         source = FileUtils.separatorsToUnix(FileUtils.normalize(prefix + source));
       } else {
         if (!exKeyDefMap.containsKey(file.keys)) {
           href = FileUtils.separatorsToUnix(FileUtils.normalize(prefix + href));
         }
         source = FileUtils.separatorsToUnix(FileUtils.normalize(prefix + source));
       }
     }
     final KeyDef keyDef = new KeyDef(keys, href, source);
     updated.add(keyDef);
   }
   // write key definition
   try {
     writeKeydef(new File(tempDir, "keydef.xml"), updated);
   } catch (final DITAOTException e) {
     logger.logError("Failed to write key definition file: " + e.getMessage(), e);
   }
   // write list file
   final Set<String> newSet = new LinkedHashSet<String>(set.size());
   for (final KeyDef keydef : updated) {
     newSet.add(keydef.toString());
   }
   prop.setSet(key, newSet);
   final String fileKey = key.substring(0, key.lastIndexOf("list")) + "file";
   prop.setProperty(fileKey, key.substring(0, key.lastIndexOf("list")) + ".list");
   try {
     prop.writeList(key);
   } catch (final IOException e) {
     logger.logError("Failed to write key list file: " + e.getMessage(), e);
   }
 }
  /**
   * Add set of values of job configuration
   *
   * @param prop job configuration
   * @param key list name
   * @param set values to add
   */
  private void addSetToProperties(final Job prop, final String key, final Set<String> set) {
    // update value
    final Set<String> newSet = new LinkedHashSet<String>(INT_128);
    for (final String file : set) {
      if (new File(file).isAbsolute()) {
        // no need to append relative path before absolute paths
        newSet.add(FileUtils.normalize(file));
      } else {
        // In ant, all the file separator should be slash, so we need to
        // replace all the back slash with slash.
        final int index = file.indexOf(EQUAL);
        if (index != -1) {
          // keyname
          final String to = file.substring(0, index);
          final String source = file.substring(index + 1);

          newSet.add(
              FileUtils.separatorsToUnix(
                      FileUtils.normalize(new StringBuffer(prefix).append(to).toString()))
                  + EQUAL
                  + FileUtils.separatorsToUnix(
                      FileUtils.normalize(new StringBuffer(prefix).append(source).toString())));
        } else {
          newSet.add(
              FileUtils.separatorsToUnix(
                  FileUtils.normalize(new StringBuffer(prefix).append(file).toString())));
        }
      }
    }
    prop.setSet(key, newSet);
    // write list file
    final String fileKey = key.substring(0, key.lastIndexOf("list")) + "file";
    prop.setProperty(fileKey, key.substring(0, key.lastIndexOf("list")) + ".list");
    try {
      prop.writeList(key);
    } catch (final IOException e) {
      logger.logError("Failed to write list file: " + e.getMessage(), e);
    }
  }
  /**
   * Write result files.
   *
   * @throws DITAOTException if writing result files failed
   */
  private void outputResult() throws DITAOTException {
    final File dir = new File(tempDir);
    if (!dir.exists()) {
      dir.mkdirs();
    }

    Job prop = null;
    try {
      prop = new Job(dir);
    } catch (final IOException e) {
      throw new DITAOTException("Failed to create empty job: " + e.getMessage(), e);
    }

    prop.setProperty(INPUT_DIR, baseInputDir);
    prop.setProperty(INPUT_DITAMAP, prefix + inputFile);

    prop.setProperty(INPUT_DITAMAP_LIST_FILE_LIST, USER_INPUT_FILE_LIST_FILE);
    final File inputfile = new File(tempDir, USER_INPUT_FILE_LIST_FILE);
    Writer bufferedWriter = null;
    try {
      bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(inputfile)));
      bufferedWriter.write(prefix + inputFile);
      bufferedWriter.flush();
    } catch (final FileNotFoundException e) {
      logger.logException(e);
    } catch (final IOException e) {
      logger.logException(e);
    } finally {
      if (bufferedWriter != null) {
        try {
          bufferedWriter.close();
        } catch (final IOException e) {
          logger.logException(e);
        }
      }
    }

    // add out.dita.files,tempdirToinputmapdir.relative.value to solve the
    // output problem
    relativeValue = prefix;
    formatRelativeValue = formatRelativeValue(relativeValue);
    prop.setProperty("tempdirToinputmapdir.relative.value", formatRelativeValue);

    prop.setProperty("uplevels", getUpdateLevels());
    addSetToProperties(prop, OUT_DITA_FILES_LIST, outDitaFilesSet);

    addSetToProperties(prop, FULL_DITAMAP_TOPIC_LIST, ditaSet);
    addSetToProperties(prop, FULL_DITA_TOPIC_LIST, fullTopicSet);
    addSetToProperties(prop, FULL_DITAMAP_LIST, fullMapSet);
    addSetToProperties(prop, HREF_DITA_TOPIC_LIST, hrefTopicSet);
    addSetToProperties(prop, CONREF_LIST, conrefSet);
    addSetToProperties(prop, IMAGE_LIST, imageSet);
    addSetToProperties(prop, FLAG_IMAGE_LIST, flagImageSet);
    addSetToProperties(prop, HTML_LIST, htmlSet);
    addSetToProperties(prop, HREF_TARGET_LIST, hrefTargetSet);
    addSetToProperties(prop, HREF_TOPIC_LIST, hrefWithIDSet);
    addSetToProperties(prop, CHUNK_TOPIC_LIST, chunkTopicSet);
    addSetToProperties(prop, SUBJEC_SCHEME_LIST, schemeSet);
    addSetToProperties(prop, CONREF_TARGET_LIST, conrefTargetSet);
    addSetToProperties(prop, COPYTO_SOURCE_LIST, copytoSourceSet);
    addSetToProperties(prop, SUBSIDIARY_TARGET_LIST, subsidiarySet);
    addSetToProperties(prop, CONREF_PUSH_LIST, conrefpushSet);
    addSetToProperties(prop, KEYREF_LIST, keyrefSet);
    addSetToProperties(prop, CODEREF_LIST, coderefSet);

    // @processing-role
    addSetToProperties(prop, RESOURCE_ONLY_LIST, resourceOnlySet);

    addFlagImagesSetToProperties(prop, REL_FLAGIMAGE_LIST, relFlagImagesSet);

    // Convert copyto map into set and output
    final Set<String> copytoSet = new HashSet<String>(INT_128);
    for (final Map.Entry<String, String> entry : copytoMap.entrySet()) {
      copytoSet.add(entry.toString());
    }
    addSetToProperties(prop, COPYTO_TARGET_TO_SOURCE_MAP_LIST, copytoSet);
    addKeyDefSetToProperties(prop, KEY_LIST, keysDefMap.values());

    try {
      logger.logInfo("Serializing job specification");
      prop.write();
    } catch (final IOException e) {
      throw new DITAOTException(
          "Failed to serialize job configuration files: " + e.getMessage(), e);
    }

    // Output relation-graph
    writeMapToXML(reader.getRelationshipGrap(), FILE_NAME_SUBJECT_RELATION);
    // Output topic-scheme dictionary
    writeMapToXML(this.schemeDictionary, FILE_NAME_SUBJECT_DICTIONARY);

    // added by Willam on 2009-07-17 for req #12014 start
    if (INDEX_TYPE_ECLIPSEHELP.equals(transtype)) {
      // Output plugin id
      final File pluginIdFile = new File(tempDir, FILE_NAME_PLUGIN_XML);
      final DelayConrefUtils delayConrefUtils = new DelayConrefUtils();
      delayConrefUtils.writeMapToXML(reader.getPluginMap(), pluginIdFile);
      // write the result into the file
      final StringBuffer result = reader.getResult();
      try {
        export.write(result.toString());
      } catch (final IOException e) {
        logger.logException(e);
      }
    }
    // added by Willam on 2009-07-17 for req #12014 end

  }