ParserAdapter.java 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package de.jplag.text;
  2. import de.jplag.AbstractParser;
  3. import de.jplag.TokenConstants;
  4. import de.jplag.TokenList;
  5. import edu.stanford.nlp.ling.CoreLabel;
  6. import edu.stanford.nlp.pipeline.CoreDocument;
  7. import edu.stanford.nlp.pipeline.StanfordCoreNLP;
  8. import java.io.File;
  9. import java.io.IOException;
  10. import java.nio.file.Files;
  11. import java.nio.file.Path;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. import java.util.Properties;
  15. public class ParserAdapter extends AbstractParser {
  16. private final Map<String, Integer> tokenTypes = new HashMap<>();
  17. private final StanfordCoreNLP pipeline;
  18. private int tokenTypeIndex = 1; // 0 is FILE_END token, SEPARATOR is not used as there are no methods.
  19. private TokenList tokens;
  20. private String currentFile;
  21. private int currentLine;
  22. private int currentLineIndex;
  23. public ParserAdapter() {
  24. Properties properties = new Properties();
  25. properties.put("annotators", "tokenize");
  26. this.pipeline = new StanfordCoreNLP(properties);
  27. }
  28. public TokenList parse(File directory, String[] files) {
  29. tokens = new TokenList();
  30. errors = 0;
  31. for (String file : files) {
  32. logger.trace("Parsing file {}", file);
  33. if (!parseFile(directory, file)) {
  34. errors++;
  35. }
  36. tokens.addToken(new TextToken(TokenConstants.FILE_END, file));
  37. }
  38. return tokens;
  39. }
  40. private boolean parseFile(File directory, String file) {
  41. this.currentFile = file;
  42. this.currentLine = 1; // lines start at 1
  43. this.currentLineIndex = 0;
  44. Path filePath = directory.toPath().resolve(file);
  45. String content = readFile(filePath);
  46. if (content == null) {
  47. return false;
  48. }
  49. int lastTokenEnd = 0;
  50. CoreDocument coreDocument = pipeline.processToCoreDocument(content);
  51. for (CoreLabel token : coreDocument.tokens()) {
  52. advanceLineBreaks(content, lastTokenEnd, token.beginPosition());
  53. lastTokenEnd = token.endPosition();
  54. if (isWord(token)) {
  55. addToken(token);
  56. }
  57. }
  58. return true;
  59. }
  60. /**
  61. * Scan for line breaks and increase {@link #currentLine} and {@link #currentLineIndex} accordingly.
  62. * @param content the file content
  63. * @param lastTokenEnd the end position of the last token
  64. * @param nextTokenBegin the begin position of the next token
  65. */
  66. private void advanceLineBreaks(String content, int lastTokenEnd, int nextTokenBegin) {
  67. for (int i = lastTokenEnd; i < nextTokenBegin; i++) {
  68. if (content.charAt(i) == '\n') { // LF
  69. currentLine++;
  70. currentLineIndex = i;
  71. } else if (content.charAt(i) == '\r') { // CR
  72. if (i + 1 < content.length() && content.charAt(i + 1) == '\n') { // CRLF
  73. i++; // skip following LF
  74. }
  75. currentLine++;
  76. currentLineIndex = i;
  77. }
  78. }
  79. }
  80. private boolean isWord(CoreLabel token) {
  81. // consider a token as a word if it contains any alphanumeric character
  82. String text = token.originalText();
  83. for (int i = 0; i < text.length(); i++) {
  84. char c = text.charAt(i);
  85. if (Character.isAlphabetic(c) || Character.isDigit(c)) {
  86. return true;
  87. }
  88. }
  89. return false;
  90. }
  91. private void addToken(CoreLabel label) {
  92. String text = label.originalText();
  93. int type = getTokenType(text);
  94. int column = label.beginPosition() - currentLineIndex;
  95. int length = label.endPosition() - label.beginPosition();
  96. TokenPosition position = new TokenPosition(currentLine, column, length);
  97. tokens.addToken(new TextToken(text, type, currentFile, position));
  98. }
  99. private String readFile(Path filePath) {
  100. try {
  101. return Files.readString(filePath);
  102. } catch (IOException e) {
  103. logger.error("Error reading from file {}", filePath, e);
  104. return null;
  105. }
  106. }
  107. private int getTokenType(String text) {
  108. text = text.toLowerCase();
  109. tokenTypes.computeIfAbsent(text, it -> {
  110. if (tokenTypeIndex == Integer.MAX_VALUE) {
  111. throw new IllegalStateException("Too many token types, should not happen!");
  112. }
  113. return ++tokenTypeIndex;
  114. });
  115. return tokenTypes.get(text);
  116. }
  117. }