JPlagResult.java 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package de.jplag;
  2. import java.util.List;
  3. import java.util.function.ToDoubleFunction;
  4. import de.jplag.clustering.ClusteringResult;
  5. import de.jplag.options.JPlagOptions;
  6. import de.jplag.options.SimilarityMetric;
  7. /**
  8. * Encapsulates the results of a comparison of a set of source code submissions.
  9. */
  10. public class JPlagResult {
  11. private List<JPlagComparison> comparisons; // comparisons whose similarity was about the specified threshold
  12. private final SubmissionSet submissions;
  13. private final JPlagOptions options;
  14. private final long durationInMillis;
  15. private final int[] similarityDistribution; // 10-element array representing the similarity distribution of the detected matches.
  16. private List<ClusteringResult<Submission>> clusteringResult;
  17. private final int SIMILARITY_DISTRIBUTION_SIZE = 10;
  18. public JPlagResult(List<JPlagComparison> comparisons, SubmissionSet submissions, long durationInMillis, JPlagOptions options) {
  19. // sort by similarity (descending)
  20. this.comparisons = comparisons.stream().sorted((first, second) -> Double.compare(second.similarity(), first.similarity())).toList();
  21. this.submissions = submissions;
  22. this.durationInMillis = durationInMillis;
  23. this.options = options;
  24. similarityDistribution = calculateSimilarityDistribution(comparisons);
  25. }
  26. /**
  27. * Drops elements from the comparison list to free memory. Note, that this affects the similarity distribution and is
  28. * only meant to be used if you don't need the information about comparisons with lower match similarity anymore.
  29. * @param limit the number of comparisons to keep in the list
  30. */
  31. public void dropComparisons(int limit) {
  32. this.comparisons = this.getComparisons(limit);
  33. }
  34. public void setClusteringResult(List<ClusteringResult<Submission>> clustering) {
  35. this.clusteringResult = clustering;
  36. }
  37. /**
  38. * @return a list of all comparisons sorted by similarity (descending)
  39. */
  40. public List<JPlagComparison> getAllComparisons() {
  41. return comparisons;
  42. }
  43. /**
  44. * Returns the first n comparisons (sorted by similarity, descending), limited by the specified parameter.
  45. * @param numberOfComparisons specifies the number of requested comparisons. If set to -1, all comparisons will be
  46. * returned.
  47. * @return a list of comparisons sorted descending by similarity.
  48. */
  49. public List<JPlagComparison> getComparisons(int numberOfComparisons) {
  50. if (numberOfComparisons == JPlagOptions.SHOW_ALL_COMPARISONS) {
  51. return comparisons;
  52. }
  53. return comparisons.subList(0, Math.min(numberOfComparisons, comparisons.size()));
  54. }
  55. /**
  56. * @return the duration of the comparison in milliseconds.
  57. */
  58. public long getDuration() {
  59. return durationInMillis;
  60. }
  61. /**
  62. * @return the submission set that contains both the valid submissions and the invalid ones.
  63. */
  64. public SubmissionSet getSubmissions() {
  65. return submissions;
  66. }
  67. /**
  68. * @return the total number of submissions that have been compared.
  69. */
  70. public int getNumberOfSubmissions() {
  71. return submissions.numberOfSubmissions(); // Convenience method to preserve API
  72. }
  73. /**
  74. * @return the JPlag options with which the JPlag run was configured.
  75. */
  76. public JPlagOptions getOptions() {
  77. return options;
  78. }
  79. /**
  80. * For the {@link SimilarityMetric} JPlag was run with, this returns the similarity distribution of detected matches in
  81. * a 10-element array. Each entry represents the absolute frequency of matches whose similarity lies within the
  82. * respective interval. Intervals: 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9: [90% - 100%]
  83. * @return the similarity distribution array.
  84. */
  85. public int[] getSimilarityDistribution() {
  86. return similarityDistribution;
  87. }
  88. /**
  89. * For the {@link SimilarityMetric#MAX} that is built in to every {@link JPlagComparison}, this returns the similarity
  90. * distribution of detected matches in a 10-element array. Each entry represents the absolute frequency of matches whose
  91. * similarity lies within the respective interval. Intervals: 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9:
  92. * [90% - 100%]
  93. * @return the similarity distribution array. When JPlag was run with the {@link SimilarityMetric#MAX}, this will return
  94. * the same distribution as {@link JPlagResult#getSimilarityDistribution()}
  95. */
  96. public int[] getMaxSimilarityDistribution() {
  97. return calculateDistributionFor(comparisons, (JPlagComparison::maximalSimilarity));
  98. }
  99. public List<ClusteringResult<Submission>> getClusteringResult() {
  100. return this.clusteringResult;
  101. }
  102. @Override
  103. public String toString() {
  104. return String.format("JPlagResult { comparisons: %d, duration: %d ms, language: %s, submissions: %d }", getAllComparisons().size(),
  105. getDuration(), getOptions().language().getName(), submissions.numberOfSubmissions());
  106. }
  107. /**
  108. * Note: Before, comparisons with a similarity below the given threshold were also included in the similarity matrix.
  109. */
  110. private int[] calculateSimilarityDistribution(List<JPlagComparison> comparisons) {
  111. return calculateDistributionFor(comparisons, JPlagComparison::similarity);
  112. }
  113. private int[] calculateDistributionFor(List<JPlagComparison> comparisons, ToDoubleFunction<JPlagComparison> similarityExtractor) {
  114. int[] similarityDistribution = new int[SIMILARITY_DISTRIBUTION_SIZE];
  115. for (JPlagComparison comparison : comparisons) {
  116. double similarity = similarityExtractor.applyAsDouble(comparison); // extract similarity: 0.0 <= similarity <= 1.0
  117. int index = (int) (similarity * SIMILARITY_DISTRIBUTION_SIZE); // divide similarity by bucket size to find index of correct bucket.
  118. index = Math.min(index, SIMILARITY_DISTRIBUTION_SIZE - 1);// index is out of bounds when similarity is 1.0. decrease by one to count
  119. // towards the highest value bucket
  120. similarityDistribution[SIMILARITY_DISTRIBUTION_SIZE - 1 - index]++; // count comparison towards its determined bucket. bucket order is
  121. // reversed, so that the highest value bucket has the lowest index
  122. }
  123. return similarityDistribution;
  124. }
  125. }