/** perform the actual compilation */
  private CompileResult compileJobs() {
    ArrayList<String> args = new ArrayList<String>();
    StringTokenizer tokenizer = new StringTokenizer(this.compilerArgumentsToUse);

    while (tokenizer.hasMoreTokens()) {
      args.add(tokenizer.nextToken());
    }

    Iterator<CompilationJob> job_it = this.jobs.iterator();

    this.handler.startAction("Compilation", this.jobs.size());

    // check whether compiler is valid (but only if there are jobs)
    if (job_it.hasNext()) {
      CompilationJob first_job = this.jobs.get(0);

      CompileResult check_result = first_job.checkCompiler(this.compilerToUse, args);
      if (!check_result.isContinue()) {
        return check_result;
      }
    }

    int job_no = 0;

    while (job_it.hasNext()) {
      CompilationJob job = job_it.next();

      this.handler.nextStep(job.getName(), job.getSize(), job_no++);

      CompileResult job_result = job.perform(this.compilerToUse, args);

      if (!job_result.isContinue()) {
        return job_result;
      }
    }

    Debug.trace("compilation finished.");
    return new CompileResult();
  }
    /**
     * Check whether the given compiler works.
     *
     * <p>This performs two steps:
     *
     * <ol>
     *   <li>check whether we can successfully call "compiler -help"
     *   <li>check whether we can successfully call "compiler -help arguments" (not all compilers
     *       return an error here)
     * </ol>
     *
     * <p>On failure, the method CompileHandler#errorCompile is called with a descriptive error
     * message.
     *
     * @param compiler the compiler to use
     * @param arguments additional arguments to pass to the compiler
     * @return false on error
     */
    public CompileResult checkCompiler(String compiler, ArrayList<String> arguments) {
      // don't do further checks for eclipse compiler - it would exit
      if (compiler.equalsIgnoreCase(ECLIPSE_COMPILER_NAME)) {
        return new CompileResult();
      }

      int retval = 0;
      FileExecutor executor = new FileExecutor();
      String[] output = new String[2];

      Debug.trace("checking whether \"" + compiler + " -help\" works");

      {
        List<String> args = new ArrayList<String>();
        args.add(compiler);
        args.add("-help");

        retval = runCompiler(executor, output, args);

        if (retval != 0) {
          CompileResult result =
              new CompileResult(
                  this.langpack.getString("CompilePanel.error.compilernotfound"),
                  args,
                  output[0],
                  output[1]);
          this.listener.handleCompileError(result);
          if (!result.isContinue()) {
            return result;
          }
        }
      }

      Debug.trace("checking whether \"" + compiler + " -help +arguments\" works");

      // used to collect the arguments for executing the compiler
      LinkedList<String> args = new LinkedList<String>(arguments);

      // add -help argument to prevent the compiler from doing anything
      args.add(0, "-help");

      // add compiler in front of arguments
      args.add(0, compiler);

      // construct classpath argument for compiler
      // - collect all classpaths
      StringBuffer classpath_sb = new StringBuffer();
      for (String cp : this.classpath) {
        if (classpath_sb.length() > 0) {
          classpath_sb.append(File.pathSeparatorChar);
        }
        classpath_sb.append(new File(cp).getAbsolutePath());
      }

      String classpath_str = classpath_sb.toString();

      // - add classpath argument to command line
      if (classpath_str.length() > 0) {
        args.add("-classpath");
        args.add(classpath_str);
      }

      retval = runCompiler(executor, output, args);

      if (retval != 0) {
        CompileResult result =
            new CompileResult(
                this.langpack.getString("CompilePanel.error.invalidarguments"),
                args,
                output[0],
                output[1]);
        this.listener.handleCompileError(result);
        if (!result.isContinue()) {
          return result;
        }
      }

      return new CompileResult();
    }
    /**
     * Perform this job - start compilation.
     *
     * @param compiler The compiler to use.
     * @param arguments The compiler arguments to use.
     * @return The result.
     */
    public CompileResult perform(String compiler, ArrayList<String> arguments) {
      Debug.trace("starting job " + this.name);
      // we have some maximum command line length - need to count
      int cmdline_len = 0;

      // used to collect the arguments for executing the compiler
      LinkedList<String> args = new LinkedList<String>(arguments);

      {
        for (String arg : args) {
          cmdline_len += (arg).length() + 1;
        }
      }

      boolean isEclipseCompiler = compiler.equalsIgnoreCase(ECLIPSE_COMPILER_NAME);

      // add compiler in front of arguments
      args.add(0, compiler);
      cmdline_len += compiler.length() + 1;

      // construct classpath argument for compiler
      // - collect all classpaths
      StringBuffer classpath_sb = new StringBuffer();
      for (String cp : this.classpath) {
        if (classpath_sb.length() > 0) {
          classpath_sb.append(File.pathSeparatorChar);
        }
        classpath_sb.append(new File(cp).getAbsolutePath());
      }

      String classpath_str = classpath_sb.toString();

      // - add classpath argument to command line
      if (classpath_str.length() > 0) {
        args.add("-classpath");
        cmdline_len += 11;
        args.add(classpath_str);
        cmdline_len += classpath_str.length() + 1;
      }

      // remember how many arguments we have which don't change for the
      // job
      int common_args_no = args.size();
      // remember how long the common command line is
      int common_args_len = cmdline_len;

      // used for execution
      FileExecutor executor = new FileExecutor();
      String output[] = new String[2];

      // used for displaying the progress bar
      String jobfiles = "";
      int fileno = 0;
      int last_fileno = 0;

      // now iterate over all files of this job

      for (File file : this.files) {
        String fpath = file.getAbsolutePath();

        Debug.trace("processing " + fpath);

        // we add the file _first_ to the arguments to have a better
        // chance to get something done if the command line is almost
        // MAX_CMDLINE_SIZE or even above
        fileno++;
        jobfiles += file.getName() + " ";
        args.add(fpath);
        cmdline_len += fpath.length();

        // start compilation if maximum command line length reached
        if (!isEclipseCompiler && cmdline_len >= MAX_CMDLINE_SIZE) {
          Debug.trace("compiling " + jobfiles);

          // display useful progress bar (avoid showing 100% while
          // still compiling a lot)
          this.listener.progress(last_fileno, jobfiles);
          last_fileno = fileno;

          int retval = runCompiler(executor, output, args);

          // update progress bar: compilation of fileno files done
          this.listener.progress(fileno, jobfiles);

          if (retval != 0) {
            CompileResult result =
                new CompileResult(
                    this.langpack.getString("CompilePanel.error"), args, output[0], output[1]);
            this.listener.handleCompileError(result);
            if (!result.isContinue()) {
              return result;
            }
          } else {
            // verify that all files have been compiled successfully
            // I found that sometimes, no error code is returned
            // although compilation failed.
            Iterator<String> arg_it = args.listIterator(common_args_no);
            while (arg_it.hasNext()) {
              File java_file = new File(arg_it.next());

              String basename = java_file.getName();
              int dotpos = basename.lastIndexOf('.');
              basename = basename.substring(0, dotpos) + ".class";
              File class_file = new File(java_file.getParentFile(), basename);

              if (!class_file.exists()) {
                CompileResult result =
                    new CompileResult(
                        this.langpack.getString("CompilePanel.error.noclassfile")
                            + java_file.getAbsolutePath(),
                        args,
                        output[0],
                        output[1]);
                this.listener.handleCompileError(result);
                if (!result.isContinue()) {
                  return result;
                }
                // don't continue any further
                break;
              }
            }
          }

          // clean command line: remove files we just compiled
          for (int i = args.size() - 1; i >= common_args_no; i--) {
            args.removeLast();
          }

          cmdline_len = common_args_len;
          jobfiles = "";
        }
      }

      if (cmdline_len > common_args_len) {
        this.listener.progress(last_fileno, jobfiles);

        int retval = runCompiler(executor, output, args);

        if (!isEclipseCompiler) {
          this.listener.progress(fileno, jobfiles);
        }

        if (retval != 0) {
          CompileResult result =
              new CompileResult(
                  this.langpack.getString("CompilePanel.error"), args, output[0], output[1]);
          this.listener.handleCompileError(result);
          if (!result.isContinue()) {
            return result;
          }
        } else {
          // verify that all files have been compiled successfully
          // I found that sometimes, no error code is returned
          // although compilation failed.
          Iterator<String> arg_it = args.listIterator(common_args_no);
          while (arg_it.hasNext()) {
            File java_file = new File(arg_it.next());

            String basename = java_file.getName();
            int dotpos = basename.lastIndexOf('.');
            basename = basename.substring(0, dotpos) + ".class";
            File class_file = new File(java_file.getParentFile(), basename);

            if (!class_file.exists()) {
              CompileResult result =
                  new CompileResult(
                      this.langpack.getString("CompilePanel.error.noclassfile")
                          + java_file.getAbsolutePath(),
                      args,
                      output[0],
                      output[1]);
              this.listener.handleCompileError(result);
              if (!result.isContinue()) {
                return result;
              }
              // don't continue any further
              break;
            }
          }
        }
      }

      Debug.trace("job " + this.name + " done (" + fileno + " files compiled)");

      return new CompileResult();
    }