diff options
Diffstat (limited to 'javaparser-core/src/main/java/com/github/javaparser/JavadocParser.java')
-rw-r--r-- | javaparser-core/src/main/java/com/github/javaparser/JavadocParser.java | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/javaparser-core/src/main/java/com/github/javaparser/JavadocParser.java b/javaparser-core/src/main/java/com/github/javaparser/JavadocParser.java new file mode 100644 index 000000000..bbe9797e2 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/JavadocParser.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. + * Copyright (C) 2011, 2013-2016 The JavaParser Team. + * + * This file is part of JavaParser. + * + * JavaParser can be used either under the terms of + * a) the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * b) the terms of the Apache License + * + * You should have received a copy of both licenses in LICENCE.LGPL and + * LICENCE.APACHE. Please refer to those files for details. + * + * JavaParser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +package com.github.javaparser; + +import com.github.javaparser.ast.comments.JavadocComment; +import com.github.javaparser.javadoc.Javadoc; +import com.github.javaparser.javadoc.JavadocBlockTag; +import com.github.javaparser.javadoc.description.JavadocDescription; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static com.github.javaparser.utils.Utils.*; +import static com.github.javaparser.utils.Utils.nextWord; + +/** + * The class responsible for parsing the content of JavadocComments and produce JavadocDocuments. + */ +class JavadocParser { + + private static String BLOCK_TAG_PREFIX = "@"; + private static Pattern BLOCK_PATTERN = Pattern.compile("^" + BLOCK_TAG_PREFIX, Pattern.MULTILINE); + + public static Javadoc parse(JavadocComment comment) { + return parse(comment.getContent()); + } + + public static Javadoc parse(String commentContent) { + List<String> cleanLines = cleanLines(normalizeEolInTextBlock(commentContent, EOL)); + int indexOfFirstBlockTag = cleanLines.stream() + .filter(JavadocParser::isABlockLine) + .map(cleanLines::indexOf) + .findFirst() + .orElse(-1); + List<String> blockLines; + String descriptionText; + if (indexOfFirstBlockTag == -1) { + descriptionText = trimRight(String.join(EOL, cleanLines)); + blockLines = Collections.emptyList(); + } else { + descriptionText = trimRight(String.join(EOL, cleanLines.subList(0, indexOfFirstBlockTag))); + + //Combine cleaned lines, but only starting with the first block tag till the end + //In this combined string it is easier to handle multiple lines which actually belong together + String tagBlock = cleanLines.subList(indexOfFirstBlockTag, cleanLines.size()) + .stream() + .collect(Collectors.joining(EOL)); + + //Split up the entire tag back again, considering now that some lines belong to the same block tag. + //The pattern splits the block at each new line starting with the '@' symbol, thus the symbol + //then needs to be added again so that the block parsers handles everything correctly. + blockLines = BLOCK_PATTERN + .splitAsStream(tagBlock) + .filter(STRING_NOT_EMPTY) + .map(s -> BLOCK_TAG_PREFIX + s) + .collect(Collectors.toList()); + } + Javadoc document = new Javadoc(JavadocDescription.parseText(descriptionText)); + blockLines.forEach(l -> document.addBlockTag(parseBlockTag(l))); + return document; + } + + private static JavadocBlockTag parseBlockTag(String line) { + line = line.trim().substring(1); + String tagName = nextWord(line); + String rest = line.substring(tagName.length()).trim(); + return new JavadocBlockTag(tagName, rest); + } + + private static boolean isABlockLine(String line) { + return line.trim().startsWith(BLOCK_TAG_PREFIX); + } + + private static String trimRight(String string) { + while (!string.isEmpty() && Character.isWhitespace(string.charAt(string.length() - 1))) { + string = string.substring(0, string.length() - 1); + } + return string; + } + + private static List<String> cleanLines(String content) { + String[] lines = content.split(EOL); + List<String> cleanedLines = Arrays.stream(lines).map(l -> { + int asteriskIndex = startsWithAsterisk(l); + if (asteriskIndex == -1) { + return l; + } else { + // if a line starts with space followed by an asterisk drop to the asterisk + // if there is a space immediately after the asterisk drop it also + if (l.length() > (asteriskIndex + 1)) { + + char c = l.charAt(asteriskIndex + 1); + if (c == ' ' || c == '\t') { + return l.substring(asteriskIndex + 2); + } + } + return l.substring(asteriskIndex + 1); + } + }).collect(Collectors.toList()); + // lines containing only whitespace are normalized to empty lines + cleanedLines = cleanedLines.stream().map(l -> l.trim().isEmpty() ? "" : l).collect(Collectors.toList()); + // if the first starts with a space, remove it + if (!cleanedLines.get(0).isEmpty() && (cleanedLines.get(0).charAt(0) == ' ' || cleanedLines.get(0).charAt(0) == '\t')) { + cleanedLines.set(0, cleanedLines.get(0).substring(1)); + } + // drop empty lines at the beginning and at the end + while (cleanedLines.size() > 0 && cleanedLines.get(0).trim().isEmpty()) { + cleanedLines = cleanedLines.subList(1, cleanedLines.size()); + } + while (cleanedLines.size() > 0 && cleanedLines.get(cleanedLines.size() - 1).trim().isEmpty()) { + cleanedLines = cleanedLines.subList(0, cleanedLines.size() - 1); + } + return cleanedLines; + } + + // Visible for testing + static int startsWithAsterisk(String line) { + if (line.startsWith("*")) { + return 0; + } else if ((line.startsWith(" ") || line.startsWith("\t")) && line.length() > 1) { + int res = startsWithAsterisk(line.substring(1)); + if (res == -1) { + return -1; + } else { + return 1 + res; + } + } else { + return -1; + } + } +} |