JPlag.java 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  1. package jplag;
  2. import static jplag.options.Verbosity.LONG;
  3. import static jplag.options.Verbosity.PARSER;
  4. import static jplag.options.Verbosity.QUIET;
  5. import java.io.BufferedReader;
  6. import java.io.File;
  7. import java.io.FileReader;
  8. import java.io.FileWriter;
  9. import java.io.IOException;
  10. import java.util.Arrays;
  11. import java.util.Date;
  12. import java.util.Enumeration;
  13. import java.util.HashSet;
  14. import java.util.Hashtable;
  15. import java.util.Iterator;
  16. import java.util.Properties;
  17. import java.util.Vector;
  18. import jplag.clustering.Cluster;
  19. import jplag.clustering.Clusters;
  20. import jplag.clustering.SimilarityMatrix;
  21. import jplag.options.JPlagClusterType;
  22. import jplag.options.JPlagOptions;
  23. import jplagUtils.PropertiesLoader;
  24. /*
  25. * This class coordinates the whole program flow.
  26. * The revision history can be found on https://svn.ipd.kit.edu/trac/jplag/wiki/JPlag/History
  27. */
  28. public class JPlag implements ProgramI {
  29. private static final Properties versionProps = PropertiesLoader
  30. .loadProps("jplag/version.properties");
  31. public static final String name = "JPlag" + versionProps.getProperty("version", "devel");
  32. public static final String name_long =
  33. "JPlag (Version " + versionProps.getProperty("version", "devel") + ")";
  34. public String currentSubmissionName = "<Unknown submission>";
  35. public Vector<String> errorVector = new Vector<>();
  36. private Submission baseCodeSubmission = null;
  37. // Used Objects of anothers jplag.Classes ,they muss be just one time
  38. // instantiate
  39. public Clusters clusters = null;
  40. private int errors = 0;
  41. private String invalidSubmissionNames = null;
  42. /**
  43. * Set of file names to be excluded in comparison.
  44. */
  45. private HashSet<String> excludedFileNames = null;
  46. /**
  47. * Vector of file name to be included in comparison.
  48. */
  49. private Vector<String> includedFileNames = null;
  50. protected GSTiling gSTiling = new GSTiling(this);
  51. private Hashtable<String, AllBasecodeMatches> htBasecodeMatches = new Hashtable<>(30);
  52. // experiment end
  53. private final JPlagOptions options;
  54. private final Runtime runtime = Runtime.getRuntime();
  55. private Vector<Submission> submissions;
  56. private FileWriter writer = null;
  57. public JPlag(JPlagOptions options) throws ExitException {
  58. this.options = options;
  59. this.options.initializeSecondStep(this);
  60. }
  61. public JPlagOptions getOptions() {
  62. return this.options;
  63. }
  64. /**
  65. * Main procedure
  66. */
  67. public void run() throws jplag.ExitException {
  68. // This file contains all files names which are excluded
  69. readExclusionFile();
  70. if (options.hasFileList()) {
  71. createSubmissionsFileList();
  72. } else if (options.getIncludeFile() == null) {
  73. createSubmissions();
  74. System.out.println(submissions.size() + " submissions");
  75. } else {
  76. createSubmissionsExp();
  77. }
  78. if (!options.isSkipParse()) {
  79. doParse();
  80. } else {
  81. print("Skipping parsing...\n", null);
  82. }
  83. if (countValidSubmissions() < 2) {
  84. throwNotEnoughSubmissions();
  85. }
  86. errorVector = null; // errorVector is not needed anymore
  87. if (options.getClusterType() != JPlagClusterType.NONE) {
  88. clusters = new Clusters(this);
  89. options.similarity = new SimilarityMatrix(submissions.size());
  90. }
  91. System.gc();
  92. switch (options.getComparisonMode()) {
  93. case NORMAL:
  94. compare();
  95. break;
  96. case REVISION:
  97. revisionCompare();
  98. break;
  99. case SPECIAL:
  100. specialCompare();
  101. break;
  102. case EXTERNAL:
  103. try {
  104. externalCompare();
  105. } catch (OutOfMemoryError e) {
  106. e.printStackTrace();
  107. }
  108. break;
  109. case EXPERIMENTAL:
  110. expCompare();
  111. break;
  112. default:
  113. throw new ExitException(
  114. "Illegal comparison mode: \"" + options.getComparisonMode() + "\""
  115. );
  116. }
  117. closeWriter();
  118. }
  119. public void doParse() throws ExitException {
  120. try {
  121. parseAll();
  122. System.gc();
  123. parseBaseCodeSubmission();
  124. } catch (OutOfMemoryError e) {
  125. submissions = null;
  126. System.gc();
  127. System.out.println(
  128. "[" + new Date() + "] OutOfMemoryError " + "during parsing of submission \""
  129. + currentSubmissionName
  130. + "\"");
  131. throw new ExitException(
  132. "Out of memory during parsing of submission \"" + currentSubmissionName + "\"");
  133. } catch (ExitException e) {
  134. throw e;
  135. } catch (Throwable e) {
  136. System.out.println(
  137. "[" + new Date() + "] Unknown exception " + "during parsing of submission \""
  138. + currentSubmissionName
  139. + "\"");
  140. e.printStackTrace();
  141. throw new ExitException(
  142. "Unknown exception during parsing of " + "submission \"" + currentSubmissionName
  143. + "\"");
  144. }
  145. }
  146. public void addError(String errorMsg) {
  147. errorVector.add("[" + currentSubmissionName + "]\n" + errorMsg);
  148. print(errorMsg, null);
  149. }
  150. public void print(String normal, String lng) {
  151. if (options.getVerbosity() == PARSER) {
  152. if (lng != null) {
  153. myWrite(lng);
  154. } else if (normal != null) {
  155. myWrite(normal);
  156. }
  157. }
  158. if (options.getVerbosity() == QUIET) {
  159. return;
  160. }
  161. try {
  162. if (normal != null) {
  163. System.out.print(normal);
  164. }
  165. if (lng != null) {
  166. if (options.getVerbosity() == LONG) {
  167. System.out.print(lng);
  168. }
  169. }
  170. } catch (Throwable e) {
  171. System.out.println(e.getMessage());
  172. }
  173. }
  174. /**
  175. * All submission with no errors are counted. (unsure if this is still necessary.)
  176. */
  177. protected int countValidSubmissions() {
  178. if (submissions == null) {
  179. return 0;
  180. }
  181. int size = 0;
  182. for (int i = submissions.size() - 1; i >= 0; i--) {
  183. if (!submissions.elementAt(i).errors) {
  184. size++;
  185. }
  186. }
  187. return size;
  188. }
  189. public void closeWriter() {
  190. try {
  191. if (writer != null) {
  192. writer.close();
  193. }
  194. } catch (IOException ex) {
  195. ex.printStackTrace();
  196. }
  197. writer = null;
  198. }
  199. private void throwNotEnoughSubmissions() throws jplag.ExitException {
  200. StringBuilder errorStr = new StringBuilder();
  201. for (String str : errorVector) {
  202. errorStr.append(str);
  203. errorStr.append('\n');
  204. }
  205. throw new ExitException("Not enough valid submissions! (only " + countValidSubmissions() + " "
  206. + (countValidSubmissions() != 1 ? "are" : "is") + " valid):\n" + errorStr.toString(),
  207. ExitException.NOT_ENOUGH_SUBMISSIONS_ERROR);
  208. }
  209. private void throwBadBaseCodeSubmission() throws jplag.ExitException {
  210. StringBuilder errorStr = new StringBuilder();
  211. for (String str : errorVector) {
  212. errorStr.append(str);
  213. errorStr.append('\n');
  214. }
  215. throw new ExitException("Bad basecode submission:\n" + errorStr.toString());
  216. }
  217. /**
  218. * Now the actual comparison: All submissions are compared pairwise.
  219. */
  220. private void compare() {
  221. int size = submissions.size();
  222. // Result vectors
  223. SortedVector<AllMatches> avgMatches = new SortedVector<>(new AllMatches.AvgComparator());
  224. SortedVector<AllMatches> maxMatches = new SortedVector<>(new AllMatches.MaxComparator());
  225. int[] dist = new int[10];
  226. long msec;
  227. AllBasecodeMatches bcMatch;
  228. Submission s1, s2;
  229. // ------------------------------------------------------------------------
  230. if (this.options.hasBaseCode()) {
  231. int countBC = 0;
  232. msec = System.currentTimeMillis();
  233. for (int i = 0; i < (size); i++) {
  234. s1 = submissions.elementAt(i);
  235. bcMatch = this.gSTiling.compareWithBasecode(s1, baseCodeSubmission);
  236. htBasecodeMatches.put(s1.name, bcMatch);
  237. this.gSTiling.resetBaseSubmission(baseCodeSubmission);
  238. countBC++;
  239. }
  240. long timebc = System.currentTimeMillis() - msec;
  241. print("\n\n",
  242. "\nTime for comparing with Basecode: " + ((timebc / 3600000 > 0) ? (timebc / 3600000)
  243. + " h " : "")
  244. + ((timebc / 60000 > 0) ? ((timebc / 60000) % 60000) + " min " : "") + (timebc / 1000
  245. % 60) + " sec\n"
  246. + "Time per basecode comparison: " + (timebc / size) + " msec\n\n");
  247. }
  248. // ------------------------------------------------------------------------
  249. int totalComps = (size - 1) * size / 2;
  250. int i, j, anz = 0, count = 0;
  251. AllMatches match;
  252. msec = System.currentTimeMillis();
  253. for (i = 0; i < (size - 1); i++) {
  254. s1 = submissions.elementAt(i);
  255. if (s1.struct == null) {
  256. count += (size - i - 1);
  257. continue;
  258. }
  259. for (j = (i + 1); j < size; j++) {
  260. s2 = submissions.elementAt(j);
  261. if (s2.struct == null) {
  262. count++;
  263. continue;
  264. }
  265. match = this.gSTiling.compare(s1, s2);
  266. anz++;
  267. System.out.println("Comparing " + s1.name + "-" + s2.name + ": " + match.percent());
  268. // histogram:
  269. if (options.hasBaseCode()) {
  270. match.bcmatchesA = htBasecodeMatches.get(match.subA.name);
  271. match.bcmatchesB = htBasecodeMatches.get(match.subB.name);
  272. }
  273. registerMatch(match, dist, avgMatches, maxMatches, null, i, j);
  274. count++;
  275. }
  276. }
  277. long time = System.currentTimeMillis() - msec;
  278. print("\n",
  279. "Total time for comparing submissions: " + ((time / 3600000 > 0) ? (time / 3600000) + " h "
  280. : "")
  281. + ((time / 60000 > 0) ? ((time / 60000) % 60000) + " min " : "") + (time / 1000 % 60)
  282. + " sec\n" + "Time per comparison: "
  283. + (time / anz) + " msec\n");
  284. // ------------------------------------------------------------------------
  285. Cluster cluster = null;
  286. if (options.getClusterType() != JPlagClusterType.NONE) {
  287. cluster = this.clusters.calculateClustering(submissions);
  288. }
  289. // Deprecated:
  290. // writeResults(dist, avgmatches, maxmatches, null, cluster);
  291. // TODO: Replace writeResults(...)
  292. }
  293. /**
  294. * Revision compare mode: Compare each submission only with its next submission.
  295. */
  296. private void revisionCompare() {
  297. int size = submissions.size();
  298. // Result vectors
  299. SortedVector<AllMatches> avgmatches = new SortedVector<>(
  300. new AllMatches.AvgReversedComparator());
  301. SortedVector<AllMatches> maxmatches = new SortedVector<>(
  302. new AllMatches.MaxReversedComparator());
  303. SortedVector<AllMatches> minmatches = new SortedVector<>(
  304. new AllMatches.MinReversedComparator());
  305. int[] dist = new int[10];
  306. long msec;
  307. AllBasecodeMatches bcmatch;
  308. Submission s1, s2;
  309. // ------------------------------------------------------------------------
  310. if (options.hasBaseCode()) {
  311. msec = System.currentTimeMillis();
  312. for (int i = 0; i < size; i++) {
  313. s1 = submissions.elementAt(i);
  314. bcmatch = gSTiling.compareWithBasecode(s1, baseCodeSubmission);
  315. htBasecodeMatches.put(s1.name, bcmatch);
  316. gSTiling.resetBaseSubmission(baseCodeSubmission);
  317. }
  318. long timebc = System.currentTimeMillis() - msec;
  319. print("\n\n",
  320. "\nTime for comparing with Basecode: " + ((timebc / 3600000 > 0) ? (timebc / 3600000)
  321. + " h " : "")
  322. + ((timebc / 60000 > 0) ? ((timebc / 60000) % 60000) + " min " : "") + (timebc / 1000
  323. % 60) + " sec\n"
  324. + "Time per basecode comparison: " + (timebc / size) + " msec\n\n");
  325. }
  326. // ------------------------------------------------------------------------
  327. int totalcomps = size - 1;
  328. int anz = 0, count = 0;
  329. AllMatches match;
  330. msec = System.currentTimeMillis();
  331. s1loop:
  332. for (int i = 0; i < size - 1; ) {
  333. s1 = submissions.elementAt(i);
  334. if (s1.struct == null) {
  335. count++;
  336. continue;
  337. }
  338. // Find next valid submission
  339. int j = i;
  340. do {
  341. j++;
  342. if (j >= size) {
  343. break s1loop; // no more comparison pairs available
  344. }
  345. s2 = submissions.elementAt(j);
  346. } while (s2.struct == null);
  347. match = this.gSTiling.compare(s1, s2);
  348. anz++;
  349. /*
  350. * System.out.println("Comparing "+s1.name+"-"+s2.name+": "+
  351. * match.percent());
  352. */
  353. // histogram:
  354. if (options.hasBaseCode()) {
  355. match.bcmatchesA = htBasecodeMatches.get(match.subA.name);
  356. match.bcmatchesB = htBasecodeMatches.get(match.subB.name);
  357. }
  358. registerMatch(match, dist, avgmatches, maxmatches, minmatches, i, j);
  359. count++;
  360. i = j;
  361. }
  362. long time = System.currentTimeMillis() - msec;
  363. print("\n",
  364. "Total time for comparing submissions: " + ((time / 3600000 > 0) ? (time / 3600000) + " h "
  365. : "")
  366. + ((time / 60000 > 0) ? ((time / 60000) % 60000) + " min " : "") + (time / 1000 % 60)
  367. + " sec\n" + "Time per comparison: "
  368. + (time / anz) + " msec\n");
  369. // ------------------------------------------------------------------------
  370. Cluster cluster = null;
  371. if (options.getClusterType() != JPlagClusterType.NONE) {
  372. cluster = this.clusters.calculateClustering(submissions);
  373. }
  374. // Deprecated:
  375. // writeResults(dist, avgmatches, maxmatches, minmatches, cluster);
  376. // TODO: Replace writeResults(...)
  377. }
  378. // EXPERIMENT !!!!! special compare routine!
  379. private void expCompare() {
  380. int size = countValidSubmissions();
  381. int[] similarity = new int[(size * size - size) / 2];
  382. int anzSub = submissions.size();
  383. int i, j, count = 0;
  384. Submission s1, s2;
  385. AllMatches match;
  386. long msec = System.currentTimeMillis();
  387. for (i = 0; i < (anzSub - 1); i++) {
  388. s1 = submissions.elementAt(i);
  389. if (s1.struct == null) {
  390. continue;
  391. }
  392. for (j = (i + 1); j < anzSub; j++) {
  393. s2 = submissions.elementAt(j);
  394. if (s2.struct == null) {
  395. continue;
  396. }
  397. match = this.gSTiling.compare(s1, s2);
  398. similarity[count++] = (int) match.percent();
  399. }
  400. }
  401. long time = System.currentTimeMillis() - msec;
  402. // output
  403. System.out.print(options.getRootDir() + " ");
  404. System.out.print(options.getMinTokenMatch() + " ");
  405. System.out.print(options.getFilter() + " ");
  406. System.out.print((time) + " ");
  407. for (i = 0; i < similarity.length; i++) {
  408. System.out.print(similarity[i] + " ");
  409. }
  410. System.out.println();
  411. }
  412. /**
  413. * This is the special external comparison routine
  414. */
  415. private void externalCompare() {
  416. int size = submissions.size();
  417. // Result vector
  418. SortedVector<AllMatches> avgmatches = new SortedVector<>(
  419. new AllMatches.AvgComparator());
  420. SortedVector<AllMatches> maxmatches = new SortedVector<>(
  421. new AllMatches.MaxComparator());
  422. int[] dist = new int[10];
  423. print("Comparing: " + size + " submissions\n", null);
  424. long totalComparisons = (size * (size - 1)) / 2, count = 0, comparisons = 0;
  425. int index;
  426. AllMatches match;
  427. Submission s1, s2;
  428. long remain;
  429. String totalTimeStr, remainTime;
  430. print("Checking memory size...\n", null);
  431. // First try to load as many submissions as possible
  432. index = fillMemory(0, size);
  433. long startTime;
  434. long totalTime = 0;
  435. int startA = 0;
  436. int endA = index / 2;
  437. int startB = endA + 1;
  438. int endB = index;
  439. int i, j;
  440. // long progStart;
  441. do {
  442. // compare A to A
  443. startTime = System.currentTimeMillis();
  444. print("Comparing block A (" + startA + "-" + endA + ") to block A\n", null);
  445. for (i = startA; i <= endA; i++) {
  446. s1 = submissions.elementAt(i);
  447. if (s1.struct == null) {
  448. count += (endA - i);
  449. continue;
  450. }
  451. for (j = (i + 1); j <= endA; j++) {
  452. s2 = submissions.elementAt(j);
  453. if (s2.struct == null) {
  454. count++;
  455. continue;
  456. }
  457. match = this.gSTiling.compare(s1, s2);
  458. registerMatch(match, dist, avgmatches, maxmatches, null, i, j);
  459. comparisons++;
  460. count++;
  461. }
  462. }
  463. print("\n", null);
  464. totalTime += System.currentTimeMillis() - startTime;
  465. // Are we finished?
  466. if (startA == startB) {
  467. break;
  468. }
  469. do {
  470. totalTimeStr = "" + ((totalTime / 3600000 > 0) ? (totalTime / 3600000) + " h " : "")
  471. + ((totalTime / 60000 > 0) ? ((totalTime / 60000) % 60) + " min " : "") + (
  472. totalTime / 1000 % 60) + " sec";
  473. if (comparisons != 0) {
  474. remain = totalTime * (totalComparisons - count) / comparisons;
  475. } else {
  476. remain = 0;
  477. }
  478. remainTime = "" + ((remain / 3600000 > 0) ? (remain / 3600000) + " h " : "")
  479. + ((remain / 60000 > 0) ? ((remain / 60000) % 60) + " min " : "") + (remain / 1000 % 60)
  480. + " sec";
  481. print("Progress: " + (100 * count) / totalComparisons + "%\nTime used for comparisons: "
  482. + totalTimeStr
  483. + "\nRemaining time (estimate): " + remainTime + "\n", null);
  484. // compare A to B
  485. startTime = System.currentTimeMillis();
  486. print("Comparing block A (" + startA + "-" + endA + ") to block B (" + startB + "-" + endB
  487. + ")\n", null);
  488. for (i = startB; i <= endB; i++) {
  489. s1 = submissions.elementAt(i);
  490. if (s1.struct == null) {
  491. count += (endA - startA + 1);
  492. continue;
  493. }
  494. for (j = startA; j <= endA; j++) {
  495. s2 = submissions.elementAt(j);
  496. if (s2.struct == null) {
  497. count++;
  498. continue;
  499. }
  500. match = this.gSTiling.compare(s1, s2);
  501. registerMatch(match, dist, avgmatches, maxmatches, null, i, j);
  502. comparisons++;
  503. count++;
  504. }
  505. s1.struct = null; // remove B
  506. }
  507. print("\n", null);
  508. totalTime += System.currentTimeMillis() - startTime;
  509. if (endB == size - 1) {
  510. totalTimeStr = "" + ((totalTime / 3600000 > 0) ? (totalTime / 3600000) + " h " : "")
  511. + ((totalTime / 60000 > 0) ? ((totalTime / 60000) % 60) + " min " : "") + (
  512. totalTime / 1000 % 60) + " sec";
  513. remain = totalTime * (totalComparisons - count) / comparisons;
  514. remainTime = "" + ((remain / 3600000 > 0) ? (remain / 3600000) + " h " : "")
  515. + ((remain / 60000 > 0) ? ((remain / 60000) % 60) + " min " : "") + (remain / 1000
  516. % 60) + " sec";
  517. print("Progress: " + (100 * count) / totalComparisons + "%\nTime used for comparisons: "
  518. + totalTimeStr
  519. + "\nRemaining time (estimate): " + remainTime + "\n", null);
  520. break;
  521. }
  522. runtime.runFinalization();
  523. runtime.gc();
  524. Thread.yield();
  525. // Try to find the next B
  526. print("Finding next B\n", null);
  527. index = fillMemory(endB + 1, size);
  528. startB = endB + 1;
  529. endB = index;
  530. } while (true);
  531. // Remove A
  532. for (i = startA; i <= endA; i++) {
  533. submissions.elementAt(i).struct = null;
  534. }
  535. runtime.runFinalization();
  536. runtime.gc();
  537. Thread.yield();
  538. print("Find next A.\n", null);
  539. // First try to load as many submissions as possible
  540. index = fillMemory(endA + 1, size);
  541. if (index != size - 1) {
  542. startA = endA + 1;
  543. endA = startA + (index - startA + 1) / 2;
  544. startB = endA + 1;
  545. endB = index;
  546. } else {
  547. startA = startB; // last block
  548. endA = endB = index;
  549. }
  550. } while (true);
  551. totalTime += System.currentTimeMillis() - startTime;
  552. totalTimeStr = "" + ((totalTime / 3600000 > 0) ? (totalTime / 3600000) + " h " : "")
  553. + ((totalTime / 60000 > 0) ? ((totalTime / 60000) % 60000) + " min " : "") + (
  554. totalTime / 1000 % 60) + " sec";
  555. print("Total comparison time: " + totalTimeStr + "\nComparisons: " + count + "/" + comparisons
  556. + "/" + totalComparisons + "\n",
  557. null);
  558. // free remaining memory
  559. for (i = startA; i <= endA; i++) {
  560. submissions.elementAt(i).struct = null;
  561. }
  562. runtime.runFinalization();
  563. runtime.gc();
  564. Thread.yield();
  565. Cluster cluster = null;
  566. if (options.getClusterType() == JPlagClusterType.NONE) {
  567. cluster = this.clusters.calculateClustering(submissions);
  568. }
  569. // TODO: Replace writeResults(...)
  570. // Deprecated:
  571. // writeResults(dist, avgmatches, maxmatches, null, cluster);
  572. }
  573. /*
  574. * Now the special comparison
  575. * TODO: Previously, this comparison created a `Report.java` (removed)
  576. * TODO: Check whether this comparison is now any different than the others after the report has been removed
  577. */
  578. private void specialCompare() {
  579. File root = new File(options.getResultDir());
  580. int size = submissions.size();
  581. int matchIndex = 0;
  582. print("Comparing: ", countValidSubmissions() + " submissions");
  583. print("\n(Writing results at the same time.)\n", null);
  584. int totalcomps = size * size;
  585. int i, j, anz = 0, count = 0;
  586. AllMatches match;
  587. Submission s1, s2;
  588. long msec = System.currentTimeMillis();
  589. for (i = 0; i < (size - 1); i++) {
  590. // Result vector
  591. SortedVector<AllMatches> matches = new SortedVector<>(
  592. new AllMatches.AvgComparator());
  593. s1 = submissions.elementAt(i);
  594. if (s1.struct == null) {
  595. count += (size - 1);
  596. continue;
  597. }
  598. for (j = 0; j < size; j++) {
  599. s2 = submissions.elementAt(j);
  600. if ((i == j) || (s2.struct == null)) {
  601. count++;
  602. continue;
  603. }
  604. match = this.gSTiling.compare(s1, s2);
  605. anz++;
  606. float percent = match.percent();
  607. if ((matches.size() < options.compare || matches.size() == 0 || match
  608. .moreThan(matches.lastElement().percent()))
  609. && match.moreThan(0)) {
  610. matches.insert(match);
  611. if (matches.size() > options.compare) {
  612. matches.removeElementAt(options.compare);
  613. }
  614. }
  615. if (options.getClusterType() != JPlagClusterType.NONE) {
  616. options.similarity.setSimilarity(i, j, percent);
  617. }
  618. count++;
  619. }
  620. }
  621. long time = System.currentTimeMillis() - msec;
  622. print("\n", "Total time: " + ((time / 3600000 > 0) ? (time / 3600000) + " h " : "")
  623. + ((time / 60000 > 0) ? ((time / 60000) % 60000) + " min " : "") + (time / 1000 % 60)
  624. + " sec\n" + "Time per comparison: "
  625. + (time / anz) + " msec\n");
  626. }
  627. /**
  628. * Parse all submissions in the given `options.rootDir`. This method is called from
  629. * Program.run(...).
  630. */
  631. private void createSubmissions() throws jplag.ExitException {
  632. submissions = new Vector<>();
  633. // ------------------------------------------------------------------------
  634. File rootDir = new File(options.getRootDir());
  635. if (!rootDir.isDirectory()) {
  636. throw new jplag.ExitException("\"" + options.getResultDir() + "\" is not a directory!");
  637. }
  638. // ------------------------------------------------------------------------
  639. String[] fileNamesInRootDir;
  640. try {
  641. fileNamesInRootDir = rootDir.list();
  642. } catch (SecurityException e) {
  643. throw new jplag.ExitException(
  644. "Unable to retrieve directory: " + options.getRootDir() + " Cause : " + e.toString());
  645. }
  646. Arrays.sort(fileNamesInRootDir);
  647. // ------------------------------------------------------------------------
  648. for (String fileName : fileNamesInRootDir) {
  649. File subm_dir = new File(rootDir, fileName);
  650. if (!subm_dir.isDirectory()) {
  651. if (options.getSubDir() != null) {
  652. continue;
  653. }
  654. boolean hasValidSuffix = false;
  655. String name = subm_dir.getName();
  656. for (String suffix : options.getFileSuffixes()) {
  657. if (name.endsWith(suffix)) {
  658. hasValidSuffix = true;
  659. break;
  660. }
  661. }
  662. if (!hasValidSuffix) {
  663. continue;
  664. }
  665. submissions.addElement(new Submission(name, rootDir, this, options.getLanguageInstance()));
  666. } else if (options.exp && isFileExcluded(subm_dir.toString())) {
  667. // EXPERIMENT !!
  668. System.err.println("excluded: " + subm_dir);
  669. } else {
  670. File file_dir = ((options.getSubDir() == null) ?
  671. subm_dir : new File(subm_dir, options.getSubDir()));
  672. if (file_dir.isDirectory()) {
  673. if (options.getBaseCode().equals(subm_dir.getName())) {
  674. baseCodeSubmission = new Submission(
  675. subm_dir.getName(),
  676. file_dir,
  677. options.isRecursive(),
  678. this,
  679. options.getLanguageInstance()
  680. );
  681. } else {
  682. submissions.addElement(new Submission(
  683. subm_dir.getName(),
  684. file_dir,
  685. options.isRecursive(),
  686. this,
  687. options.getLanguageInstance()
  688. ));
  689. }
  690. } else {
  691. throw new ExitException("Cannot find directory: " + file_dir.toString());
  692. }
  693. }
  694. }
  695. }
  696. /**
  697. * Parse submissions from the given `options.fileList` only. This method is called from
  698. * Program.run(...).
  699. */
  700. private void createSubmissionsFileList() throws jplag.ExitException {
  701. submissions = new Vector<>();
  702. File rootDir = null;
  703. if (options.getRootDir() != null) {
  704. rootDir = new File(options.getRootDir());
  705. if (!rootDir.isDirectory()) {
  706. throw new jplag.ExitException(options.getRootDir() + " is not a directory!");
  707. }
  708. }
  709. for (String file : options.getFileList()) {
  710. submissions.addElement(new Submission(file, rootDir, this, options.getLanguageInstance()));
  711. }
  712. }
  713. /**
  714. * THIS IS FOR THE EMPIRICAL STUDY
  715. */
  716. private void createSubmissionsExp() throws jplag.ExitException {
  717. // ES IST SICHER, DASS EIN INCLUDE-FILE ANGEGEBEN WURDE!
  718. readIncludeFile();
  719. submissions = new Vector<>();
  720. // ------------------------------------------------------------------------
  721. File rootDir = new File(options.getRootDir());
  722. if (!rootDir.isDirectory()) {
  723. throw new jplag.ExitException(options.getRootDir() + " is not a directory!");
  724. }
  725. // ------------------------------------------------------------------------
  726. String[] list = new String[includedFileNames.size()];
  727. includedFileNames.copyInto(list);
  728. // ------------------------------------------------------------------------
  729. for (String s : list) {
  730. File submissionDir = new File(rootDir, s);
  731. if (!submissionDir.isDirectory()) {
  732. continue;
  733. }
  734. if (options.exp && isFileExcluded(submissionDir.toString())) { // EXPERIMENT
  735. System.err.println("excluded: " + submissionDir);
  736. continue;
  737. }
  738. File file_dir = ((options.getSubDir() == null) ? submissionDir
  739. : new File(submissionDir, options.getSubDir()));
  740. if (file_dir.isDirectory()) {
  741. submissions.addElement(new Submission(
  742. submissionDir.getName(),
  743. file_dir,
  744. options.isRecursive(),
  745. this,
  746. this.options.getLanguageInstance()
  747. ));
  748. } else if (options.getSubDir() == null) {
  749. throw new ExitException(options.getRootDir() + " is not a directory!");
  750. }
  751. }
  752. }
  753. /**
  754. * Check if a file is excluded or not.
  755. */
  756. protected boolean isFileExcluded(String file) {
  757. if (excludedFileNames == null) {
  758. return false;
  759. }
  760. for (String s : excludedFileNames) {
  761. if (file.endsWith(s)) {
  762. return true;
  763. }
  764. }
  765. return false;
  766. }
  767. private int fillMemory(int from, int size) {
  768. Submission sub = null;
  769. int index = from;
  770. runtime.runFinalization();
  771. runtime.gc();
  772. Thread.yield();
  773. long freeBefore = runtime.freeMemory();
  774. try {
  775. for (; index < size; index++) {
  776. sub = submissions.elementAt(index);
  777. sub.struct = new Structure();
  778. if (!sub.struct.load(new File("temp", sub.dir.getName() + sub.name))) {
  779. sub.struct = null;
  780. }
  781. }
  782. } catch (java.lang.OutOfMemoryError e) {
  783. sub.struct = null;
  784. print("Memory overflow after loading " + (index - from + 1) + " submissions.\n", null);
  785. }
  786. if (index >= size) {
  787. index = size - 1;
  788. }
  789. if (freeBefore / runtime.freeMemory() <= 2) {
  790. return index;
  791. }
  792. for (int i = (index - from) / 2; i > 0; i--) {
  793. submissions.elementAt(index--).struct = null;
  794. }
  795. runtime.runFinalization();
  796. runtime.gc();
  797. Thread.yield();
  798. // make sure we freed half of the "available" memory.
  799. long free;
  800. while (freeBefore / (free = runtime.freeMemory()) > 2) {
  801. submissions.elementAt(index--).struct = null;
  802. runtime.runFinalization();
  803. runtime.gc();
  804. Thread.yield();
  805. }
  806. print(free / 1024 / 1024 + "MByte freed. Current index: " + index + "\n", null);
  807. return index;
  808. }
  809. private void makeTempDir() throws jplag.ExitException {
  810. print(null, "Creating temporary dir.\n");
  811. File f = new File("temp");
  812. if (!f.exists()) {
  813. if (!f.mkdirs()) {
  814. throw new jplag.ExitException("Cannot create temporary directory!");
  815. }
  816. }
  817. if (!f.isDirectory()) {
  818. throw new ExitException("'temp' is not a directory!");
  819. }
  820. if (!f.canWrite()) {
  821. throw new ExitException("Cannot write directory: 'temp'");
  822. }
  823. }
  824. private void myWrite(String str) {
  825. if (writer != null) {
  826. try {
  827. writer.write(str);
  828. } catch (IOException e) {
  829. e.printStackTrace();
  830. }
  831. } else {
  832. System.out.print(str);
  833. }
  834. }
  835. /*
  836. * Compiles all "submissions"
  837. */
  838. private void parseAll() throws jplag.ExitException {
  839. if (submissions == null) {
  840. System.out.println("Nothing to parse!");
  841. return;
  842. }
  843. // lets go:)
  844. int count = 0;
  845. int totalcount = submissions.size();
  846. long msec = System.currentTimeMillis();
  847. Iterator<Submission> iter = submissions.iterator();
  848. if (options.isUseExternalSearch()) {
  849. makeTempDir();
  850. }
  851. int invalid = 0;
  852. while (iter.hasNext()) {
  853. boolean ok;
  854. boolean removed = false;
  855. Submission subm = iter.next();
  856. print(null, "------ Parsing submission: " + subm.name + "\n");
  857. currentSubmissionName = subm.name;
  858. if (!(ok = subm.parse())) {
  859. errors++;
  860. }
  861. if (options.exp && options.getFilter() != null) {
  862. subm.struct = options.getFilter().filter(subm.struct); // EXPERIMENT
  863. }
  864. count++;
  865. if (subm.struct != null && subm.size() < options.getMinTokenMatch()) {
  866. print(null, "Submission contains fewer tokens than minimum match " + "length allows!\n");
  867. subm.struct = null;
  868. invalid++;
  869. removed = true;
  870. }
  871. if (options.isUseExternalSearch()) {
  872. if (subm.struct != null) {
  873. this.gSTiling.create_hashes(subm.struct, options.getMinTokenMatch(), false);
  874. subm.struct.save(new File("temp", subm.dir.getName() + subm.name));
  875. subm.struct = null;
  876. }
  877. }
  878. if (!options.isUseExternalSearch() && subm.struct == null) {
  879. invalidSubmissionNames = (invalidSubmissionNames == null) ? subm.name
  880. : invalidSubmissionNames + " - " + subm.name;
  881. iter.remove();
  882. }
  883. if (ok && !removed) {
  884. print(null, "OK\n");
  885. } else {
  886. print(null, "ERROR -> Submission removed\n");
  887. }
  888. }
  889. print("\n" + (count - errors - invalid) + " submissions parsed successfully!\n" + errors
  890. + " parser error"
  891. + (errors != 1 ? "s!\n" : "!\n"), null);
  892. if (invalid != 0) {
  893. print(null, invalid
  894. + ((invalid == 1) ? " submission is not valid because it contains"
  895. : " submissions are not valid because they contain")
  896. + " fewer tokens\nthan minimum match length allows.\n");
  897. }
  898. long time = System.currentTimeMillis() - msec;
  899. print("\n\n",
  900. "\nTotal time for parsing: " + ((time / 3600000 > 0) ? (time / 3600000) + " h " : "")
  901. + ((time / 60000 > 0) ? ((time / 60000) % 60000) + " min " : "") + (time / 1000 % 60)
  902. + " sec\n"
  903. + "Time per parsed submission: " + (count > 0 ? (time / count) : "n/a") + " msec\n\n");
  904. }
  905. private void parseBaseCodeSubmission() throws jplag.ExitException {
  906. Submission subm = baseCodeSubmission;
  907. if (subm == null) {
  908. // TODO:
  909. // options.useBasecode = false;
  910. return;
  911. }
  912. long msec = System.currentTimeMillis();
  913. print("----- Parsing basecode submission: " + subm.name + "\n", null);
  914. // lets go:
  915. if (options.isUseExternalSearch()) {
  916. makeTempDir();
  917. }
  918. if (!subm.parse()) {
  919. throwBadBaseCodeSubmission();
  920. }
  921. if (options.exp && options.getFilter() != null) {
  922. subm.struct = options.getFilter().filter(subm.struct); // EXPERIMENT
  923. }
  924. if (subm.struct != null && subm.size() < options.getMinTokenMatch()) {
  925. throw new ExitException(
  926. "Basecode submission contains fewer tokens " + "than minimum match length allows!\n");
  927. }
  928. if (options.hasBaseCode()) {
  929. gSTiling.create_hashes(subm.struct, options.getMinTokenMatch(), true);
  930. }
  931. if (options.isUseExternalSearch()) {
  932. if (subm.struct != null) {
  933. gSTiling.create_hashes(subm.struct, options.getMinTokenMatch(), false);
  934. subm.struct.save(new File("temp", subm.dir.getName() + subm.name));
  935. subm.struct = null;
  936. }
  937. }
  938. print("\nBasecode submission parsed!\n", null);
  939. long time = System.currentTimeMillis() - msec;
  940. print("\n",
  941. "\nTime for parsing Basecode: " + ((time / 3600000 > 0) ? (time / 3600000) + " h " : "")
  942. + ((time / 60000 > 0) ? ((time / 60000) % 60000) + " min " : "") + (time / 1000 % 60)
  943. + " sec\n");
  944. }
  945. /*
  946. * If an exclusion file is given, it is read in and all stings are saved in
  947. * the set "excluded".
  948. */
  949. private void readExclusionFile() {
  950. if (options.getExcludeFile() == null) {
  951. return;
  952. }
  953. excludedFileNames = new HashSet<>();
  954. try {
  955. BufferedReader in = new BufferedReader(new FileReader(options.getExcludeFile()));
  956. String line;
  957. while ((line = in.readLine()) != null) {
  958. excludedFileNames.add(line.trim());
  959. }
  960. in.close();
  961. } catch (IOException e) {
  962. System.out.println("Could not read exclusion file: " + options.getExcludeFile());
  963. }
  964. if (options.getVerbosity() == LONG) {
  965. print(null, "Excluded files:\n");
  966. for (String excludedFileName : excludedFileNames) {
  967. print(null, " " + excludedFileName + "\n");
  968. }
  969. }
  970. }
  971. /*
  972. * If an include file is given, read it in and store all the strings in
  973. * "included".
  974. */
  975. private void readIncludeFile() {
  976. if (options.getIncludeFile() == null) {
  977. return;
  978. }
  979. includedFileNames = new Vector<>();
  980. try {
  981. BufferedReader in = new BufferedReader(new FileReader(options.getIncludeFile()));
  982. String line;
  983. while ((line = in.readLine()) != null) {
  984. includedFileNames.addElement(line.trim());
  985. }
  986. in.close();
  987. } catch (IOException e) {
  988. System.out.println("Could not read include file: " + options.getIncludeFile());
  989. }
  990. if (options.getVerbosity() == LONG) {
  991. print(null, "Included dirs:\n");
  992. Enumeration<String> enum1 = includedFileNames.elements();
  993. while (enum1.hasMoreElements()) {
  994. print(null, " " + enum1.nextElement() + "\n");
  995. }
  996. }
  997. }
  998. private void registerMatch(
  999. AllMatches match,
  1000. int[] dist,
  1001. SortedVector<AllMatches> avgMatches,
  1002. SortedVector<AllMatches> maxMatches,
  1003. SortedVector<AllMatches> minMatches,
  1004. int a,
  1005. int b) {
  1006. float avgPercent = match.percent();
  1007. float maxPercent = match.percentMaxAB();
  1008. float minPercent = match.percentMinAB();
  1009. dist[((((int) avgPercent) / 10) == 10) ? 9 : (((int) avgPercent) / 10)]++;
  1010. if (!options.isStorePercent()) {
  1011. if ((avgMatches.size() < options.getStoreMatches() || avgPercent > avgMatches.lastElement()
  1012. .percent()) && avgPercent > 0) {
  1013. avgMatches.insert(match);
  1014. if (avgMatches.size() > options.getStoreMatches()) {
  1015. avgMatches.removeElementAt(options.getStoreMatches());
  1016. }
  1017. }
  1018. if (maxMatches != null && (maxMatches.size() < options.getStoreMatches()
  1019. || maxPercent > maxMatches.lastElement().percent())
  1020. && maxPercent > 0) {
  1021. maxMatches.insert(match);
  1022. if (maxMatches.size() > options.getStoreMatches()) {
  1023. maxMatches.removeElementAt(options.getStoreMatches());
  1024. }
  1025. }
  1026. if (minMatches != null && (minMatches.size() < options.getStoreMatches()
  1027. || minPercent > minMatches.lastElement().percent())
  1028. && minPercent > 0) {
  1029. minMatches.insert(match);
  1030. if (minMatches.size() > options.getStoreMatches()) {
  1031. minMatches.removeElementAt(options.getStoreMatches());
  1032. }
  1033. }
  1034. } else { // store_percent
  1035. if (avgPercent > options.getStoreMatches()) {
  1036. avgMatches.insert(match);
  1037. if (avgMatches.size() > JPlagOptions.MAX_RESULT_PAIRS) {
  1038. avgMatches.removeElementAt(JPlagOptions.MAX_RESULT_PAIRS);
  1039. }
  1040. }
  1041. if (maxMatches != null && maxPercent > options.getStoreMatches()) {
  1042. maxMatches.insert(match);
  1043. if (maxMatches.size() > JPlagOptions.MAX_RESULT_PAIRS) {
  1044. maxMatches.removeElementAt(JPlagOptions.MAX_RESULT_PAIRS);
  1045. }
  1046. }
  1047. if (minMatches != null && minPercent > options.getStoreMatches()) {
  1048. minMatches.insert(match);
  1049. if (minMatches.size() > JPlagOptions.MAX_RESULT_PAIRS) {
  1050. minMatches.removeElementAt(JPlagOptions.MAX_RESULT_PAIRS);
  1051. }
  1052. }
  1053. }
  1054. if (options.getClusterType() != JPlagClusterType.NONE) {
  1055. options.similarity.setSimilarity(a, b, avgPercent);
  1056. }
  1057. }
  1058. }