ParserAdapter.java 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package de.jplag.text;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.nio.file.Files;
  5. import java.nio.file.Path;
  6. import java.util.HashMap;
  7. import java.util.Map;
  8. import java.util.Properties;
  9. import de.jplag.AbstractParser;
  10. import de.jplag.TokenConstants;
  11. import de.jplag.TokenList;
  12. import edu.stanford.nlp.ling.CoreLabel;
  13. import edu.stanford.nlp.pipeline.CoreDocument;
  14. import edu.stanford.nlp.pipeline.StanfordCoreNLP;
  15. public class ParserAdapter extends AbstractParser {
  16. private static final char LF = '\n';
  17. private static final char CR = '\r';
  18. private static final String ANNOTATORS_KEY = "annotators";
  19. private static final String ANNOTATORS_VALUE = "tokenize";
  20. private final Map<String, Integer> tokenTypes = new HashMap<>();
  21. private final StanfordCoreNLP pipeline;
  22. private int tokenTypeIndex = 2; // 0 is FILE_END token, 1 is SEPARATOR_TOKEN, so start at 2.
  23. private TokenList tokens;
  24. private String currentFile;
  25. private int currentLine;
  26. /**
  27. * The position of the current line break in the content string
  28. */
  29. private int currentLineBreakIndex;
  30. public ParserAdapter() {
  31. Properties properties = new Properties();
  32. properties.put(ANNOTATORS_KEY, ANNOTATORS_VALUE);
  33. this.pipeline = new StanfordCoreNLP(properties);
  34. }
  35. public TokenList parse(File directory, String[] files) {
  36. tokens = new TokenList();
  37. errors = 0;
  38. for (String file : files) {
  39. logger.trace("Parsing file {}", file);
  40. if (!parseFile(directory, file)) {
  41. errors++;
  42. }
  43. tokens.addToken(new TextToken(TokenConstants.FILE_END, file));
  44. }
  45. return tokens;
  46. }
  47. private boolean parseFile(File directory, String file) {
  48. this.currentFile = file;
  49. this.currentLine = 1; // lines start at 1
  50. this.currentLineBreakIndex = 0;
  51. Path filePath = directory.toPath().resolve(file);
  52. String content = readFile(filePath);
  53. if (content == null) {
  54. return false;
  55. }
  56. int lastTokenEnd = 0;
  57. CoreDocument coreDocument = pipeline.processToCoreDocument(content);
  58. for (CoreLabel token : coreDocument.tokens()) {
  59. advanceLineBreaks(content, lastTokenEnd, token.beginPosition());
  60. lastTokenEnd = token.endPosition();
  61. if (isWord(token)) {
  62. addToken(token);
  63. }
  64. }
  65. return true;
  66. }
  67. /**
  68. * Scan for line breaks and increase {@link #currentLine} and {@link #currentLineBreakIndex} accordingly.
  69. * @param content the file content
  70. * @param lastTokenEnd the end position of the last token
  71. * @param nextTokenBegin the begin position of the next token
  72. */
  73. private void advanceLineBreaks(String content, int lastTokenEnd, int nextTokenBegin) {
  74. for (int i = lastTokenEnd; i < nextTokenBegin; i++) {
  75. if (content.charAt(i) == LF) {
  76. currentLine++;
  77. currentLineBreakIndex = i;
  78. } else if (content.charAt(i) == CR) {
  79. if (i + 1 < content.length() && content.charAt(i + 1) == LF) { // CRLF
  80. i++; // skip following LF
  81. }
  82. currentLine++;
  83. currentLineBreakIndex = i;
  84. }
  85. }
  86. }
  87. private boolean isWord(CoreLabel token) {
  88. // consider a token as a word if it contains any alphanumeric character
  89. String text = token.originalText();
  90. return text.chars().anyMatch(it -> Character.isAlphabetic(it) || Character.isDigit(it));
  91. }
  92. private void addToken(CoreLabel label) {
  93. String text = label.originalText();
  94. int type = getTokenType(text);
  95. int column = label.beginPosition() - currentLineBreakIndex;
  96. int length = label.endPosition() - label.beginPosition();
  97. tokens.addToken(new TextToken(text, type, currentFile, currentLine, column, length));
  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. }