瀏覽代碼

Add possibility to generate a report of the jplag result

Philipp Bauch 5 年之前
父節點
當前提交
ed7e486f8f
共有 38 個文件被更改,包括 2615 次插入47 次删除
  1. 2 0
      .gitignore
  2. 1 1
      jplag.frontend-utils/src/main/java/jplag/Token.java
  3. 8 5
      jplag/src/main/java/jplag/CLI.java
  4. 4 1
      jplag/src/main/java/jplag/JPlag.java
  5. 24 5
      jplag/src/main/java/jplag/JPlagComparison.java
  6. 13 0
      jplag/src/main/java/jplag/JPlagOptions.java
  7. 32 27
      jplag/src/main/java/jplag/JPlagResult.java
  8. 86 2
      jplag/src/main/java/jplag/Submission.java
  9. 48 0
      jplag/src/main/java/jplag/reporting/BufferedCounter.java
  10. 425 0
      jplag/src/main/java/jplag/reporting/Colors.java
  11. 38 0
      jplag/src/main/java/jplag/reporting/HTMLFile.java
  12. 24 0
      jplag/src/main/java/jplag/reporting/MarkupText.java
  13. 26 0
      jplag/src/main/java/jplag/reporting/Messages.java
  14. 981 0
      jplag/src/main/java/jplag/reporting/Report.java
  15. 37 0
      jplag/src/main/java/jplag/reporting/TagParser.java
  16. 1 3
      jplag/src/main/java/jplag/strategy/NormalComparisonStrategy.java
  17. 1 3
      jplag/src/main/java/jplag/strategy/RevisionComparisonStrategy.java
  18. 59 0
      jplag/src/main/resources/jplag/messages_de.properties
  19. 59 0
      jplag/src/main/resources/jplag/messages_en.properties
  20. 59 0
      jplag/src/main/resources/jplag/messages_es.properties
  21. 59 0
      jplag/src/main/resources/jplag/messages_fr.properties
  22. 59 0
      jplag/src/main/resources/jplag/messages_pt.properties
  23. 二進制
      jplag/src/main/resources/jplag/reporting/data/back.gif
  24. 8 0
      jplag/src/main/resources/jplag/reporting/data/fields.js
  25. 二進制
      jplag/src/main/resources/jplag/reporting/data/forward.gif
  26. 50 0
      jplag/src/main/resources/jplag/reporting/data/help-de.html
  27. 52 0
      jplag/src/main/resources/jplag/reporting/data/help-en.html
  28. 53 0
      jplag/src/main/resources/jplag/reporting/data/help-es.html
  29. 52 0
      jplag/src/main/resources/jplag/reporting/data/help-fr.html
  30. 52 0
      jplag/src/main/resources/jplag/reporting/data/help-pt.html
  31. 73 0
      jplag/src/main/resources/jplag/reporting/data/help-ptbr.html
  32. 38 0
      jplag/src/main/resources/jplag/reporting/data/help-sim-de.html
  33. 38 0
      jplag/src/main/resources/jplag/reporting/data/help-sim-en.html
  34. 39 0
      jplag/src/main/resources/jplag/reporting/data/help-sim-es.html
  35. 38 0
      jplag/src/main/resources/jplag/reporting/data/help-sim-fr.html
  36. 38 0
      jplag/src/main/resources/jplag/reporting/data/help-sim-pt.html
  37. 38 0
      jplag/src/main/resources/jplag/reporting/data/help-sim-ptbr.html
  38. 二進制
      jplag/src/main/resources/jplag/reporting/data/logo.gif

+ 2 - 0
.gitignore

@@ -5,6 +5,8 @@ target/
 
 *.class
 
+result
+
 # Mobile Tools for Java (J2ME)
 .mtj.tmp/
 

+ 1 - 1
jplag.frontend-utils/src/main/java/jplag/Token.java

@@ -34,7 +34,7 @@ abstract public class Token implements TokenConstants, Serializable {
 	
 	// this is made to distinguish the character front end.
 	// maybe other front ends can use it too?
-	protected int getIndex() { return -1; }
+  public int getIndex() { return -1; }
 	
 	public static String type2string(int type) {
 		return "<abstract>";

+ 8 - 5
jplag/src/main/java/jplag/CLI.java

@@ -1,13 +1,13 @@
 package jplag;
 
+import java.io.File;
 import jplag.options.LanguageOption;
+import jplag.reporting.Report;
 
 public class CLI {
 
   public static void main(String[] args) {
-
     try {
-
       JPlagOptions options = new JPlagOptions(
           "/Users/philippbauch/Develop/jplag-test",
           LanguageOption.JAVA_1_9
@@ -17,12 +17,15 @@ public class CLI {
       JPlag program = new JPlag(options);
 
       System.out.println("JPlag initialized");
-      JPlagResult run = program.run();
-      System.out.println(run);
+      JPlagResult result = program.run();
+
+      File reportDir = new File("result");
+      Report report = new Report(reportDir);
+
+      report.writeResult(result);
     } catch (ExitException ex) {
       System.out.println("Error: " + ex.getReport());
       System.exit(1);
     }
-
   }
 }

+ 4 - 1
jplag/src/main/java/jplag/JPlag.java

@@ -144,7 +144,10 @@ public class JPlag implements ProgramI {
       Constructor<?> constructor = languageConstructors[0];
       Object[] constructorParams = {this};
 
-      this.language = (Language) constructor.newInstance(constructorParams);
+      Language language = (Language) constructor.newInstance(constructorParams);
+
+      this.language = language;
+      this.options.setLanguage(language);
     } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
       e.printStackTrace();
 

+ 24 - 5
jplag/src/main/java/jplag/JPlagComparison.java

@@ -139,7 +139,8 @@ public class JPlagComparison implements Comparator<JPlagComparison> {
   public final float percentA() {
     int divisor;
     if (bcMatchesA != null) {
-      divisor = subA.getNumberOfTokens() - subA.files.size() - bcMatchesA.getNumberOfMatchedTokens();
+      divisor =
+          subA.getNumberOfTokens() - subA.files.size() - bcMatchesA.getNumberOfMatchedTokens();
     } else {
       divisor = subA.getNumberOfTokens() - subA.files.size();
     }
@@ -149,7 +150,8 @@ public class JPlagComparison implements Comparator<JPlagComparison> {
   public final float percentB() {
     int divisor;
     if (bcMatchesB != null) {
-      divisor = subB.getNumberOfTokens() - subB.files.size() - bcMatchesB.getNumberOfMatchedTokens();
+      divisor =
+          subB.getNumberOfTokens() - subB.files.size() - bcMatchesB.getNumberOfMatchedTokens();
     } else {
       divisor = subB.getNumberOfTokens() - subB.files.size();
     }
@@ -206,13 +208,18 @@ public class JPlagComparison implements Comparator<JPlagComparison> {
     return ((int) (percent * 10)) / (float) 10;
   }
 
-  /* This method returns all the files which contributed to a match.
-   * Parameter: j == 0   submission A,
-   *            j != 0   submission B.
+  /**
+   * This method returns all the files which contributed to a match.
+   * Parameter: j == 0 submission A, j != 0   submission B.
    */
   public final String[] files(int j) {
+    if (matches.size() == 0) {
+      return new String[]{};
+    }
+
     Token[] tokens = (j == 0 ? subA : subB).tokenList.tokens;
     int i, h, starti, starth, count = 1;
+
     o1:
     for (i = 1; i < matches.size(); i++) {
       starti = (j == 0 ? matches.get(i).startA : matches.get(i).startB);
@@ -224,9 +231,11 @@ public class JPlagComparison implements Comparator<JPlagComparison> {
       }
       count++;
     }
+
     String[] res = new String[count];
     res[0] = tokens[(j == 0 ? matches.get(0).startA : matches.get(0).startB)].file;
     count = 1;
+
     o2:
     for (i = 1; i < matches.size(); i++) {
       starti = (j == 0 ? matches.get(i).startA : matches.get(i).startB);
@@ -246,6 +255,16 @@ public class JPlagComparison implements Comparator<JPlagComparison> {
     return res;
   }
 
+  /**
+   * The bigger a match (length "anz") is relatively to the biggest match the redder is the color
+   * returned by this method.
+   */
+  public String color(int anz) {
+    int farbe = 255 * anz / biggestMatch();
+    String help = (farbe < 16 ? "0" : "") + Integer.toHexString(farbe);
+    return "#" + help + "0000";
+  }
+
   /* This method returns the name of all files that are represented by
    * at least one token. */
   public final String[] allFiles(int sub) {

+ 13 - 0
jplag/src/main/java/jplag/JPlagOptions.java

@@ -15,6 +15,11 @@ public class JPlagOptions {
    */
   public static final int MAX_RESULT_PAIRS = 1000;
 
+  /**
+   * Language used to parse the submissions.
+   */
+  private Language language;
+
   /**
    * Deprecated - use similarityThreshold instead!
    *
@@ -176,6 +181,10 @@ public class JPlagOptions {
     return storePercent;
   }
 
+  public Language getLanguage() {
+    return language;
+  }
+
   public Integer getMinTokenMatch() {
     return minTokenMatch;
   }
@@ -212,6 +221,10 @@ public class JPlagOptions {
     return similarityMetric;
   }
 
+  void setLanguage(Language language) {
+    this.language = language;
+  }
+
   public void setNumberOfSubmissionsToCompareTo(int numberOfSubmissionsToCompareTo) {
     this.numberOfSubmissionsToCompareTo = numberOfSubmissionsToCompareTo;
   }

+ 32 - 27
jplag/src/main/java/jplag/JPlagResult.java

@@ -1,50 +1,54 @@
 package jplag;
 
 import java.util.List;
-import jplag.clustering.Cluster;
 
 public class JPlagResult {
 
+  /**
+   * List of detected comparisons whose similarity was about the specified threshold.
+   */
+  private List<JPlagComparison> comparisons;
 
   /**
-   * 10-element array representing the similarity distribution of the detected matches.
-   * <p>
-   * Each entry represents the absolute frequency of matches whose similarity lies within the
-   * respective interval.
-   * <p>
-   * Intervals:
-   * <p>
-   * 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9: [90% - 100%]
+   * Duration of the JPlag run in milliseconds.
    */
-  private int[] similarityDistribution = null;
+  private long durationInMillis;
 
   /**
-   * Total number of comparisons. This number also takes into account the comparisons that were
-   * ignored due to their too low similarity.
+   * Total number of submissions that have been compared.
    */
-  private int totalNumberOfComparisons;
+  private int numberOfSubmissions;
 
   /**
-   * Duration of the JPlag run in milliseconds.
+   * Options for the plagiarism detection run.
    */
-  private long durationInMillis;
+  private JPlagOptions options;
 
   /**
-   * List of detected comparisons whose similarity was about the specified threshold.
+   * 10-element array representing the similarity distribution of the detected matches.
+   * <p>
+   * Each entry represents the absolute frequency of matches whose similarity lies within the
+   * respective interval.
+   * <p>
+   * Intervals:
+   * <p>
+   * 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9: [90% - 100%]
    */
-  private List<JPlagComparison> comparisons;
+  private int[] similarityDistribution = null;
 
   public JPlagResult() {
   }
 
   public JPlagResult(
       List<JPlagComparison> comparisons,
-      int totalNumberOfComparisons,
-      long durationInMillis
+      long durationInMillis,
+      int numberOfSubmissions,
+      JPlagOptions options
   ) {
     this.comparisons = comparisons;
     this.durationInMillis = durationInMillis;
-    this.totalNumberOfComparisons = totalNumberOfComparisons;
+    this.numberOfSubmissions = numberOfSubmissions;
+    this.options = options;
 
     this.similarityDistribution = calculateSimilarityDistribution(comparisons);
   }
@@ -74,12 +78,12 @@ public class JPlagResult {
     return durationInMillis;
   }
 
-  public int getTotalNumberOfComparisons() {
-    return totalNumberOfComparisons;
+  public JPlagOptions getOptions() {
+    return options;
   }
 
-  public int getNumberOfComparisons() {
-    return comparisons.size();
+  public int getNumberOfSubmissions() {
+    return numberOfSubmissions;
   }
 
   public int[] getSimilarityDistribution() {
@@ -89,10 +93,11 @@ public class JPlagResult {
   @Override
   public String toString() {
     return String.format(
-        "JPlagResult { duration: %d ms, totalComparisons: %d, detectedComparisons: %d }",
+        "JPlagResult { comparisons: %d, duration: %d ms, language: %s, submissions: %d }",
+        getComparisons().size(),
         getDuration(),
-        getTotalNumberOfComparisons(),
-        getNumberOfComparisons()
+        getOptions().getLanguageOption(),
+        getNumberOfSubmissions()
     );
   }
 }

+ 86 - 2
jplag/src/main/java/jplag/Submission.java

@@ -1,15 +1,21 @@
 package jplag;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Vector;
 import jplag.options.Verbosity;
 
 /**
@@ -18,7 +24,8 @@ import jplag.options.Verbosity;
 public class Submission implements Comparable<Submission> {
 
   /**
-   * Name that uniquely identifies this submission. Will most commonly be the directory or file name.
+   * Name that uniquely identifies this submission. Will most commonly be the directory or file
+   * name.
    */
   public String name;
 
@@ -32,7 +39,8 @@ public class Submission implements Comparable<Submission> {
   /**
    * List of tokens that have been parsed from the files this submission consists of.
    * <p>
-   * TODO: The name 'Structure' is very generic and should be changed to something more descriptive.
+   * TODO: The name 'Structure' is very generic and should be changed to something more
+   * descriptive.
    */
   public Structure tokenList;
 
@@ -157,6 +165,82 @@ public class Submission implements Comparable<Submission> {
     return false;
   }
 
+  /**
+   * Used by the "Report" class. All source files are returned as an array of an array of strings.
+   */
+  public String[][] readFiles(String[] files) throws jplag.ExitException {
+    String[][] result = new String[files.length][];
+    String help;
+    Vector<String> text = new Vector<>();
+
+    for (int i = 0; i < files.length; i++) {
+      text.removeAllElements();
+
+      try {
+        /* file encoding = "UTF-8" */
+        FileInputStream fileInputStream = new FileInputStream(new File(submissionFile, files[i]));
+        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,
+            StandardCharsets.UTF_8);
+        BufferedReader in = new BufferedReader(inputStreamReader);
+
+        while ((help = in.readLine()) != null) {
+          help = help.replaceAll("&", "&amp;");
+          help = help.replaceAll("<", "&lt;");
+          help = help.replaceAll(">", "&gt;");
+          help = help.replaceAll("\"", "&quot;");
+          text.addElement(help);
+        }
+
+        in.close();
+        inputStreamReader.close();
+        fileInputStream.close();
+      } catch (FileNotFoundException e) {
+        System.out.println("File not found: " + ((new File(submissionFile, files[i])).toString()));
+      } catch (IOException e) {
+        throw new jplag.ExitException("I/O exception!");
+      }
+
+      result[i] = new String[text.size()];
+      text.copyInto(result[i]);
+    }
+
+    return result;
+  }
+
+  /**
+   * Used by the "Report" class. All source files are returned as an array of an array of chars.
+   */
+  public char[][] readFilesChar(String[] files) throws jplag.ExitException {
+    char[][] result = new char[files.length][];
+
+    for (int i = 0; i < files.length; i++) {
+      try {
+        File file = new File(submissionFile, files[i]);
+        int size = (int) file.length();
+        char[] buffer = new char[size];
+
+        FileReader fis = new FileReader(file);
+
+        if (size != fis.read(buffer)) {
+          System.out
+              .println("Not right size read from the file, " + "but I will still continue...");
+        }
+
+        result[i] = buffer;
+        fis.close();
+      } catch (FileNotFoundException e) {
+        // TODO: Should an ExitException be thrown here?
+        System.out.println("File not found: " + ((new File(submissionFile, files[i])).toString()));
+      } catch (IOException e) {
+        throw new jplag.ExitException(
+            "I/O exception reading file \"" + (new File(submissionFile, files[i])).toString()
+                + "\"!", e);
+      }
+    }
+    
+    return result;
+  }
+
   /*
    * This method is used to copy files that can not be parsed to a special
    * folder: jplag/errors/java old_java scheme cpp /001/(...files...)

+ 48 - 0
jplag/src/main/java/jplag/reporting/BufferedCounter.java

@@ -0,0 +1,48 @@
+package jplag.reporting;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/* This class counts the number of printed characters.
+ */
+public class BufferedCounter extends BufferedWriter {
+
+  private int count;
+
+  public BufferedCounter(Writer out) {
+    super(out);
+    count = 0;
+  }
+
+  public BufferedCounter(Writer out, int sz) {
+    super(out, sz);
+    count = 0;
+  }
+
+  public void write(int c) throws IOException {
+    super.write(c);
+    count++;
+  }
+
+  public void write(char[] cbuf, int off, int len) throws IOException {
+    super.write(cbuf, off, len);
+    count += len;
+  }
+
+  public void write(String s, int off, int len) throws IOException {
+    super.write(s, off, len);
+    count += len;
+  }
+
+  public void newLine() throws IOException {
+    super.newLine();
+    count++;
+  }
+
+  public int bytesWritten() {
+    return count;
+  }
+}
+
+

+ 425 - 0
jplag/src/main/java/jplag/reporting/Colors.java

@@ -0,0 +1,425 @@
+package jplag.reporting;
+
+public class Colors {
+
+  static public final String[] colors = {
+
+      //"#eff7ff", "AliceBlue ",
+      //"#f9e8d2", "AntiqueWhite ",
+      //"#feedd6", "AntiqueWhite1 ",
+      //"#ebdbc5", "AntiqueWhite2 ",
+      //"#c8b9a6", "AntiqueWhite3 ",
+      //"#817468", "AntiqueWhite4 ",
+      ///"#43b7ba", "Aquamarine ",
+      //"#87fdce", "aquamarine1 ",
+      //"#7deabe", "aquamarine2 ",
+      //"#69c69f", "aquamarine3 ",
+      //#417c64", "aquamarine4 ",
+      //"#efffff", "azure ",
+      //"#deecec", "azure2 ",
+      //"#bcc7c7", "azure3 ",
+      //"#7a7d7d", "azure4 ",
+      //"#f5f3d7", "beige ",
+      //"#fde0bc", "bisque ",
+      //"#ead0ae", "bisque2 ",
+      //"#c7af92", "bisque3 ",
+      //"#816e59", "bisque4 ",
+      //"#000000", "Black ",
+      //"#fee8c6", "BlanchedAlmond ",
+      "#0000ff", "Blue ",
+      //"#1535ff", "blue1 ",
+      //"#1531ec", "blue2 ",
+      //"#1528c7", "blue3 ",
+      //"#151b7e", "blue4 ",
+      //"#7931df", "BlueViolet ",
+      "#f63526", "brown1 ",
+      //"#e42d17", "brown2 ",
+      //"#c22217", "brown3 ",
+      //"#fcce8e", "burlywood1 ",
+      //"#eabe83", "burlywood2 ",
+      //"#c6a06d", "burlywood3 ",
+      //"#806341", "burlywood4 ",
+      //"#578693", "CadetBlue ",
+      //"#99f3ff", "CadetBlue1 ",
+      //"#8ee2ec", "CadetBlue2 ",
+      "#77bfc7", "CadetBlue3 ",
+      //"#4c787e", "CadetBlue4 ",
+      //"#8afb17", "chartreuse ",
+      //"#7fe817", "chartreuse2 ",
+      "#6cc417", "chartreuse3 ",
+      //"#437c17", "chartreuse4 ",
+      //"#c85a17", "chocolate ",
+      //"#f76541", "Coral ",
+      //"#e55b3c", "coral2 ",
+      //"#c34a2c", "coral3 ",
+      //"#7e2817", "coral4 ",
+      "#151b8d", "CornflowerBlue ",
+      //"#fff7d7", "cornsilk ",
+      //"#ece5c6", "cornsilk2 ",
+      "#c8c2a7", "cornsilk3 ",
+      //"#817a68", "cornsilk4 ",
+      //"#00ffff", "Cyan ",
+      //"#57feff", "cyan1 ",
+      "#50ebec", "cyan2 ",
+      //"#46c7c7", "cyan3 ",
+      //"#307d7e", "cyan4 ",
+      //"#af7817", "DarkGoldenrod ",
+      //"#fbb117", "DarkGoldenrod1 ",
+      //"#e8a317", "DarkGoldenrod2 ",
+      "#c58917", "DarkGoldenrod3 ",
+      //"#7f5217", "DarkGoldenrod4 ",
+      //"#254117", "DarkGreen ",
+      //"#b7ad59", "DarkKhaki ",
+      //"#4a4117", "DarkOliveGreen ",
+      //"#ccfb5d", "DarkOliveGreen1 ",
+      "#bce954", "DarkOliveGreen2 ",
+      //"#a0c544", "DarkOliveGreen3 ",
+      //"#667c26", "DarkOliveGreen4 ",
+      "#f88017", "DarkOrange ",
+      //"#f87217", "DarkOrange1 ",
+      //"#e56717", "DarkOrange2 ",
+      //"#c35617", "DarkOrange3 ",
+      //"#7e3117", "DarkOrange4 ",
+      //"#7d1b7e", "DarkOrchid ",
+      "#b041ff", "DarkOrchid1 ",
+      //"#a23bec", "DarkOrchid2 ",
+      //"#8b31c7", "DarkOrchid3 ",
+      "#571b7e", "DarkOrchid4 ",
+      //"#e18b6b", "DarkSalmon ",
+      //"#8bb381", "DarkSeaGreen ",
+      //"#c3fdb8", "DarkSeaGreen1 ",
+      //"#b5eaaa", "DarkSeaGreen2 ",
+      //"#99c68e", "DarkSeaGreen3 ",
+      //"#617c58", "DarkSeaGreen4 ",
+      //"#2b3856", "DarkSlateBlue ",
+      //"#25383c", "DarkSlateGray ",
+      //"#9afeff", "DarkSlateGray1 ",
+      //"#8eebec", "DarkSlateGray2 ",
+      //"#78c7c7", "DarkSlateGray3 ",
+      //"#4c7d7e", "DarkSlateGray4 ",
+      "#3b9c9c", "DarkTurquoise ",
+      "#842dce", "DarkViolet ",
+      "#f52887", "DeepPink ",
+      //"#e4287c", "DeepPink2 ",
+      //"#c12267", "DeepPink3 ",
+      //"#7d053f", "DeepPink4 ",
+      "#3bb9ff", "DeepSkyBlue ",
+      //"#38acec", "DeepSkyBlue2 ",
+      "#3090c7", "DeepSkyBlue3 ",
+      //"#25587e", "DeepSkyBlue4 ",
+      //"#463e41", "DimGray ",
+      //"#1589ff", "DodgerBlue ",
+      //"#157dec", "DodgerBlue2 ",
+      //"#1569c7", "DodgerBlue3 ",
+      //"#153e7e", "DodgerBlue4 ",
+      "#800517", "Firebrick ",
+      "#f62817", "firebrick1 ",
+      //"#e42217", "firebrick2 ",
+      //"#c11b17", "firebrick3 ",
+      //"#fff9ee", "FloralWhite ",
+      "#4e9258", "ForestGreen ",
+      //"#d8d9d7", "gainsboro ",
+      //"#f7f7ff", "GhostWhite ",
+      "#d4a017", "Gold ",
+      //"#fdd017", "gold1 ",
+      //"#eac117", "gold2 ",
+      //"#c7a317", "gold3 ",
+      //"#806517", "gold4 ",
+      //"#edda74", "Goldenrod ",
+      //"#fbb917", "goldenrod1 ",
+      //"#e9ab17", "goldenrod2 ",
+      //"#c68e17", "goldenrod3 ",
+      //"#805817", "goldenrod4 ",
+      "#00ff00", "Green ",
+      //"#5ffb17", "green1 ",
+      //"#59e817", "green2 ",
+      "#4cc417", "green3 ",
+      //"#347c17", "green4 ",
+      //"#b1fb17", "GreenYellow ",
+      //"#f0feee", "honeydew ",
+      //"#deebdc", "honeydew2 ",
+      //"#bcc7b9", "honeydew3 ",
+      //"#7a7d74", "honeydew4 ",
+      "#f660ab", "HotPink ",
+      //"#f665ab", "HotPink1 ",
+      //"#e45e9d", "HotPink2 ",
+      //"#c25283", "HotPink3 ",
+      //"#7d2252", "HotPink4 ",
+      //"#5e2217", "IndianRed ",
+      //"#f75d59", "IndianRed1 ",
+      //"#e55451", "IndianRed2 ",
+      //"#c24641", "IndianRed3 ",
+      //"#7e2217", "IndianRed4 ",
+      //"#ffffee", "ivory ",
+      //"#ececdc", "ivory2 ",
+      //"#c9c7b9", "ivory3 ",
+      //"#817d74", "ivory4 ",
+      "#ada96e", "Khaki ",
+      //"#fff380", "khaki1 ",
+      //"#ede275", "khaki2 ",
+      //"#c9be62", "khaki3 ",
+      //"#827839", "khaki4 ",
+      //"#e3e4fa", "lavender ",
+      //"#fdeef4", "LavenderBlush ",
+      //"#ebdde2", "LavenderBlush2 ",
+      //"#c8bbbe", "LavenderBlush3 ",
+      //"#817679", "LavenderBlush4 ",
+      "#87f717", "LawnGreen ",
+      //"#fff8c6", "LemonChiffon ",
+      //"#ece5b6", "LemonChiffon2 ",
+      //"#c9c299", "LemonChiffon3 ",
+      //"#827b60", "LemonChiffon4 ",
+      //"#addfff", "LightBlue ",
+      //"#bdedff", "LightBlue1 ",
+      //"#afdcec", "LightBlue2 ",
+      "#95b9c7", "LightBlue3 ",
+      //"#5e767e", "LightBlue4 ",
+      "#e77471", "LightCoral ",
+      //"#e0ffff", "LightCyan ",
+      //"#cfecec", "LightCyan2 ",
+      //"#afc7c7", "LightCyan3 ",
+      "#717d7d", "LightCyan4 ",
+      //"#ecd872", "LightGoldenrod ",
+      //"#ffe87c", "LightGoldenrod1 ",
+      //"#ecd672", "LightGoldenrod2 ",
+      //"#c8b560", "LightGoldenrod3 ",
+      //"#817339", "LightGoldenrod4 ",
+      //"#faf8cc", "LightGoldenrodYellow ",
+      "#faafba", "LightPink ",
+      //"#f9a7b0", "LightPink1 ",
+      //"#e799a3", "LightPink2 ",
+      //"#c48189", "LightPink3 ",
+      //"#7f4e52", "LightPink4 ",
+      "#f9966b", "LightSalmon ",
+      //"#e78a61", "LightSalmon2 ",
+      //"#c47451", "LightSalmon3 ",
+      //"#7f462c", "LightSalmon4 ",
+      "#3ea99f", "LightSeaGreen ",
+      "#82cafa", "LightSkyBlue ",
+      //"#a0cfec", "LightSkyBlue2 ",
+      //"#87afc7", "LightSkyBlue3 ",
+      //"#566d7e", "LightSkyBlue4 ",
+      "#736aff", "LightSlateBlue ",
+      //"#6d7b8d", "LightSlateGray ",
+      //"#728fce", "LightSteelBlue ",
+      //"#c6deff", "LightSteelBlue1 ",
+      //"#b7ceec", "LightSteelBlue2 ",
+      //"#9aadc7", "LightSteelBlue3 ",
+      //"#646d7e", "LightSteelBlue4 ",
+      //"#fffedc", "LightYellow ",
+      //"#edebcb", "LightYellow2 ",
+      //"#c9c7aa", "LightYellow3 ",
+      "#827d6b", "LightYellow4 ",
+      "#41a317", "LimeGreen ",
+      //"#f9eee2", "linen ",
+      "#ff00ff", "Magenta ",
+      //"#f43eff", "magenta1 ",
+      //"#e238ec", "magenta2 ",
+      //"#c031c7", "magenta3 ",
+      "#810541", "Maroon ",
+      //"#f535aa", "maroon1 ",
+      //"#e3319d", "maroon2 ",
+      //"#c12283", "maroon3 ",
+      //"#7d0552", "maroon4 ",
+      "#348781", "MediumAquamarine ",
+      "#152dc6", "MediumBlue ",
+      "#347235", "MediumForestGreen ",
+      //"#ccb954", "MediumGoldenrod ",
+      //"#b048b5", "MediumOrchid ",
+      //"#d462ff", "MediumOrchid1 ",
+      //"#c45aec", "MediumOrchid2 ",
+      //"#a74ac7", "MediumOrchid3 ",
+      //"#6a287e", "MediumOrchid4 ",
+      //"#8467d7", "MediumPurple ",
+      //"#9e7bff", "MediumPurple1 ",
+      //"#9172ec", "MediumPurple2 ",
+      //"#7a5dc7", "MediumPurple3 ",
+      //"#4e387e", "MediumPurple4 ",
+      //"#306754", "MediumSeaGreen ",
+      //"#5e5a80", "MediumSlateBlue ",
+      //"#348017", "MediumSpringGreen ",
+      //"#48cccd", "MediumTurquoise ",
+      //"#ca226b", "MediumVioletRed ",
+      //"#151b54", "MidnightBlue ",
+      //"#f5fff9", "MintCream ",
+      //"#fde1dd", "MistyRose ",
+      //"#ead0cc", "MistyRose2 ",
+      //"#c6afac", "MistyRose3 ",
+      //"#806f6c", "MistyRose4 ",
+      //"#fde0ac", "moccasin ",
+      //"#fddaa3", "NavajoWhite ",
+      //"#eac995", "NavajoWhite2 ",
+      //"#c7aa7d", "NavajoWhite3 ",
+      //"#806a4b", "NavajoWhite4 ",
+      //"#150567", "Navy ",
+      //"#fcf3e2", "OldLace ",
+      //"#658017", "OliveDrab ",
+      //"#c3fb17", "OliveDrab1 ",
+      //"#b5e917", "OliveDrab2 ",
+      //"#99c517", "OliveDrab3 ",
+      //"#617c17", "OliveDrab4 ",
+      "#f87a17", "Orange ",
+      //"#fa9b17", "orange1 ",
+      //"#e78e17", "orange2 ",
+      "#c57717", "orange3 ",
+      //"#7f4817", "orange4 ",
+      //"#f63817", "OrangeRed ",
+      //"#e43117", "OrangeRed2 ",
+      "#c22817", "OrangeRed3 ",
+      //"#7e0517", "OrangeRed4 ",
+      "#e57ded", "Orchid ",
+      //"#f67dfa", "orchid1 ",
+      //"#e473e7", "orchid2 ",
+      //"#c160c3", "orchid3 ",
+      //"#7d387c", "orchid4 ",
+      //"#ede49e", "PaleGoldenrod ",
+      "#79d867", "PaleGreen ",
+      //"#a0fc8d", "PaleGreen1 ",
+      //"#94e981", "PaleGreen2 ",
+      //"#7dc56c", "PaleGreen3 ",
+      //"#4e7c41", "PaleGreen4 ",
+      //"#aeebec", "PaleTurquoise ",
+      //"#bcfeff", "PaleTurquoise1 ",
+      //"#adebec", "PaleTurquoise2 ",
+      "#92c7c7", "PaleTurquoise3 ",
+      //"#5e7d7e", "PaleTurquoise4 ",
+      "#d16587", "PaleVioletRed ",
+      //"#f778a1", "PaleVioletRed1 ",
+      //"#e56e94", "PaleVioletRed2 ",
+      //"#c25a7c", "PaleVioletRed3 ",
+      //"#7e354d", "PaleVioletRed4 ",
+      //"#feeccf", "PapayaWhip ",
+      //"#fcd5b0", "PeachPuff ",
+      //"#eac5a3", "PeachPuff2 ",
+      //"#c6a688", "PeachPuff3 ",
+      //"#806752", "PeachPuff4 ",
+      "#c57726", "peru ",
+      //"#faafbe", "Pink ",
+      //"#e7a1b0", "pink2 ",
+      //"#c48793", "pink3 ",
+      //"#7f525d", "pink4 ",
+      //"#b93b8f", "Plum ",
+      //"#f9b7ff", "plum1 ",
+      //"#e6a9ec", "plum2 ",
+      //"#c38ec7", "plum3 ",
+      //"#7e587e", "plum4 ",
+      //"#addce3", "PowderBlue ",
+      "#8e35ef", "purple ",
+      //"#893bff", "purple1 ",
+      //"#7f38ec", "purple2 ",
+      //"#6c2dc7", "purple3 ",
+      //"#461b7e", "purple4 ",
+      "#ff0000", "Red ",
+      //"#f62217", "red1 ",
+      //"#e41b17", "red2 ",
+      "#b38481", "RosyBrown ",
+      //"#fbbbb9", "RosyBrown1 ",
+      //"#e8adaa", "RosyBrown2 ",
+      //"#c5908e", "RosyBrown3 ",
+      //"#7f5a58", "RosyBrown4 ",
+      "#2b60de", "RoyalBlue ",
+      //"#306eff", "RoyalBlue1 ",
+      //"#2b65ec", "RoyalBlue2 ",
+      //"#2554c7", "RoyalBlue3 ",
+      //"#15317e", "RoyalBlue4 ",
+      "#f88158", "salmon1 ",
+      //"#e67451", "salmon2 ",
+      //"#c36241", "salmon3 ",
+      //"#7e3817", "salmon4 ",
+      //"#ee9a4d", "SandyBrown ",
+      "#4e8975", "SeaGreen ",
+      //"#6afb92", "SeaGreen1 ",
+      //"#64e986", "SeaGreen2 ",
+      //"#54c571", "SeaGreen3 ",
+      //"#387c44", "SeaGreen4 ",
+      //"#fef3eb", "seashell ",
+      //"#ebe2d9", "seashell2 ",
+      //"#c8bfb6", "seashell3 ",
+      //"#817873", "seashell4 ",
+      //"#8a4117", "Sienna ",
+      //"#f87431", "sienna1 ",
+      //"#e66c2c", "sienna2 ",
+      //"#c35817", "sienna3 ",
+      //"#7e3517", "sienna4 ",
+      //"#6698ff", "SkyBlue ",
+      //"#82caff", "SkyBlue1 ",
+      //"#79baec", "SkyBlue2 ",
+      //"#659ec7", "SkyBlue3 ",
+      //"#41627e", "SkyBlue4 ",
+      //"#737ca1", "SlateBlue ",
+      //"#7369ff", "SlateBlue1 ",
+      //"#6960ec", "SlateBlue2 ",
+      //"#574ec7", "SlateBlue3 ",
+      //"#342d7e", "SlateBlue4 ",
+      //"#657383", "SlateGray ",
+      //"#c2dfff", "SlateGray1 ",
+      //"#b4cfec", "SlateGray2 ",
+      //"#98afc7", "SlateGray3 ",
+      //"#616d7e", "SlateGray4 ",
+      //"#fff9fa", "snow ",
+      //"#ece7e6", "snow2 ",
+      //"#c8c4c2", "snow3 ",
+      //"#817c7b", "snow4 ",
+      //"#4aa02c", "SpringGreen ",
+      //"#5efb6e", "SpringGreen1 ",
+      //"#57e964", "SpringGreen2 ",
+      //"#4cc552", "SpringGreen3 ",
+      //"#347c2c", "SpringGreen4 ",
+      "#4863a0", "SteelBlue ",
+      //"#5cb3ff", "SteelBlue1 ",
+      //"#56a5ec", "SteelBlue2 ",
+      //"#488ac7", "SteelBlue3 ",
+      //"#2b547e", "SteelBlue4 ",
+      //"#d8af79", "Tan ",
+      //"#fa9b3c", "tan1 ",
+      //"#e78e35", "tan2 ",
+      //"#d2b9d3", "Thistle ",
+      //"#fcdfff", "thistle1 ",
+      //"#e9cfec", "thistle2 ",
+      //"#c6aec7", "thistle3 ",
+      //"#806d7e", "thistle4 ",
+      //"#f75431", "tomato ",
+      //"#e54c2c", "tomato2 ",
+      //"#c23e17", "tomato3 ",
+      //"#43c6db", "Turquoise ",
+      //"#52f3ff", "turquoise1 ",
+      //"#4ee2ec", "turquoise2 ",
+      //"#43bfc7", "turquoise3 ",
+      //"#30787e", "turquoise4 ",
+      //"#8d38c9", "Violet ",
+      //"#e9358a", "VioletRed ",
+      //"#f6358a", "VioletRed1 ",
+      //"#e4317f", "VioletRed2 ",
+      //"#c12869", "VioletRed3 ",
+      //"#7d0541", "VioletRed4 ",
+      //"#f3daa9", "Wheat ",
+      //"#fee4b1", "wheat1 ",
+      //"#ebd3a3", "wheat2 ",
+      //"#c8b189", "wheat3 ",
+      //"#816f54", "wheat4 ",
+      //"#ffff00", "Yellow ",
+      //"#fffc17", "yellow1 ",
+      "#52d017", "YellowGreen ",
+
+      "#980517", "Brown ",
+
+  };
+
+  //"#c00000","#00a000","#0000d0", "#b0b020","#b000b0","#00a0a0", "#b06060","#70a070","#7070d0","#b0b070","#b070b0","#70a0a0"};
+
+  public static String getColor(int i) {
+    return colors[(i % (colors.length / 2)) * 2];
+  }
+
+  public static void main(String[] args) {
+    System.out.println("<!DOCTYPE HTML PUBLIC \"-//TEST//DTD HTML 3.2//EN\">" +
+        "<HTML>\n<HEAD>\n <TITLE>Test</TITLE></HEAD>\n" +
+        "<BODY BGCOLOR=\"#ffffff\">");
+    for (int i = 0; i < colors.length; i += 2) {
+      System.out.println("TEST TEST TEST <font color=\"" + colors[i] + "\">" +
+          "Das ist die Farbe #" + colors[i + 1] + "#</font><br>");
+    }
+
+    System.out.println("</BODY></HTML>");
+  }
+}

+ 38 - 0
jplag/src/main/java/jplag/reporting/HTMLFile.java

@@ -0,0 +1,38 @@
+package jplag.reporting;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+
+public class HTMLFile extends PrintWriter {
+
+  private BufferedCounter bc;
+
+  /**
+   * Static factory method to instantiate an HTMLFile objects.
+   */
+  public static HTMLFile fromFile(File file) throws IOException {
+    BufferedCounter bc = new BufferedCounter(new OutputStreamWriter(
+        new FileOutputStream(file),
+        StandardCharsets.UTF_8
+    ));
+
+    HTMLFile htmlFile = new HTMLFile(bc);
+    htmlFile.bc = bc;
+
+    return htmlFile;
+  }
+
+  private HTMLFile(BufferedWriter writer) {
+    super(writer);
+  }
+
+  public int bytesWritten() {
+    return bc.bytesWritten();
+  }
+}
+

+ 24 - 0
jplag/src/main/java/jplag/reporting/MarkupText.java

@@ -0,0 +1,24 @@
+package jplag.reporting;
+
+/**
+ * This class represents one markup tag that will be included in the text. It is
+ * necessary to sort the objects before they are included into the text, so that
+ * the original position can be found.
+ */
+public class MarkupText {
+  public int fileIndex, lineIndex, column;
+  public String text;
+  public boolean frontMarkup = false;
+
+  public MarkupText(int fileIndex, int lineIndex, int column, String text, boolean frontMarkup) {
+    this.fileIndex = fileIndex;
+    this.lineIndex = lineIndex;
+    this.column = column;
+    this.text = text;
+    this.frontMarkup = frontMarkup;
+  }
+
+  public String toString() {
+    return "MarkUp - file: " + fileIndex + " line: " + lineIndex + " column: " + column + " text: " + text;
+  }
+}

+ 26 - 0
jplag/src/main/java/jplag/reporting/Messages.java

@@ -0,0 +1,26 @@
+package jplag.reporting;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class Messages {
+
+  private final ResourceBundle resourceBundle;
+
+  /**
+   * @param countryTag may be "de", "en", "fr", "es", "pt" or "ptbr"
+   */
+  public Messages(String countryTag) {
+    String bundleName = "jplag.messages";
+    resourceBundle = ResourceBundle.getBundle(bundleName, new Locale(countryTag));
+  }
+
+  public String getString(String key) {
+    try {
+      return resourceBundle.getString(key);
+    } catch (MissingResourceException e) {
+      return '!' + key + '!';
+    }
+  }
+}

+ 981 - 0
jplag/src/main/java/jplag/reporting/Report.java

@@ -0,0 +1,981 @@
+package jplag.reporting;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Vector;
+import jplag.ExitException;
+import jplag.JPlagBaseCodeComparison;
+import jplag.JPlagComparison;
+import jplag.JPlagResult;
+import jplag.Match;
+import jplag.Submission;
+import jplag.Token;
+
+/**
+ * This class writes all the HTML pages
+ */
+public class Report {
+
+  private JPlagResult result;
+  private final File reportDir;
+  private final Messages msg;
+
+  int currentComparisonIndex = 0;
+  Map<JPlagComparison, Integer> comparisonToIndex = new HashMap<>();
+
+  public Report(File reportDir) throws ExitException {
+    this.reportDir = reportDir;
+    this.msg = new Messages("en");
+
+    validateReportDir();
+  }
+
+  /**
+   * Make sure the report directory exists, is a directory and has write access.
+   */
+  private void validateReportDir() throws ExitException {
+    if (!reportDir.exists() && !reportDir.mkdirs()) {
+      throw new ExitException("Cannot create report directory!");
+    }
+
+    if (!reportDir.isDirectory()) {
+      throw new ExitException(reportDir + " is not a directory!");
+    }
+
+    if (!reportDir.canWrite()) {
+      throw new ExitException("Cannot write directory: " + reportDir);
+    }
+  }
+
+  /*
+   * Two colors, represented by Rl,Gl,Bl and Rh,Gh,Bh respectively are mixed
+   * according to the percentage "percent"
+   */
+  private String color(float percent, int Rl, int Rh, int Gl, int Gh, int Bl, int Bh) {
+    int farbeR = (int) (Rl + (Rh - Rl) * percent / 100);
+    int farbeG = (int) (Gl + (Gh - Gl) * percent / 100);
+    int farbeB = (int) (Bl + (Bh - Bl) * percent / 100);
+
+    String helpR = (farbeR < 16 ? "0" : "") + Integer.toHexString(farbeR);
+    String helpG = (farbeG < 16 ? "0" : "") + Integer.toHexString(farbeG);
+    String helpB = (farbeB < 16 ? "0" : "") + Integer.toHexString(farbeB);
+
+    return "#" + helpR + helpG + helpB;
+  }
+
+  /*
+   * This procedure copies all the data from "data/" into the
+   * result-directory.
+   */
+  private void copyStaticFiles() {
+    final String[] fileList = {
+        "back.gif", "forward.gif", "help-en.html", "help-sim-en.html", "logo.gif", "fields.js"
+    };
+
+    for (int i = fileList.length - 1; i >= 0; i--) {
+      try {
+        URL url = Report.class.getResource("data/" + fileList[i]);
+        DataInputStream dis = new DataInputStream(url.openStream());
+
+        File dest = new File(reportDir, fileList[i]);
+        DataOutputStream dos = new DataOutputStream(new FileOutputStream(dest));
+
+        byte[] buffer = new byte[1024];
+        int count;
+        do {
+          count = dis.read(buffer);
+          if (count != -1) {
+            dos.write(buffer, 0, count);
+          }
+        } while (count != -1);
+        dis.close();
+        dos.close();
+      } catch (IOException | NullPointerException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  private int getComparisonIndex(JPlagComparison comparison) {
+    Integer index = comparisonToIndex.get(comparison);
+
+    if (index != null) {
+      return index;
+    }
+
+    comparisonToIndex.put(comparison, currentComparisonIndex++);
+
+    return currentComparisonIndex - 1;
+  }
+
+  public void writeResult(JPlagResult result) throws ExitException {
+    this.result = result;
+
+    writeIndex();
+
+    // TODO:
+    // if (result.getOptions().getClusterType() != ClusterType.NONE) {
+    //   writeClusters(clustering);
+    // }
+
+    copyStaticFiles();
+
+    writeMatches(result.getComparisons());
+  }
+
+  /**
+   * Create a new HTML file.
+   */
+  private HTMLFile createHTMLFile(String name) throws ExitException {
+    File file = new File(reportDir, name);
+
+    try {
+      return HTMLFile.fromFile(file);
+    } catch (IOException e) {
+      throw new jplag.ExitException("Error opening file: " + file);
+    }
+  }
+
+  private void writeHTMLHeader(HTMLFile file, String title) {
+    file.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
+    file.println("<HTML><HEAD><TITLE>" + title + "</TITLE>");
+    file.println("<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
+    file.println("</HEAD>");
+  }
+
+  private void writeHTMLHeaderWithScript(HTMLFile file, String title) {
+    file.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
+    file.println("<HTML>\n<HEAD>\n <TITLE>" + title + "</TITLE>");
+    file.println("<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
+    file.println("  <script type=\"text/javascript\">\n  <!--");
+    file.println("   function ZweiFrames(URL1,F1,URL2,F2)\n   {");
+    file.println("    parent.frames[F1].location.href=URL1;");
+    file.println("    parent.frames[F2].location.href=URL2;\n   }\n  //-->");
+    file.println("  </script>\n</HEAD>");
+  }
+
+  /**
+   * Write the index.html file.
+   */
+  private void writeIndex() throws ExitException {
+    HTMLFile htmlFile = createHTMLFile("index.html");
+
+    writeIndexBegin(htmlFile, msg.getString("Report.Search_Results"));
+    writeDistribution(htmlFile);
+
+    String csvFile = "matches_avg.csv";
+
+    writeLinksToComparisons(
+        htmlFile,
+        "<H4>" + msg.getString("Report.MatchesAvg"),
+        csvFile
+    );
+
+    writeMatchesCSV(csvFile);
+
+    writeIndexEnd(htmlFile);
+
+    htmlFile.close();
+  }
+
+  /**
+   * Write the beginning of the index.html file.
+   */
+  private void writeIndexBegin(HTMLFile htmlFile, String title) {
+    writeHTMLHeader(htmlFile, title);
+
+    htmlFile.println("<BODY BGCOLOR=#ffffff LINK=#000088 VLINK=#000000 TEXT=#000000>");
+    htmlFile.println("<TABLE ALIGN=center CELLPADDING=2 CELLSPACING=1>");
+    htmlFile.println("<TR VALIGN=middle ALIGN=center BGCOLOR=#ffffff><TD>"
+        + "<IMG SRC=\"logo.gif\" ALT=\"JPlag\" BORDER=0></TD>");
+    htmlFile.println("<TD><H1><BIG>" + title + "</BIG></H1></TD></TR>");
+
+    htmlFile.println(
+        "<TR BGCOLOR=#aaaaff VALIGN=top><TD>" + msg.getString("Report.Language") + ":</TD><TD>"
+            + result.getOptions().getLanguageOption().name()
+            + "</TD></TR>");
+    htmlFile.print(
+        "<TR BGCOLOR=#aaaaff VALIGN=top><TD>" + msg.getString("Report.Submissions") + ":</TD><TD>"
+            + result.getNumberOfSubmissions());
+
+    htmlFile.println("</TD></TR>");
+
+    if (result.getOptions().hasBaseCode()) {
+      htmlFile.print(
+          "<TR BGCOLOR=#aaaaff VALIGN=top><TD>" + msg.getString("Report.Basecode_submission")
+              + ":</TD>" + "<TD>"
+              + result.getOptions().getBaseCodeSubmissionName() + "</TD></TR>");
+    }
+
+    if (result.getComparisons().size() > 0) {
+      htmlFile.println(
+          "<TR BGCOLOR=#aaaaff VALIGN=top><TD>" + msg.getString("Report.Matches_displayed")
+              + ":</TD>" + "<TD>");
+
+      htmlFile.println(
+          result.getComparisons().size() + " (" + msg.getString("Report.Treshold") + ": "
+              + result.getOptions().getSimilarityThreshold() + "%)<br>");
+
+      htmlFile.println("</TD></TR>");
+    }
+
+    SimpleDateFormat dateFormat = new SimpleDateFormat();
+
+    htmlFile.println(
+        "<TR BGCOLOR=#aaaaff VALIGN=top><TD>" + msg.getString("Report.Date") + ":</TD><TD>"
+            + dateFormat.format(new Date()) + "</TD></TR>");
+    htmlFile.println(
+        "<TR BGCOLOR=#aaaaff>" + "<TD><EM>" + msg.getString("Report.Minimum_Match_Length")
+            + "</EM> ("
+            + msg.getString("Report.sensitivity") + "):</TD><TD>" + result.getOptions()
+            .getMinTokenMatch()
+            + "</TD></TR>");
+    htmlFile.println(
+        "<TR BGCOLOR=#aaaaff VALIGN=top><TD>" + msg.getString("Report.Suffixes") + ":</TD><TD>");
+
+    String[] fileSuffixes = result.getOptions().getFileSuffixes();
+
+    for (int i = 0; i < fileSuffixes.length; i++) {
+      htmlFile.print(fileSuffixes[i] + (i < fileSuffixes.length - 1 ? ", " : "</TD></TR>\n"));
+    }
+
+    htmlFile.println("</TABLE>\n<HR>");
+  }
+
+  /**
+   * Write the end of the index.html file.
+   */
+  private void writeIndexEnd(HTMLFile htmlFile) {
+    htmlFile.println(
+        "<HR>\n<P ALIGN=right><FONT SIZE=\"1\" FACE=\"helvetica\">JPlag</FONT></P>");
+    htmlFile.println("</BODY>\n</HTML>");
+  }
+
+  private void writeDistribution(HTMLFile htmlFile) {
+    int barLength = 75;
+    int[] similarityDistribution = result.getSimilarityDistribution();
+
+    int max = 0;
+
+    for (int i = 0; i < 10; i++) {
+      if (similarityDistribution[i] > max) {
+        max = similarityDistribution[i];
+      }
+    }
+
+    htmlFile.println("<H4>" + this.msg.getString("Report.Distribution") + ":</H4>\n<CENTER>");
+    htmlFile.println("<TABLE CELLPADDING=1 CELLSPACING=1>");
+
+    for (int i = 9; i >= 0; i--) {
+      htmlFile.print(
+          "<TR BGCOLOR=" + color(i * 10 + 10, 128, 192, 128, 192, 255, 255) + "><TD ALIGN=center>"
+              + (i * 10) + "% - "
+              + (i * 10 + 10) + "%" + "</TD><TD ALIGN=right>" + similarityDistribution[i]
+              + "</TD><TD>");
+
+      for (int j = (similarityDistribution[i] * barLength / max); j > 0; j--) {
+        htmlFile.print("#");
+      }
+
+      if (similarityDistribution[i] * barLength / max == 0) {
+        if (similarityDistribution[i] == 0) {
+          htmlFile.print(".");
+        } else {
+          htmlFile.print("#");
+        }
+      }
+
+      htmlFile.println("</TD></TR>");
+    }
+
+    htmlFile.println("</TABLE></CENTER>\n<P>\n<HR>");
+  }
+
+  private void writeLinksToComparisons(
+      HTMLFile htmlFile,
+      String headerStr,
+      String csvFile
+  ) {
+    List<JPlagComparison> comparisons = result.getComparisons();
+
+    htmlFile.println(headerStr + " (<a href=\"help-sim-" + "en" // Country tag
+        + ".html\"><small><font color=\"#000088\">"
+        + msg.getString("Report.WhatIsThis") + "</font></small></a>):</H4>");
+    htmlFile.println("<p><a href=\"" + csvFile + "\">download csv</a></p>");
+    htmlFile.println("<TABLE CELLPADDING=3 CELLSPACING=2>");
+
+    for (JPlagComparison comparison : comparisons) {
+      String submissionNameA = comparison.subA.name;
+      String submissionNameB = comparison.subB.name;
+
+      htmlFile.print(
+          "<TR><TD BGCOLOR=" + color(comparison.percentA(), 128, 192, 128, 192, 255, 255)
+              + ">" + submissionNameA
+              + "</TD><TD><nobr>-&gt;</nobr>");
+
+      htmlFile
+          .print("</TD><TD BGCOLOR=" + color(comparison.percentB(), 128, 192, 128, 192, 255, 255)
+              + " ALIGN=center><A HREF=\"match"
+              + getComparisonIndex(comparison) + ".html\">" + submissionNameB
+              + "</A><BR><FONT COLOR=\""
+              + color(comparison.percent(), 0, 255, 0, 0, 0, 0) + "\">(" + (
+              ((int) (comparison.percent() * 10))
+                  / (float) 10) + "%)</FONT>");
+
+      htmlFile.println("</TD></TR>");
+    }
+
+    htmlFile.println("</TABLE><P>\n");
+    htmlFile.println("<!---->");
+  }
+
+  private void writeMatchesCSV(String fileName) {
+    FileWriter writer = null;
+    File csvFile = new File(reportDir, fileName);
+    List<JPlagComparison> comparisons = result.getComparisons();
+
+    try {
+      csvFile.createNewFile();
+      writer = new FileWriter(csvFile);
+
+      for (JPlagComparison comparison : comparisons) {
+        String submissionNameA = comparison.subA.name;
+        String submissionNameB = comparison.subB.name;
+
+        writer.write(getComparisonIndex(comparison) + ";");
+        writer.write(submissionNameA + ";");
+        writer.write(submissionNameB + ";");
+        writer.write((((int) (comparison.percent() * 10)) / (float) 10) + ";");
+        writer.write("\n");
+      }
+
+      writer.flush();
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      try {
+        writer.close();
+      } catch (Exception ignored) {
+      }
+    }
+  }
+
+  private void writeMatches(List<JPlagComparison> comparisons) {
+    comparisons.forEach(comparison -> {
+      try {
+        writeMatch(comparison);
+      } catch (ExitException e) {
+        e.printStackTrace();
+      }
+    });
+  }
+
+  private void writeMatch(JPlagComparison comparison) throws ExitException {
+    int i = getComparisonIndex(comparison);
+
+    // match???.html
+    writeFrames(i, comparison);
+
+    // match???-link.html
+    writeLink(i, comparison);
+
+    // match???-top.html
+    writeTop(i, comparison);
+
+    // match???-?.html
+    if (result.getOptions().getLanguage().usesIndex()) {
+      writeIndexedSubmission(i, comparison, 0);
+      writeIndexedSubmission(i, comparison, 1);
+    } else if (result.getOptions().getLanguage().supportsColumns()) {
+      writeImprovedSubmission(i, comparison, 0);
+      writeImprovedSubmission(i, comparison, 1);
+    } else {
+      writeNormalSubmission(i, comparison, 0);
+      writeNormalSubmission(i, comparison, 1);
+    }
+  }
+
+  private void writeFrames(int i, JPlagComparison comparison) throws ExitException {
+    HTMLFile htmlFile = createHTMLFile("match" + i + ".html");
+
+    writeHTMLHeader(
+        htmlFile,
+        TagParser.parse(
+            msg.getString("Report.Matches_for_X1_AND_X2"),
+            new String[]{comparison.subA.name, comparison.subB.name}
+        )
+    );
+
+    htmlFile.println("<FRAMESET ROWS=\"130,*\">\n <FRAMESET COLS=\"30%,70%\">");
+    htmlFile.println("  <FRAME SRC=\"match" + i + "-link.html\" NAME=\"link\" " + "FRAMEBORDER=0>");
+    htmlFile.println("  <FRAME SRC=\"match" + i + "-top.html\" NAME=\"top\" " + "FRAMEBORDER=0>");
+    htmlFile.println(" </FRAMESET>");
+    htmlFile.println(" <FRAMESET COLS=\"50%,50%\">");
+    htmlFile.println("  <FRAME SRC=\"match" + i + "-0.html\" NAME=\"0\">");
+    htmlFile.println("  <FRAME SRC=\"match" + i + "-1.html\" NAME=\"1\">");
+    htmlFile.println(" </FRAMESET>\n</FRAMESET>\n</HTML>");
+    htmlFile.close();
+  }
+
+  private void writeLink(int i, JPlagComparison comparison) throws jplag.ExitException {
+    HTMLFile htmlFile = createHTMLFile("match" + i + "-link.html");
+
+    writeHTMLHeader(htmlFile, msg.getString("Report.Links"));
+
+    htmlFile.println("<BODY>\n <H3 ALIGN=\"center\">"
+        + TagParser.parse(msg.getString("Report.Matches_for_X1_AND_X2"),
+        new String[]{comparison.subA.name, comparison.subB.name})
+        + "</H3>");
+    htmlFile.println(" <H1 align=\"center\">" + comparison.roundedPercent() + "%</H1>\n<CENTER>");
+    htmlFile
+        .println(" <A HREF=\"index.html#matches\" TARGET=\"_top\">" + msg.getString("Report.INDEX")
+            + "</A> - ");
+    htmlFile.println(" <A HREF=\"help-" + "en" + ".html\" TARGET=\"_top\">" + msg
+        .getString("Report.HELP")
+        + "</A></CENTER>");
+    htmlFile.println("</BODY>\n</HTML>");
+    htmlFile.close();
+  }
+
+  private void writeTop(int i, JPlagComparison comparison) throws jplag.ExitException {
+    HTMLFile htmlFile = createHTMLFile("match" + i + "-top.html");
+
+    writeHTMLHeaderWithScript(htmlFile, "Top");
+
+    htmlFile.println("<BODY BGCOLOR=\"#ffffff\">");
+
+    reportComparison(htmlFile, comparison, i);
+
+    htmlFile.println("</BODY>\n</HTML>\n");
+    htmlFile.close();
+  }
+
+  /**
+   * This method generates an table entry in the list of all comparisons.
+   */
+  private void reportComparison(HTMLFile htmlFile, JPlagComparison comparison, int index) {
+    Match match;
+    Token[] tokensA = comparison.subA.tokenList.tokens;
+    Token[] tokensB = comparison.subB.tokenList.tokens;
+    // sort();
+
+    htmlFile.println("<CENTER>\n<TABLE BORDER=\"1\" CELLSPACING=\"0\" " +
+        "BGCOLOR=\"#d0d0d0\">");
+    htmlFile
+        .println("<TR><TH><TH>" + comparison.subA.name + " (" + comparison.percentA() + "%)<TH>" +
+            comparison.subB.name + " (" + comparison.percentB() + "%)<TH>" + msg
+            .getString("AllMatches.Tokens"));
+
+    for (int i = 0; i < comparison.matches.size(); i++) {
+      match = comparison.matches.get(i);
+
+      Token startA = tokensA[match.startA];
+      Token endA = tokensA[match.startA + match.length - 1];
+      Token startB = tokensB[match.startB];
+      Token endB = tokensB[match.startB + match.length - 1];
+
+      String col = Colors.getColor(i);
+
+      htmlFile.print("<TR><TD BGCOLOR=\"" + col + "\"><FONT COLOR=\"" + col + "\">-</FONT>");
+      htmlFile.print("<TD><A HREF=\"javascript:ZweiFrames('match" +
+          index + "-0.html#" + i + "',2,'match" +
+          index + "-1.html#" + i + "',3)\" NAME=\"" + i + "\">");
+      htmlFile.print(new String(startA.file.getBytes()));
+
+      if (result.getOptions().getLanguage().usesIndex()) {
+        htmlFile.print("(" + startA.getIndex() + "-" + endA.getIndex() + ")");
+      } else {
+        htmlFile.print("(" + startA.getLine() + "-" + endA.getLine() + ")");
+      }
+
+      htmlFile.print("<TD><A HREF=\"javascript:ZweiFrames('match" +
+          index + "-0.html#" + i + "',2,'match" +
+          index + "-1.html#" + i + "',3)\" NAME=\"" + i + "\">");
+      htmlFile.print(startB.file);
+
+      if (result.getOptions().getLanguage().usesIndex()) {
+        htmlFile.print("(" + startB.getIndex() + "-" + endB.getIndex());
+      } else {
+        htmlFile.print("(" + startB.getLine() + "-" + endB.getLine());
+      }
+
+      htmlFile.println(")</A><TD ALIGN=center>" + "<FONT COLOR=\"" +
+          comparison.color(match.length) + "\">" + match.length + "</FONT>");
+    }
+
+    if (result.getOptions().hasBaseCode()) {
+      htmlFile.print("<TR><TD BGCOLOR=\"#C0C0C0\"><TD>"
+          + msg.getString("AllMatches.Basecode") + " "
+          + comparison.roundedPercentBasecodeA() + "%");
+      htmlFile.println("<TD>"
+          + msg.getString("AllMatches.Basecode") + " "
+          + comparison.roundedPercentBasecodeB() + "%<TD>&nbsp;");
+    }
+
+    htmlFile.println("</TABLE>\n</CENTER>");
+  }
+
+  // --------------------------------------------------------------------------
+  // ==========================================================================
+  // --------------------------------------------------------------------------
+
+//  TODO:
+//  /* this function copies all submissions into the result directory */
+//  private int copySubmissions() throws jplag.ExitException {
+//    int bytes = 0;
+//    for (Iterator<Submission> i = program.clusters.neededSubmissions.iterator(); i.hasNext(); ) {
+//      Submission sub = i.next();
+//      int index = this.program.clusters.submissions.indexOf(sub);
+//
+//      HTMLFile f = createHTMLFile(reportDir, "submission" + index + ".html");
+//      writeHTMLHeader(f, sub.name);
+//      f.println("<BODY BGCOLOR=\"#ffffff\">");
+//
+//      String[] files = sub.files;
+//      String text[][] = sub.readFiles(files);
+//
+//      for (int j = 0; j < files.length; j++) {
+//        f.println("<HR>\n<H3><CENTER>" + files[j] + "</CENTER></H3><HR>");
+//        if (this.language.isPreformated()) {
+//          f.println("<PRE>");
+//        }
+//        for (int k = 0; k < text[j].length; k++) {
+//          f.print(text[j][k]);
+//          if (!this.language.isPreformated()) {
+//            f.println("<BR>");
+//          } else {
+//            f.println();
+//          }
+//        }
+//        if (language.isPreformated()) {
+//          f.println("</PRE>");
+//        }
+//      }
+//
+//      f.println("</BODY>\n</HTML>");
+//      f.close();
+//      bytes += f.bytesWritten();
+//    }
+//    return bytes;
+//  }
+
+//  TODO:
+//  private int writeClusters(Cluster clustering) throws jplag.ExitException {
+//    int bytes = 0;
+//
+//    HTMLFile f = createHTMLFile("cluster.html");
+//    writeHTMLHeader(f, msg.getString("Report.Clustering_Results"));
+//    String clustertype = msg.getString("Report.Type") + ": " + program.clusters.getType();
+//    f.println("<BODY>\n<H2>" + msg.getString("Report.Clustering_Results") + " (" + clustertype
+//        + ")</H2>");
+//    f.println("<H3><A HREF=\"dendro.html\">" + msg.getString("Report.Dendrogram") + "</A></H3>");
+//    bytes += this.program.clusters.makeDendrograms(reportDir, clustering);
+//
+//    if (this.program.get_threshold() != null) {
+//      for (int i = 0; i < this.program.get_threshold().length; i++) {
+//        float threshold = this.program.get_threshold()[i];
+//        String clustertitle = TagParser
+//            .parse(msg.getString("Report.Clusters_for_Xpercent_treshold"),
+//                new String[]{threshold + ""});
+//        f.println("<H3><A HREF=\"cluster" + threshold + ".html\">" + clustertitle + "</A></H3>");
+//        HTMLFile f2 = createHTMLFile(reportDir, "cluster" + threshold + ".html");
+//        writeHTMLHeader(f2, clustertitle);
+//        f2.println("<BODY>\n<H2>" + clustertitle + " (" + clustertype + ")</H2>");
+//        String text = program.clusters.printClusters(clustering, threshold, f2);
+//        f2.println("</BODY>\n</HTML>");
+//        f2.close();
+//        bytes += f2.bytesWritten();
+//        f.print(text);
+//      }
+//    } else {
+//      float increase = this.program.clusters.maxMergeValue / 10;
+//      if (increase < 5) {
+//        increase = 5;
+//      }
+//      for (float threshold = increase; threshold <= program.clusters.maxMergeValue;
+//          threshold += increase) {
+//        String clustertitle = TagParser
+//            .parse(msg.getString("Report.Clusters_for_Xpercent_treshold"),
+//                new String[]{threshold + ""});
+//        f.println(
+//            "<H3><A HREF=\"cluster" + (int) threshold + ".html\">" + clustertitle + "</A></H3>");
+//        HTMLFile f2 = createHTMLFile(reportDir, "cluster" + (int) threshold + ".html");
+//        writeHTMLHeader(f2, clustertitle);
+//        f2.println("<BODY>\n<H2>" + clustertitle + " (" + clustertype + ")</H2>");
+//        String text = program.clusters.printClusters(clustering, (int) threshold, f2);
+//        f2.println("</BODY>\n</HTML>");
+//        f2.close();
+//        bytes += f2.bytesWritten();
+//        f.print(text);
+//      }
+//    }
+//
+//    f.println("</BODY>\n</HTML>");
+//    f.close();
+//
+//    bytes += copySubmissions();
+//    return f.bytesWritten() + bytes;
+//  }
+
+  // SUBMISSION - here it comes...
+  private final String[] pics = {"forward.gif", "back.gif"};
+
+  /*
+   * i is the number of the match j == 0 if subA is considered, otherwise (j
+   * must then be 1) it is subB
+   */
+  private void writeNormalSubmission(int i, JPlagComparison comparison, int j)
+      throws ExitException {
+    Submission sub = (j == 0 ? comparison.subA : comparison.subB);
+    String[] files = comparison.files(j);
+
+    String[][] text = sub.readFiles(files);
+
+    Token[] tokens = (j == 0 ? comparison.subA : comparison.subB).tokenList.tokens;
+    Match currentMatch;
+    String hilf;
+    int h;
+    for (int x = 0; x < comparison.matches.size(); x++) {
+      currentMatch = comparison.matches.get(x);
+
+      Token start = tokens[(j == 0 ? currentMatch.startA : currentMatch.startB)];
+      Token ende = tokens[(
+          (j == 0 ? currentMatch.startA : currentMatch.startB) + currentMatch.length - 1)];
+
+      for (int y = 0; y < files.length; y++) {
+        if (start.file.equals(files[y]) && text[y] != null) {
+          hilf = "<FONT color=\"" + Colors.getColor(x) + "\">" + (j == 1
+              ? "<div style=\"position:absolute;left:0\">" : "")
+              + "<A HREF=\"javascript:ZweiFrames('match" + i + "-" + (1 - j) + ".html#" + x + "',"
+              + (3 - j) + ",'match" + i
+              + "-top.html#" + x + "',1)\"><IMG SRC=\"" + pics[j] + "\" ALT=\"other\" "
+              + "BORDER=\"0\" ALIGN=\""
+              + (j == 0 ? "right" : "left") + "\"></A>" + (j == 1 ? "</div>" : "") + "<B>";
+          // position the icon and the beginning of the colorblock
+          if (text[y][start.getLine() - 1].endsWith("</FONT>")) {
+            text[y][start.getLine() - 1] += hilf;
+          } else {
+            text[y][start.getLine() - 1] = hilf + text[y][start.getLine() - 1];
+          }
+          // the link location is placed 3 lines before the start of a block
+          h = (Math.max(start.getLine() - 4, 0));
+          text[y][h] = "<A NAME=\"" + x + "\"></A>" + text[y][h];
+          // mark the end
+          if (start.getLine() != ende.getLine() && // if match is only one line
+              text[y][ende.getLine() - 1].startsWith("<FONT ")) {
+            text[y][ende.getLine() - 1] = "</B></FONT>" + text[y][ende.getLine() - 1];
+          } else {
+            text[y][ende.getLine() - 1] += "</B></FONT>";
+          }
+        }
+      }
+    }
+
+    if (result.getOptions().hasBaseCode() && comparison.bcMatchesA != null
+        && comparison.bcMatchesB != null) {
+      JPlagBaseCodeComparison baseCodeComparison = (j == 0 ? comparison.bcMatchesA
+          : comparison.bcMatchesB);
+
+      for (int x = 0; x < baseCodeComparison.matches.size(); x++) {
+        currentMatch = baseCodeComparison.matches.get(x);
+        Token start = tokens[currentMatch.startA];
+        Token ende = tokens[currentMatch.startA + currentMatch.length - 1];
+
+        for (int y = 0; y < files.length; y++) {
+          if (start.file.equals(files[y]) && text[y] != null) {
+            hilf = ("<font color=\"#C0C0C0\"><EM>");
+            // position the icon and the beginning of the colorblock
+            if (text[y][start.getLine() - 1].endsWith("<font color=\"#000000\">")) {
+              text[y][start.getLine() - 1] += hilf;
+            } else {
+              text[y][start.getLine() - 1] = hilf + text[y][start.getLine() - 1];
+            }
+
+            // mark the end
+            if (start.getLine() != ende.getLine() && // match is only one line
+                text[y][ende.getLine() - 1].startsWith("<font color=\"#C0C0C0\">")) {
+              text[y][ende.getLine() - 1] =
+                  "</EM><font color=\"#000000\">" + text[y][ende.getLine() - 1];
+            } else {
+              text[y][ende.getLine() - 1] += "</EM><font color=\"#000000\">";
+            }
+          }
+        }
+      }
+    }
+
+    HTMLFile f = createHTMLFile("match" + i + "-" + j + ".html");
+    writeHTMLHeaderWithScript(f, (j == 0 ? comparison.subA : comparison.subB).name);
+    f.println("<BODY BGCOLOR=\"#ffffff\"" + (j == 1 ? " style=\"margin-left:25\">" : ">"));
+
+    for (int x = 0; x < text.length; x++) {
+      f.println("<HR>\n<H3><CENTER>" + files[x] + "</CENTER></H3><HR>");
+      if (result.getOptions().getLanguage().isPreformated()) {
+        f.println("<PRE>");
+      }
+      for (int y = 0; y < text[x].length; y++) {
+        f.print(text[x][y]);
+        if (!result.getOptions().getLanguage().isPreformated()) {
+          f.println("<BR>");
+        } else {
+          f.println();
+        }
+      }
+      if (result.getOptions().getLanguage().isPreformated()) {
+        f.println("</PRE>");
+      }
+    }
+
+    f.println("</BODY>\n</HTML>");
+    f.close();
+  }
+
+  /*
+   * i is the number of the match j == 0 if subA is considered, otherwise it
+   * is subB
+   *
+   * This procedure uses only the getIndex() method of the token. It is meant
+   * to be used with the Character front end
+   */
+  private void writeIndexedSubmission(int i, JPlagComparison comparison, int j)
+      throws ExitException {
+    Submission sub = (j == 0 ? comparison.subA : comparison.subB);
+    String[] files = comparison.files(j);
+    char[][] text = sub.readFilesChar(files);
+    Token[] tokens = (j == 0 ? comparison.subA : comparison.subB).tokenList.tokens;
+
+    // get index array with matches sorted in ascending order.
+    int[] perm = comparison.sort_permutation(j);
+
+    // HTML intro
+    HTMLFile f = createHTMLFile("match" + i + "-" + j + ".html");
+    writeHTMLHeaderWithScript(f, (j == 0 ? comparison.subA : comparison.subB).name);
+    f.println("<BODY BGCOLOR=\"#ffffff\">");
+
+    int index = 0; // match index
+    Match onematch = null;
+    Token start = null;
+    Token end = null;
+    for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
+      // print filename
+      f.println("<HR>\n<H3><CENTER>" + files[fileIndex] + "</CENTER></H3><HR>");
+      char[] buffer = text[fileIndex];
+
+      for (int charNr = 0; charNr < buffer.length; charNr++) {
+        if (onematch == null) {
+          if (index < comparison.matches.size()) {
+            onematch = comparison.matches.get(perm[index]);
+            start = tokens[(j == 0 ? onematch.startA : onematch.startB)];
+            end = tokens[((j == 0 ? onematch.startA : onematch.startB) + onematch.length - 1)];
+            index++;
+          } else {
+            start = end = null;
+          }
+        }
+        // begin markup
+        if (start != null && start.getIndex() == charNr) {
+          f.print("<A NAME=\"" + perm[index - 1] + "\"></A>");
+          f.print("<FONT color=\"" + Colors.getColor(perm[index - 1]) + "\"><B>");
+          //"<A HREF=\"javascript:ZweiFrames('match"+i+"-"+(1-j)+
+          //".html#"+index+"',"+(3-j)+",'match"+i+"-top.html#"+index+
+          //"',1)\">"+"<IMG SRC=\""+pics[j]+
+          //"\" ALT=\"other\" BORDER=\"0\" "+"ALIGN="+
+          //(j==0 ? "right" : "left")+"></A>");
+        }
+        // text
+        if (buffer[charNr] == '<') {
+          f.print("&lt;");
+        } else if (buffer[charNr] == '>') {
+          f.print("&gt;");
+        } else if (buffer[charNr] == '\n') {
+          f.print("<br>\n");
+        } else {
+          f.print(buffer[charNr]);
+        }
+        // end markup
+        if (end != null && end.getIndex() == charNr) {
+          f.print("</B></FONT>");
+          onematch = null; // switch to next match
+        }
+      }
+    }
+
+    f.println("\n</BODY>\n</HTML>");
+    f.close();
+  }
+
+  /*
+   * i is the number of the match j == 0 if subA is considered, otherwise (j
+   * must then be 1) it is subB
+   *
+   * This procedure makes use of the column and length information!
+   */
+  private int writeImprovedSubmission(int i, JPlagComparison comparison, int j)
+      throws jplag.ExitException {
+    Submission sub = (j == 0 ? comparison.subA : comparison.subB);
+    String[] files = comparison.files(j);
+    String[][] text = sub.readFiles(files);
+    Token[] tokens = (j == 0 ? comparison.subA : comparison.subB).tokenList.tokens;
+
+    // Markup list:
+    Comparator<MarkupText> comp = (mo1, mo2) -> {
+      int col1 = mo1.column;
+      int col2 = mo2.column;
+      if (col1 > col2) {
+        return -1;
+      } else if (col1 < col2) {
+        return 1;
+      }
+      return (mo1.frontMarkup ? -1 : 1);
+    };
+    TreeMap<MarkupText, Object> markupList = new TreeMap<>(comp);
+
+    for (int x = 0; x < comparison.matches.size(); x++) {
+      Match onematch = comparison.matches.get(x);
+
+      Token start = tokens[(j == 0 ? onematch.startA : onematch.startB)];
+      Token end = tokens[((j == 0 ? onematch.startA : onematch.startB) + onematch.length - 1)];
+      for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
+        if (start.file.equals(files[fileIndex]) && text[fileIndex] != null) {
+          String tmp = "<FONT color=\"" + Colors.getColor(x) + "\">" + (j == 1
+              ? "<div style=\"position:absolute;left:0\">" : "")
+              + "<A HREF=\"javascript:ZweiFrames('match" + i + "-" + (1 - j) + ".html#" + x + "',"
+              + (3 - j) + ",'match" + i
+              + "-top.html#" + x + "',1)\"><IMG SRC=\"" + pics[j] + "\" ALT=\"other\" "
+              + "BORDER=\"0\" ALIGN=\""
+              + (j == 0 ? "right" : "left") + "\"></A>" + (j == 1 ? "</div>" : "") + "<B>";
+          // position the icon and the beginning of the colorblock
+          markupList
+              .put(new MarkupText(fileIndex, start.getLine() - 1, start.getColumn() - 1, tmp, true),
+                  null);
+          // mark the end
+          markupList
+              .put(new MarkupText(fileIndex, end.getLine() - 1,
+                      end.getColumn() + end.getLength() - 1, "</B></FONT>", false),
+                  null);
+
+          // the link location is placed 3 lines before the start of a block
+          int linkLine = (Math.max(start.getLine() - 4, 0));
+          markupList
+              .put(new MarkupText(fileIndex, linkLine, 0, "<A NAME=\"" + x + "\"></A>", false),
+                  null);
+        }
+      }
+    }
+
+    if (result.getOptions().hasBaseCode() && comparison.bcMatchesA != null
+        && comparison.bcMatchesB != null) {
+      JPlagBaseCodeComparison baseCodeComparison = (j == 0 ? comparison.bcMatchesA
+          : comparison.bcMatchesB);
+
+      for (int x = 0; x < baseCodeComparison.matches.size(); x++) {
+        Match onematch = baseCodeComparison.matches.get(x);
+        Token start = tokens[onematch.startA];
+        Token end = tokens[onematch.startA + onematch.length - 1];
+
+        for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
+          if (start.file.equals(files[fileIndex]) && text[fileIndex] != null) {
+            String tmp = "<font color=\"#C0C0C0\"><EM>";
+            // beginning of the colorblock
+            markupList.put(
+                new MarkupText(fileIndex, start.getLine() - 1, start.getColumn() - 1, tmp, false),
+                null);
+            // mark the end
+            markupList.put(
+                new MarkupText(fileIndex, end.getLine() - 1, end.getColumn() + end.getLength() - 1,
+                    "</EM></font>",
+                    true), null);
+          }
+        }
+      }
+    }
+
+    // Apply changes:
+    for (MarkupText markup : markupList.keySet()) {
+      //System.out.println(markup);
+      String tmp = text[markup.fileIndex][markup.lineIndex];
+      // is there any &quot;, &amp;, &gt; or &lt; in the String?
+      if (tmp.indexOf('&') >= 0) {
+        Vector<String> tmpV = new Vector<>();
+        // convert the string into a vector
+        int strLength = tmp.length();
+        for (int k = 0; k < strLength; k++) {
+          if (tmp.charAt(k) != '&') {
+            tmpV.addElement(tmp.charAt(k) + "");
+          } else { //put &quot;, &amp;, &gt; and &lt; into one element
+            String tmpSub = tmp.substring(k);
+            if (tmpSub.startsWith("&quot;")) {
+              tmpV.addElement("&quot;");
+              k = k + 5;
+            } else if (tmpSub.startsWith("&amp;")) {
+              tmpV.addElement("&amp;");
+              k = k + 4;
+            } else if (tmpSub.startsWith("&lt;")) {
+              tmpV.addElement("&lt;");
+              k = k + 3;
+            } else if (tmpSub.startsWith("&gt;")) {
+              tmpV.addElement("&gt;");
+              k = k + 3;
+            } else {
+              tmpV.addElement(tmp.charAt(k) + "");
+            }
+          }
+        }
+        if (markup.column <= tmpV.size()) {
+          tmpV.insertElementAt(markup.text, markup.column);
+        } else {
+          tmpV.addElement(markup.text);
+        }
+
+        StringBuilder tmpVStr = new StringBuilder();
+        // reconvert the Vector into a String
+        for (int k = 0; k < tmpV.size(); k++) {
+          tmpVStr.append(tmpV.elementAt(k));
+        }
+        text[markup.fileIndex][markup.lineIndex] = tmpVStr.toString();
+      } else {
+        text[markup.fileIndex][markup.lineIndex] =
+            tmp.substring(0, (Math.min(tmp.length(), markup.column)))
+                + markup.text + tmp
+                .substring((Math.min(tmp.length(), markup.column)));
+      }
+    }
+
+    HTMLFile f = createHTMLFile("match" + i + "-" + j + ".html");
+    writeHTMLHeaderWithScript(f, (j == 0 ? comparison.subA : comparison.subB).name);
+    f.println("<BODY BGCOLOR=\"#ffffff\"" + (j == 1 ? " style=\"margin-left:25\">" : ">"));
+
+    for (int x = 0; x < text.length; x++) {
+      f.println("<HR>\n<H3><CENTER>" + files[x] + "</CENTER></H3><HR>");
+      if (result.getOptions().getLanguage().isPreformated()) {
+        f.println("<PRE>");
+      }
+      for (int y = 0; y < text[x].length; y++) {
+        f.print(text[x][y]);
+        if (!result.getOptions().getLanguage().isPreformated()) {
+          f.println("<BR>");
+        } else {
+          f.println();
+        }
+      }
+      if (result.getOptions().getLanguage().isPreformated()) {
+        f.println("</PRE>");
+      }
+    }
+    f.println("\n</BODY>\n</HTML>");
+    f.close();
+    return f.bytesWritten();
+  }
+}

+ 37 - 0
jplag/src/main/java/jplag/reporting/TagParser.java

@@ -0,0 +1,37 @@
+package jplag.reporting;
+
+public class TagParser {
+
+  /**
+   * Replaces all "{<index>_description}" tags inside the message string with params[<index>-1].
+   * <p>
+   * Example: parse("blb {1_n/a} d {3_desc} sf {2_bla} d", new String[] {"#", blabla(), null});
+   * returns "blb # d null sf blab d", if blabla() returns "blab"
+   */
+  public static String parse(String message, String[] params) {
+    String[] tokens = message.split("[{}]", -1);
+    String result = tokens[0];
+
+    for (int i = 1; i < tokens.length; i += 2)    // Go to next tag position
+    {
+      try {
+        int ind = tokens[i].indexOf('_');
+        String num = (ind == -1) ? tokens[i] : tokens[i].substring(0, ind);
+        result += params[Integer.parseInt(num) - 1];
+      } catch (Exception ex) {
+        if (ex instanceof NumberFormatException
+            || ex instanceof IndexOutOfBoundsException) {
+          ex.printStackTrace();
+          result += "{ILLEGAL PARAMETER INDEX \"" + tokens[i] + "\"}";
+        } else {
+          throw (RuntimeException) ex;
+        }
+      }
+      if (i + 1 < tokens.length) {
+        result += tokens[i + 1];
+      }
+    }
+
+    return result;
+  }
+}

+ 1 - 3
jplag/src/main/java/jplag/strategy/NormalComparisonStrategy.java

@@ -25,7 +25,6 @@ public class NormalComparisonStrategy extends AbstractComparisonStrategy {
     }
 
     long timeBeforeStartInMillis = System.currentTimeMillis();
-    int numberOfComparisons = 0;
     int i, j, numberOfSubmissions = submissions.size();
     Submission s1, s2;
     JPlagComparison comparison;
@@ -47,7 +46,6 @@ public class NormalComparisonStrategy extends AbstractComparisonStrategy {
         }
 
         comparison = this.gSTiling.compare(s1, s2);
-        numberOfComparisons++;
 
         System.out.println("Comparing " + s1.name + "-" + s2.name + ": " + comparison.percent());
 
@@ -71,7 +69,7 @@ public class NormalComparisonStrategy extends AbstractComparisonStrategy {
     //     cluster = this.clusters.calculateClustering(submissions);
     // }
 
-    return new JPlagResult(comparisons, numberOfComparisons, durationInMillis);
+    return new JPlagResult(comparisons, durationInMillis, numberOfSubmissions, options);
   }
 
 }

+ 1 - 3
jplag/src/main/java/jplag/strategy/RevisionComparisonStrategy.java

@@ -25,7 +25,6 @@ public class RevisionComparisonStrategy extends AbstractComparisonStrategy {
     }
 
     long timeBeforeStartInMillis = System.currentTimeMillis();
-    int numberOfComparisons = 0;
     int numberOfSubmissions = submissions.size();
     Submission s1, s2;
     JPlagComparison comparison;
@@ -54,7 +53,6 @@ public class RevisionComparisonStrategy extends AbstractComparisonStrategy {
       } while (s2.tokenList == null);
 
       comparison = this.gSTiling.compare(s1, s2);
-      numberOfComparisons++;
 
       System.out.println("Comparing " + s1.name + "-" + s2.name + ": " + comparison.percent());
 
@@ -79,6 +77,6 @@ public class RevisionComparisonStrategy extends AbstractComparisonStrategy {
     //     cluster = this.clusters.calculateClustering(submissions);
     // }
 
-    return new JPlagResult(comparisons, numberOfComparisons, durationInMillis);
+    return new JPlagResult(comparisons, durationInMillis, numberOfSubmissions, options);
   }
 }

+ 59 - 0
jplag/src/main/resources/jplag/messages_de.properties

@@ -0,0 +1,59 @@
+Report.Distribution=Verteilung
+Report.MatchesAvg=&Uuml;bereinstimmungen, sortiert nach der mittleren H&auml;ufigkeit
+Report.MatchesMax=&Uuml;bereinstimmungen, sortiert nach der maximalen H&auml;ufigkeit
+Report.MatchesMin=&Uuml;bereinstimmungen, sortiert nach der minimalen H&auml;ufigkeit
+Report.average_similarity=mittlere H&auml;ufigkeit
+Report.maximum_similarity=maximale H&auml;ufigkeit
+Report.minimum_similarity=minimale H&auml;ufigkeit
+Report.WhatIsThis=Was ist das?
+Report.Search_Results=Ergebnisse
+Report.Clustering_Results=Clustering Ergebnisse
+Report.Title=Titel
+Report.Directory=Verzeichnis
+Report.Programs=Quellen
+Report.Language=Sprache
+Report.Not_available=Nicht verf&uuml;gbar
+Report.Submissions=Eingaben
+Report.Invalid_submissions=Ung&uuml;ltige Eingaben
+Report.see_LOGBEG_log_file_LOGEND=(siehe {1_LOGBEG}Log-Datei{2_LOGEND})
+Report.Basecode_submission=Referenzeingabe
+Report.1_has_not_been_parsed_successfully=1 ist nicht erfolgreich geparst worden
+Report.X_have_not_been_parsed_successfully={1_NUMERRORS} sind nicht erfolgreich geparst worden
+Report.Matches_displayed=Dargestellte Ergebnisse
+Report.Treshold=Untere Grenze
+Report.Date=Datum
+Report.Minimum_Match_Length=Minimale Tokenl&auml;nge
+Report.sensitivity=Empfindlichkeit
+Report.Suffixes=Endungen
+Report.Type=Typ
+Report.Dendrogram=Dendrogram
+Report.Clusters_for_Xpercent_treshold=Cluster f&uuml;r eine untere Grenze von {1_PERCENT}%
+Report.Matches_for_X1_AND_X2=&Uuml;bereinstimmungen f&uuml;r {1_NAME} & {2_NAME}
+Report.Links=Links
+Report.INDEX=INDEX
+Report.HELP=HILFE
+Report.Distribution=Verteilung
+Report.Token_Distribution=Verteilung der Token
+
+Clusters.MIN_single_link=MIN, einfache Verkn&uuml;pfung
+Clusters.AVR_group_average=AVR, Gruppenmittel
+Clusters.MAX_complete_link=MAX, vollst&auml;ndige Verkn&uuml;pfung
+Clusters.unknown=unbekannt
+Clusters.Cluster_number=Cluster Nr.
+Clusters.Size=Gr&ouml;&szlig;e
+Clusters.Threshold=Untere Grenze
+Clusters.Cluster_members=Mitglieder des Clusters
+Clusters.Most_frequent_words=Meist vorkommende Worte
+Clusters.Distribution_of_cluster_size=Verteilung der Clustergröße
+Clusters.Cluster_size=Clustergr&ouml;&szlig;e
+Clusters.Number_of_clusters=Anzahl der Cluster
+Clusters.Dendrogram=Dendrogram
+Clusters.Themewords=Themewords
+Clusters.Documents=Dokumente
+Clusters.Dendrogram_picture=Dendrogram Bild
+
+AllMatches.Tokens=Meta-Zeichen
+AllMatches.Distribution=Verteilung
+AllMatches.Length=L&auml;nge
+AllMatches.Number_of_matches=Anzahl der &Uuml;bereinstimmungen
+AllMatches.Basecode=Referenz&uuml;bereinstimmung

+ 59 - 0
jplag/src/main/resources/jplag/messages_en.properties

@@ -0,0 +1,59 @@
+Report.Distribution=Distribution
+Report.MatchesAvg=Matches sorted by average similarity
+Report.MatchesMax=Matches sorted by maximum similarity
+Report.MatchesMin=Matches sorted by minimum similarity
+Report.average_similarity=average similarity
+Report.maximum_similarity=maximum similarity
+Report.minimum_similarity=minimum similarity
+Report.WhatIsThis=What is this?
+Report.Search_Results=Search Results
+Report.Clustering_Results=Clustering Results
+Report.Title=Title
+Report.Directory=Directory
+Report.Programs=Programs
+Report.Language=Language
+Report.Not_available=Not available
+Report.Submissions=Submissions
+Report.Invalid_submissions=Invalid submissions
+Report.see_LOGBEG_log_file_LOGEND=(see {1_LOGBEG}log file{2_LOGEND})
+Report.Basecode_submission=Basecode submission
+Report.1_has_not_been_parsed_successfully=1 has not been parsed successfully
+Report.X_have_not_been_parsed_successfully={1_NUMERRORS} have not been parsed successfully
+Report.Matches_displayed=Matches displayed
+Report.Treshold=Treshold
+Report.Date=Date
+Report.Minimum_Match_Length=Minimum Match Length
+Report.sensitivity=sensitivity
+Report.Suffixes=Suffixes
+Report.Type=Type
+Report.Dendrogram=Dendrogram
+Report.Clusters_for_Xpercent_treshold=Clusters for {1_PERCENT}% treshold
+Report.Matches_for_X1_AND_X2=Matches for {1_NAME} & {2_NAME}
+Report.Links=Links
+Report.INDEX=INDEX
+Report.HELP=HELP
+Report.Distribution=Distribution
+Report.Token_Distribution=Token Distribution
+
+Clusters.MIN_single_link=MIN, single link
+Clusters.AVR_group_average=AVR, group average
+Clusters.MAX_complete_link=MAX, complete link
+Clusters.unknown=unknown
+Clusters.Cluster_number=Cluster No
+Clusters.Size=Size
+Clusters.Threshold=Threshold
+Clusters.Cluster_members=Members of the cluster
+Clusters.Most_frequent_words=Most frequent words
+Clusters.Distribution_of_cluster_size=Distribution of cluster size
+Clusters.Cluster_size=Cluster size
+Clusters.Number_of_clusters=No of clusters
+Clusters.Dendrogram=Dendrogram
+Clusters.Themewords=Themewords
+Clusters.Documents=Documents
+Clusters.Dendrogram_picture=Dendrogram picture
+
+AllMatches.Tokens=Tokens
+AllMatches.Distribution=Distribution
+AllMatches.Length=Length
+AllMatches.Number_of_matches=No of matches
+AllMatches.Basecode=Basecode

+ 59 - 0
jplag/src/main/resources/jplag/messages_es.properties

@@ -0,0 +1,59 @@
+Report.Distribution=Distribución
+Report.MatchesAvg=Coincidencias clasificadas por la semejanza media
+Report.MatchesMax=Coincidencias clasificadas por semejanza máxima
+Report.MatchesMin=Coincidencias clasificadas por semejanza mínima
+Report.average_similarity=semejanza media
+Report.maximum_similarity=semejanza máxima
+Report.maximum_similarity=semejanza mínima
+Report.WhatIsThis=¿Qué es esto?
+Report.Search_Results=Resultados de la Búsqueda
+Report.Clustering_Results=Resultados del Clustering
+Report.Title=Título
+Report.Directory=Directorio
+Report.Programs=Programas
+Report.Language=Lenguaje
+Report.Not_available=No disponible
+Report.Submissions=Solicitudes
+Report.Invalid_submissions=Solicitudes  no válidas
+Report.see_LOGBEG_log_file_LOGEND=(ver {1_LOGBEG}archivo de informe{2_LOGEND})
+Report.Basecode_submission=Solicitud del código base
+Report.1_has_not_been_parsed_successfully=1 no ha sido análizado con éxito
+Report.X_have_not_been_parsed_successfully={1_NUMERRORS} no han sido análizados con éxito
+Report.Matches_displayed=Coincidencias mostradas
+Report.Treshold=Umbral
+Report.Date=Fecha
+Report.Minimum_Match_Length=Longitud mínima de coincidencias
+Report.sensitivity=sensibilidad
+Report.Suffixes=Sufijos
+Report.Type=Tipo
+Report.Dendrogram=Dendrogram
+Report.Clusters_for_Xpercent_treshold=Clusters para {1_PERCENT}% de umbral
+Report.Matches_for_X1_AND_X2=Coinicdencias para {1_NAME} & {2_NAME}
+Report.Links=Links
+Report.INDEX=INDICE
+Report.HELP=AYUDA
+Report.Distribution=Distribución
+Report.Token_Distribution=Distribución de símbolos
+
+Clusters.MIN_single_link=MIN, link único
+Clusters.AVR_group_average=AVR, media de grupo
+Clusters.MAX_complete_link=MAX, link completo
+Clusters.unknown=desconocido
+Clusters.Cluster_number=Cluster No
+Clusters.Size=Tamaño
+Clusters.Threshold=Umbral
+Clusters.Cluster_members=Miembros del cluster
+Clusters.Most_frequent_words=Palabras más frecuentes
+Clusters.Distribution_of_cluster_size=Distribución del tamaño del cluster
+Clusters.Cluster_size=Tamaño del cluster
+Clusters.Number_of_clusters=No de clusters
+Clusters.Dendrogram=Dendrogram
+Clusters.Themewords=Themewords
+Clusters.Documents=Documentos
+Clusters.Dendrogram_picture=Imagen Dendrogram 
+
+AllMatches.Tokens=Símbolos
+AllMatches.Distribution=Distribución
+AllMatches.Length=Longitud
+AllMatches.Number_of_matches=No de coincidencias
+AllMatches.Basecode=Código Base

+ 59 - 0
jplag/src/main/resources/jplag/messages_fr.properties

@@ -0,0 +1,59 @@
+Report.Distribution=Distribution
+Report.MatchesAvg=Matches sorted by average similarity
+Report.MatchesMax=Matches sorted by maximum similarity
+Report.MatchesMin=Matches sorted by minimum similarity
+Report.average_similarity=average similarity
+Report.maximum_similarity=maximum similarity
+Report.minimum_similarity=minimum similarity
+Report.WhatIsThis=What is this?
+Report.Search_Results=Recherche des Resultats
+Report.Clustering_Results=Resultats pour les reseaux 
+Report.Title=Title
+Report.Directory=Repertoire
+Report.Programs=Code source
+Report.Language=Languages
+Report.Not_available=Invisible
+Report.Submissions=Differents codes source
+Report.Invalid_submissions=Invalid submissions
+Report.see_LOGBEG_log_file_LOGEND=(see {1_LOGBEG}log file{2_LOGEND})
+Report.Basecode_submission=Code source de reference
+Report.1_has_not_been_parsed_successfully=1 n&#39a pas &#233t&#233 pars&#233
+Report.X_have_not_been_parsed_successfully={1_NUMERRORS} n&#39ont pas &#233t&#233 pars&#233
+Report.Matches_displayed=Ressemblances affich&#233es
+Report.Treshold=Treshold
+Report.Date=Date
+Report.Minimum_Match_Length=Longueur minimale des tokens consider&#233s
+Report.sensitivity=sensitivit&#233
+Report.Suffixes=Suffixes
+Report.Type=Types
+Report.Dendrogram=Dendrogram
+Report.Clusters_for_Xpercent_treshold=Reseaux pour {1_PERCENT}% treshold
+Report.Matches_for_X1_AND_X2=Ressemblance pour {1_NAME} & {2_NAME}
+Report.Links=Liens
+Report.INDEX=INDEX
+Report.HELP=AIDE
+Report.Distribution=Repartition
+Report.Token_Distribution=Repartition des tokens
+
+Clusters.MIN_single_link=MIN, single link
+Clusters.AVR_group_average=AVR, group average
+Clusters.MAX_complete_link=MAX, complete link
+Clusters.unknown=unknown
+Clusters.Cluster_number=Cluster No
+Clusters.Size=Size
+Clusters.Threshold=Threshold
+Clusters.Cluster_members=Members of the cluster
+Clusters.Most_frequent_words=Most frequent words
+Clusters.Distribution_of_cluster_size=Distribution of cluster size
+Clusters.Cluster_size=Cluster size
+Clusters.Number_of_clusters=No of clusters
+Clusters.Dendrogram=Dendrogram
+Clusters.Themewords=Themewords
+Clusters.Documents=Documents
+Clusters.Dendrogram_picture=Dendrogram picture
+
+AllMatches.Tokens=Tokens
+AllMatches.Distribution=Distribution
+AllMatches.Length=Length
+AllMatches.Number_of_matches=No of matches
+AllMatches.Basecode=Code source de reference

+ 59 - 0
jplag/src/main/resources/jplag/messages_pt.properties

@@ -0,0 +1,59 @@
+Report.Distribution=Distribution
+Report.MatchesAvg=Matches sorted by average similarity
+Report.MatchesMax=Matches sorted by maximum similarity
+Report.MatchesMin=Matches sorted by minimum similarity
+Report.average_similarity=average similarity
+Report.maximum_similarity=maximum similarity
+Report.minimum_similarity=minimum similarity
+Report.WhatIsThis=What is this?
+Report.Search_Results=Search Results
+Report.Clustering_Results=Clustering Results
+Report.Title=Title
+Report.Directory=Directory
+Report.Programs=Programs
+Report.Language=Language
+Report.Not_available=Not available
+Report.Submissions=Submissions
+Report.Invalid_submissions=Invalid submissions
+Report.see_LOGBEG_log_file_LOGEND=(see {1_LOGBEG}log file{2_LOGEND})
+Report.Basecode_submission=Basecode submission
+Report.1_has_not_been_parsed_successfully=1 has not been parsed successfully
+Report.X_have_not_been_parsed_successfully={1_NUMERRORS} have not been parsed successfully
+Report.Matches_displayed=Matches displayed
+Report.Treshold=Treshold
+Report.Date=Date
+Report.Minimum_Match_Length=Minimum Match Length
+Report.sensitivity=sensitivity
+Report.Suffixes=Suffixes
+Report.Type=Type
+Report.Dendrogram=Dendrogram
+Report.Clusters_for_Xpercent_treshold=Clusters for {1_PERCENT}% treshold
+Report.Matches_for_X1_AND_X2=Matches for {1_NAME} & {2_NAME}
+Report.Links=Links
+Report.INDEX=INDEX
+Report.HELP=HELP
+Report.Distribution=Distribution
+Report.Token_Distribution=Token Distribution
+
+Clusters.MIN_single_link=MIN, single link
+Clusters.AVR_group_average=AVR, group average
+Clusters.MAX_complete_link=MAX, complete link
+Clusters.unknown=unknown
+Clusters.Cluster_number=Cluster No
+Clusters.Size=Size
+Clusters.Threshold=Threshold
+Clusters.Cluster_members=Members of the cluster
+Clusters.Most_frequent_words=Most frequent words
+Clusters.Distribution_of_cluster_size=Distribution of cluster size
+Clusters.Cluster_size=Cluster size
+Clusters.Number_of_clusters=No of clusters
+Clusters.Dendrogram=Dendrogram
+Clusters.Themewords=Themewords
+Clusters.Documents=Documents
+Clusters.Dendrogram_picture=Dendrogram picture
+
+AllMatches.Tokens=Tokens
+AllMatches.Distribution=Distribution
+AllMatches.Length=Length
+AllMatches.Number_of_matches=No of matches
+AllMatches.Basecode=Basecode

二進制
jplag/src/main/resources/jplag/reporting/data/back.gif


+ 8 - 0
jplag/src/main/resources/jplag/reporting/data/fields.js

@@ -0,0 +1,8 @@
+function set(newSize, newThreshold, newDocuments, newThemewords) {
+    with(document.data) {
+	size.value = newSize;
+	thresh.value = newThreshold;
+	docs.value = newDocuments;
+	theme.value = newThemewords;
+    }
+}

二進制
jplag/src/main/resources/jplag/reporting/data/forward.gif


+ 50 - 0
jplag/src/main/resources/jplag/reporting/data/help-de.html

@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Wie man die Ergebnisse liest</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Wie man die Ergebnisse liest</h1>
+
+<p>Das Fenster ist in vier Teile unterteilt:
+
+<p>Die beiden unteren Teile enthalten die Programme, wobei ähnliche Abschnitte
+mit der selben Farbe markiert sind und durch <img src="forward.gif"
+alt="Vorwärts"> oder <img src="back.gif" alt="Rückwärts"> auf die jeweilige
+Stelle im anderen Programm verwiesen wird. Klickt man auf diese Bilder,
+springen die anderen Ansichten zu den entsprechenden Stellen.<br>
+Wurde zu einem Block kein ähnlicher Abschnitt im anderen Programm gefunden, so
+wird dieser Block schwarz dargestellt. 
+
+<p>Der linke obere Teil zeigt die prozentuale Ähnlichkeit zwischen den beiden
+Programmen und bietet Verweise zur Index-Seite und zu dieser Hilfe-Seite an.
+
+<p>Zuletzt enthält der rechte obere Teil eine Tabelle aller Abschnitte, zu denen
+ähnliche Abschnitte gefunden wurden. Dabei hat die Tabelle folgendes Format:
+
+<p><TABLE BORDER="1" CELLSPACING="0" BGCOLOR="#d0d0d0">
+<TR><TH><TH>Programm_1 (??%)<TH>Programm_2 (??%)<TH>Meta-Zeichen
+<TR><TD BGCOLOR="#c00000"><FONT COLOR="#c00000">-</FONT><TD>
+<A HREF="">Datei_1 (1-8)</a><TD><A HREF="">Datei_2 (1-10)</A>
+<TD ALIGN=center><FONT COLOR="#b00000">23</FONT>
+<TR><TD BGCOLOR="#00a000"><FONT COLOR="#00a000">-</FONT><TD>
+<A HREF="">Datei_1 (9-20)</a><TD><A HREF="">Datei_3 (15-19)</A>
+<TD ALIGN=center><FONT COLOR="#600000">11</FONT>
+</TABLE>
+
+<p>Jede Zeile gibt einen Bereich von Dateizeilen an, die als gleich zu
+betrachten sind, zusammen mit der Größe der Abschnitte in Meta-Zeichen.
+Die Dateinamen sind wiederum Verweise auf die entsprechenden
+Stellen in beiden Programmen, die in der Farbe des kleinen Rechtecks dargestellt
+werden.
+
+<p><a href="index.html">INDEX</a>
+<p>
+
+<hr>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 52 - 0
jplag/src/main/resources/jplag/reporting/data/help-en.html

@@ -0,0 +1,52 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>How to read the results</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>How to read the results</h1>
+
+<p>The window is divided into four frames:
+
+<p>The two big frames on the bottom contain the submissions whereby
+similar passages are marked with the same color. Black text means that
+no matching passage in the other submission was found. In addition,
+every passage has an associated <img src="forward.gif" alt="Forward"> or
+<img src="back.gif" alt="Backward">. Clicking on these icons sets the focus in the
+other program frame to the matching passage and scrolls the table on
+the top to the appropriate position.
+
+<p>The left frame on the top shows the percentage of similarity between
+the two programs and provides links to the index and to the page you
+are reading right now.
+
+<p>The right frame finally contains a table of all passages that
+matched. The table has the following format:
+
+<p><TABLE BORDER="1" CELLSPACING="0" BGCOLOR="#d0d0d0">
+<TR><TH><TH>Submission1 (??%)<TH>Submission2 (??%)<TH>Tokens
+<TR><TD BGCOLOR="#c00000"><FONT COLOR="#c00000">-</FONT><TD>
+<A HREF="">file1 (1-8)</a><TD><A HREF="">file2 (1-10)</A>
+<TD ALIGN=center><FONT COLOR="#b00000">23</FONT>
+<TR><TD BGCOLOR="#00a000"><FONT COLOR="#00a000">-</FONT><TD>
+<A HREF="">file1 (9-20)</a><TD><A HREF="">file2 (15-19)</A>
+<TD ALIGN=center><FONT COLOR="#600000">11</FONT>
+</TABLE>
+
+<p>Each row gives a range of line numbers from file1 and file2 that are
+considered to be the same, along with the size of the passage in
+tokens. The filenames are a hyperlink to the appropriate passages in
+both programs.
+The passage has the color of the small rectangle at the left-hand edge
+of the row.
+
+<p><a href="index.html">INDEX</a>
+<p>
+
+<hr>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 53 - 0
jplag/src/main/resources/jplag/reporting/data/help-es.html

@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Cómo leer los resultados</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Cómo leer los resultados</h1>
+
+<p>La ventana se encuentra dividida en cuatro marcos:
+
+<p>Los dos marcos grandes en el fondo contienen las solicitudes en las
+ cuales los parrafos similares están marcados con el mismo color. El texto 
+negro significa que no se encontró ninguna parte que coincidiese con la 
+otra solicitud. Además, cada parrafo tiene asociado <img src="forward.gif" alt="Adelante"> o
+<img src="back.gif" alt="Atrás">. Pulsar sobre estos iconos lleva el foco en el 
+otro marco del programa al pasaje que coincida realiza un scroll en la tabla
+a lo alto de la otra posición.
+
+<p>El marco izquierdo en la parte superior muestra el porcentaje de la 
+semejanza entre los dos programas y proporciona links al inicio 
+y a la página estás leyendo ahora.
+
+<p>El marco de la derecha contiene una tabla de todos los pasajes que
+coinciden. La tabla tiene el siguiente formato:
+
+<p><TABLE BORDER="1" CELLSPACING="0" BGCOLOR="#d0d0d0">
+<TR><TH><TH>Solicitud1 (??%)<TH>Solicitud2 (??%)<TH>Símbolos
+<TR><TD BGCOLOR="#c00000"><FONT COLOR="#c00000">-</FONT><TD>
+<A HREF="">archivo1 (1-8)</a><TD><A HREF="">archivo2 (1-10)</A>
+<TD ALIGN=center><FONT COLOR="#b00000">23</FONT>
+<TR><TD BGCOLOR="#00a000"><FONT COLOR="#00a000">-</FONT><TD>
+<A HREF="">archivo1 (9-20)</a><TD><A HREF="">archivo2 (15-19)</A>
+<TD ALIGN=center><FONT COLOR="#600000">11</FONT>
+</TABLE>
+
+<p>Cada línea proporciona un rango de números de línea del archivo1 y 
+el archivo2 que son consideradas iguales, junto con el tamaño del 
+pasaje en símbolos.
+
+Los nombres de archivos son hyperlinks al parrafo apropiado en ambos programas.
+
+El parrafo tiene el color del rectángulo pequeño en el borde izquierdo de la fila.
+
+<p><a href="index.html">INDICE</a>
+<p>
+
+<hr>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 52 - 0
jplag/src/main/resources/jplag/reporting/data/help-fr.html

@@ -0,0 +1,52 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>How to read the results</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>How to read the results</h1>
+
+<p>The window is divided into four frames:
+
+<p>The two big frames on the bottom contain the submissions whereby
+similar passages are marked with the same color. Black text means that
+no matching passage in the other submission was found. In addition,
+every passage has an associated <img src="forward.gif" alt="Forward"> or
+<img src="back.gif" alt="Backward">. Clicking on these icons sets the focus in the
+other program frame to the matching passage and scrolls the table on
+the top to the appropriate position.
+
+<p>The left frame on the top shows the percentage of similarity between
+the two programs and provides links to the index and to the page you
+are reading right now.
+
+<p>The right frame finally contains a table of all passages that
+matched. The table has the following format:
+
+<p><TABLE BORDER="1" CELLSPACING="0" BGCOLOR="#d0d0d0">
+<TR><TH><TH>Submission1 (??%)<TH>Submission2 (??%)<TH>Tokens
+<TR><TD BGCOLOR="#c00000"><FONT COLOR="#c00000">-</FONT><TD>
+<A HREF="">file1 (1-8)</a><TD><A HREF="">file2 (1-10)</A>
+<TD ALIGN=center><FONT COLOR="#b00000">23</FONT>
+<TR><TD BGCOLOR="#00a000"><FONT COLOR="#00a000">-</FONT><TD>
+<A HREF="">file1 (9-20)</a><TD><A HREF="">file2 (15-19)</A>
+<TD ALIGN=center><FONT COLOR="#600000">11</FONT>
+</TABLE>
+
+<p>Each row gives a range of line numbers from file1 and file2 that are
+considered to be the same, along with the size of the passage in
+tokens. The filenames are a hyperlink to the appropriate passages in
+both programs.
+The passage has the color of the small rectangle at the left-hand edge
+of the row.
+
+<p><a href="index.html">INDEX</a>
+<p>
+
+<hr>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 52 - 0
jplag/src/main/resources/jplag/reporting/data/help-pt.html

@@ -0,0 +1,52 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>How to read the results</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>How to read the results</h1>
+
+<p>The window is divided into four frames:
+
+<p>The two big frames on the bottom contain the submissions whereby
+similar passages are marked with the same color. Black text means that
+no matching passage in the other submission was found. In addition,
+every passage has an associated <img src="forward.gif" alt="Forward"> or
+<img src="back.gif" alt="Backward">. Clicking on these icons sets the focus in the
+other program frame to the matching passage and scrolls the table on
+the top to the appropriate position.
+
+<p>The left frame on the top shows the percentage of similarity between
+the two programs and provides links to the index and to the page you
+are reading right now.
+
+<p>The right frame finally contains a table of all passages that
+matched. The table has the following format:
+
+<p><TABLE BORDER="1" CELLSPACING="0" BGCOLOR="#d0d0d0">
+<TR><TH><TH>Submission1 (??%)<TH>Submission2 (??%)<TH>Tokens
+<TR><TD BGCOLOR="#c00000"><FONT COLOR="#c00000">-</FONT><TD>
+<A HREF="">file1 (1-8)</a><TD><A HREF="">file2 (1-10)</A>
+<TD ALIGN=center><FONT COLOR="#b00000">23</FONT>
+<TR><TD BGCOLOR="#00a000"><FONT COLOR="#00a000">-</FONT><TD>
+<A HREF="">file1 (9-20)</a><TD><A HREF="">file2 (15-19)</A>
+<TD ALIGN=center><FONT COLOR="#600000">11</FONT>
+</TABLE>
+
+<p>Each row gives a range of line numbers from file1 and file2 that are
+considered to be the same, along with the size of the passage in
+tokens. The filenames are a hyperlink to the appropriate passages in
+both programs.
+The passage has the color of the small rectangle at the left-hand edge
+of the row.
+
+<p><a href="index.html">INDEX</a>
+<p>
+
+<hr>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 73 - 0
jplag/src/main/resources/jplag/reporting/data/help-ptbr.html

@@ -0,0 +1,73 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title>Como ler os resultados</title>
+  <meta http-equiv="Content-Type"
+ content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="#ffffff" link="#000088" text="#000000" vlink="#000000">
+<center><img src="logo.gif" alt="JPlag" border="0"></center>
+<h1>Como ler os resultados</h1>
+<p>A janela est&aacute; dividida em 4 blocos:
+</p>
+<p>Os dois blocos de cima cont&eacute;m as submiss&otilde;es nas quais
+trechos similares est&atilde;o marcados com a mesma cor. Texto preto
+significa que n&atilde;o foi encontrado algum trecho equivalente em
+outro arquivo submetido.<br>
+&nbsp; Todo trecho com alguma equivalência encontrada
+tem um <img src="forward.gif" alt="Forward">
+ou
+<img src="back.gif" alt="Backward"> associado. Clicando nesses icones, o 
+foco vai para outro bloco com o trecho equivalente já na posição apropriada.
+</p>
+<p>O bloco da esquerda mostra a porcentagem de similaridade entre
+dois programas e possui links para a página inicial e para o código  
+que você estava vendo.
+
+</p>
+<p>
+O bloco da direita contém uma tabela de todos os trechos equivalentes encontrados.
+A tabela tem o seguinte formato:
+</p>
+<p>
+<table bgcolor="#d0d0d0" border="1" cellspacing="0">
+  <tbody>
+    <tr>
+      <th><br>
+      </th>
+      <th>Submissão1 (??%)</th>
+      <th>Submissão2 (??%)</th>
+      <th>Marcação
+      </th>
+    </tr>
+    <tr>
+      <td bgcolor="#c00000"><font color="#c00000">-</font></td>
+      <td><a href="">file1 (1-8)</a></td>
+      <td><a href="">file2 (1-10)</a>
+      </td>
+      <td align="center"><font color="#b00000">23</font>
+      </td>
+    </tr>
+    <tr>
+      <td bgcolor="#00a000"><font color="#00a000">-</font></td>
+      <td><a href="">file1 (9-20)</a></td>
+      <td><a href="">file2 (15-19)</a>
+      </td>
+      <td align="center"><font color="#600000">11</font>
+      </td>
+    </tr>
+  </tbody>
+</table>
+</p>
+<p> Cada coluna mostra uma sequência de números de linhas do arquivo1 e arquivo 2 que
+são consideradas equivalentes, juntamente com o tamanho do trecho em
+marcações. Os nomes dos arquivos são um hiperlink para o trecho apropriado em 
+ambos os programas.
+O trecho tem a cor de um pequeno retângulo na fronteira esquerda da coluna.
+</p>
+<p><a href="index.html">INDEX</a>
+</p>
+<p></p>
+<hr><small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 38 - 0
jplag/src/main/resources/jplag/reporting/data/help-sim-de.html

@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Match ranking</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Match ranking</h1>
+
+<p>JPlag compares programs by trying to cover one of the programs with
+(preferably big) sequences from the other program. The coverage of a program
+is defined as the ratio of covered source code to the total size of the source
+code.</p>
+
+<h4>Rank matches by average similarity</h4>
+
+<p>The average similarity is defined as the average of both program
+coverages.<br/>
+This is the default similarity which works in most cases: Matches with a high
+average similarity indicate that the programs work in a very similar way.</b>
+
+<h4>Rank matches by maximum similarity</h4>
+
+<p>The maximum similarity is defined as the maximum of both program
+coverages.<br/>
+This ranking is especially useful if the programs are very different in
+size. This can happen when dead code was inserted to disguise the origin
+of the plagiarized program.</p>
+
+<p><a href="index.html">INDEX</a></p>
+<p/>
+
+<hr/>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 38 - 0
jplag/src/main/resources/jplag/reporting/data/help-sim-en.html

@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Match ranking</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Match ranking</h1>
+
+<p>JPlag compares programs by trying to cover one of the programs with
+(preferably big) sequences from the other program. The coverage of a program
+is defined as the ratio of covered source code to the total size of the source
+code.</p>
+
+<h4>Rank matches by average similarity</h4>
+
+<p>The average similarity is defined as the average of both program
+coverages.<br/>
+This is the default similarity which works in most cases: Matches with a high
+average similarity indicate that the programs work in a very similar way.</b>
+
+<h4>Rank matches by maximum similarity</h4>
+
+<p>The maximum similarity is defined as the maximum of both program
+coverages.<br/>
+This ranking is especially useful if the programs are very different in
+size. This can happen when dead code was inserted to disguise the origin
+of the plagiarized program.</p>
+
+<p><a href="index.html">INDEX</a></p>
+<p/>
+
+<hr/>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 39 - 0
jplag/src/main/resources/jplag/reporting/data/help-sim-es.html

@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Ranking de Coincidencias</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Ranking de coincidencias</h1>
+
+<p>JPlag compara los programas intentando cubrir uno de los programas 
+con secuencias (preferiblemente grandes) del otro programa. La cobertura 
+de un programa se define como la proporción del código de fuente frente 
+al tamaño total del código de fuente.</p>
+
+<h4>Rango de coincidencias por semejanza media</h4>
+
+<p>La semejanza media se define como la media de la cobertura de 
+ambos programas.<br/>
+
+Esta es la semejanza por defecto que funciona en la mayoría de los casos: Las 
+coincidencias con una alta semajanza media indica que esos programas funcionan 
+de una forma muy similar.</b>
+
+<h4>Rango de coincidencias por semjanza máxima</h4>
+
+<p>La semejanza máxima se define como la mayor de ambas coberturas del programa.<br/>
+Este ranking es especialmente útil en programas que son muy diferentes en tamaño. Esto 
+se puede manifestar cuando se introducen grandes porciones de código dentro de uno de ellos 
+para que no se distinga respecto al programa plagiado.</p>
+
+<p><a href="index.html">INDEX</a></p>
+<p/>
+
+<hr/>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 38 - 0
jplag/src/main/resources/jplag/reporting/data/help-sim-fr.html

@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Match ranking</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Match ranking</h1>
+
+<p>JPlag compares programs by trying to cover one of the programs with
+(preferably big) sequences from the other program. The coverage of a program
+is defined as the ratio of covered source code to the total size of the source
+code.</p>
+
+<h4>Rank matches by average similarity</h4>
+
+<p>The average similarity is defined as the average of both program
+coverages.<br/>
+This is the default similarity which works in most cases: Matches with a high
+average similarity indicate that the programs work in a very similar way.</b>
+
+<h4>Rank matches by maximum similarity</h4>
+
+<p>The maximum similarity is defined as the maximum of both program
+coverages.<br/>
+This ranking is especially useful if the programs are very different in
+size. This can happen when dead code was inserted to disguise the origin
+of the plagiarized program.</p>
+
+<p><a href="index.html">INDEX</a></p>
+<p/>
+
+<hr/>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 38 - 0
jplag/src/main/resources/jplag/reporting/data/help-sim-pt.html

@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Match ranking</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Match ranking</h1>
+
+<p>JPlag compares programs by trying to cover one of the programs with
+(preferably big) sequences from the other program. The coverage of a program
+is defined as the ratio of covered source code to the total size of the source
+code.</p>
+
+<h4>Rank matches by average similarity</h4>
+
+<p>The average similarity is defined as the average of both program
+coverages.<br/>
+This is the default similarity which works in most cases: Matches with a high
+average similarity indicate that the programs work in a very similar way.</b>
+
+<h4>Rank matches by maximum similarity</h4>
+
+<p>The maximum similarity is defined as the maximum of both program
+coverages.<br/>
+This ranking is especially useful if the programs are very different in
+size. This can happen when dead code was inserted to disguise the origin
+of the plagiarized program.</p>
+
+<p><a href="index.html">INDEX</a></p>
+<p/>
+
+<hr/>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

+ 38 - 0
jplag/src/main/resources/jplag/reporting/data/help-sim-ptbr.html

@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML><HEAD><TITLE>Match ranking</TITLE>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#000088" VLINK="#000000" TEXT="#000000">
+
+<center><IMG SRC="logo.gif" ALT="JPlag" BORDER="0"></center>
+
+<h1>Ranking de Equivalências</h1>
+
+<p> JPlag compara programas tentando cobrir um dos programas com trechos 
+(preferencialmente grandes) de outros programs. A cobertura de um programa
+é definida como a razão do código fonte coberto com seu tamanho total.
+</p>
+
+<h4>Ranking de equivalências pela similaridade média</h4>
+
+<p>A similaridade média é definida como a média das coberturas de ambos os programas.<br/>
+Essa é a similaridade padrão que funciona na maioria dos casos: Equivalências com
+uma similaridade alta indica que os programas funcionam de uma maneira muito
+semelhante.</b>
+
+<h4>Ranking de equivalências pela similaridade máxima</h4>
+
+<p>A similaridade máxima é definida como a máxima similaridade da cobertura 
+de ambos os programas.<br/>
+Esse ranking é especialmente útil quando programas são muito distintos 
+em tamanho. Isso pode acontecer quando partes inúteis de código são inseridas com o intuito
+de enganar um programa que busca plágios.</p>
+
+<p><a href="index.html">INDEX</a></p>
+<p/>
+
+<hr/>
+<small><a href="mailto:jplag@ira.uka.de">Guido Malpohl</a></small>
+</body>
+</html>

二進制
jplag/src/main/resources/jplag/reporting/data/logo.gif