| |
| import com.sun.source.doctree.*; |
| import com.sun.source.util.DocTrees; |
| import java.util.HashMap; |
| import java.util.Map.Entry; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Iterator; |
| import java.io.BufferedWriter; |
| import java.io.OutputStreamWriter; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.util.*; |
| import java.util.spi.ToolProvider; |
| import javax.lang.model.*; |
| import javax.lang.model.element.*; |
| import javax.lang.model.util.*; |
| import jdk.javadoc.doclet.*; |
| |
| |
| public class CommentParser implements Doclet { |
| private static Map<String, String> m_parsedComments = new HashMap<String, String>(); |
| |
| // We need to implement these base class pure virtual methods. |
| |
| @Override |
| public void init(Locale locale, Reporter reporter) { |
| } |
| |
| @Override |
| public Set<? extends Option> getSupportedOptions() { |
| return new HashSet<>(); |
| } |
| |
| @Override |
| public SourceVersion getSupportedSourceVersion() { |
| return SourceVersion.latest(); |
| } |
| |
| @Override |
| public String getName() { |
| return "CommentParser"; |
| } |
| |
| // Element name must be the fully qualified name of the element. |
| // |
| // If there is no comment associated with this element, simply do nothing. |
| private void storeCommentFor(DocTrees docTrees, String fullName, Element e) { |
| DocCommentTree docCommentTree = docTrees.getDocCommentTree(e); |
| if (docCommentTree == null) |
| return; |
| |
| StringBuilder name = new StringBuilder(fullName); |
| |
| // We must use signature in the key for methods for compatibility with |
| // the existing tests and to allow distinguishing between overloaded |
| // methods. |
| if (e instanceof ExecutableElement) { |
| ExecutableElement ex = (ExecutableElement)e; |
| name.append("("); |
| |
| boolean firstParam = true; |
| for (VariableElement p : ex.getParameters()) { |
| if (firstParam) { |
| firstParam = false; |
| } else { |
| name.append(", "); |
| } |
| |
| name.append(p.asType().toString()); |
| } |
| |
| name.append(")"); |
| } |
| |
| // For some reason the comment in the source is split into "body" and |
| // "block tags" parts, so we need to concatenate them back together. |
| StringBuilder comment = new StringBuilder(); |
| for (DocTree d : docCommentTree.getFullBody()) { |
| comment.append(d.toString()); |
| comment.append("\n"); |
| } |
| |
| boolean firstBlockTag = true; |
| for (DocTree d : docCommentTree.getBlockTags()) { |
| if (firstBlockTag) { |
| firstBlockTag = false; |
| comment.append("\n"); |
| } |
| |
| comment.append(d.toString()); |
| comment.append("\n"); |
| } |
| |
| m_parsedComments.put(name.toString(), comment.toString()); |
| } |
| |
| @Override |
| public boolean run(DocletEnvironment docEnv) { |
| /* |
| * This method is called by 'javadoc' and gets the whole parsed java |
| * file, we get comments and store them |
| */ |
| DocTrees docTrees = docEnv.getDocTrees(); |
| for (TypeElement t : ElementFilter.typesIn(docEnv.getIncludedElements())) { |
| String typeName = t.getQualifiedName().toString(); |
| |
| storeCommentFor(docTrees, typeName, t); |
| |
| for (Element e : t.getEnclosedElements()) { |
| // Omit the method name for ctors: this is a bit weird, but |
| // this is what the existing tests expect. |
| String fullName = typeName; |
| if (e.getKind() != ElementKind.CONSTRUCTOR) { |
| fullName = fullName + "." + e.getSimpleName(); |
| } |
| |
| storeCommentFor(docTrees, fullName, e); |
| } |
| } |
| |
| return true; |
| } |
| |
| |
| public static int check(Map<String, String> wantedComments) { |
| int errorCount=0; |
| Iterator<Entry<String, String>> it = m_parsedComments.entrySet().iterator(); |
| |
| while (it.hasNext()) { |
| |
| Entry<String, String> e = (Entry<String, String>) it.next(); |
| String actualStr = e.getValue(); |
| String wantedStr = wantedComments.get(e.getKey()); |
| // this may be weird, but I don't know any more effective solution |
| actualStr = actualStr.replace(" ", ""); |
| actualStr = actualStr.replaceAll("\t", ""); |
| actualStr = actualStr.replace("\n", ""); |
| |
| // Removing of <br> is temporary solution, since adding of |
| // <br> tag requires changes in all tests. However, <br> |
| // tag should be added more selectively and when this is |
| // implemented, tests should be updated. |
| actualStr = actualStr.replace("<br>", ""); |
| |
| if (wantedStr != null) { |
| wantedStr = wantedStr.replace(" ", ""); |
| wantedStr = wantedStr.replace("\t", ""); |
| wantedStr = wantedStr.replace("\n", ""); |
| wantedStr = wantedStr.replace("<br>", ""); |
| } |
| /* The following lines replace multiple whitespaces with a single one. |
| Although this would be more exact testing, it would also require |
| more work on test maintenance. |
| actualStr = actualStr.replace('\t', ' '); |
| actualStr = actualStr.replaceAll(" +", " "); |
| // actualStr = actualStr.replace("\n", ""); |
| if (wantedStr != null) { |
| wantedStr = wantedStr.replace('\t', ' '); |
| wantedStr = wantedStr.replaceAll(" +", " "); |
| // wantedStr = wantedStr.replace("\n", ""); |
| } */ |
| |
| if (!actualStr.equals(wantedStr)) { |
| System.out.println("\n\n////////////////////////////////////////////////////////////////////////"); |
| System.out.println("Documentation comments for '" + e.getKey() + "' do not match!"); |
| String expectedFileName = "expected.txt"; |
| String gotFileName = "got.txt"; |
| System.out.println("Output is also saved to files '" + expectedFileName + |
| "' and '" + gotFileName + "'"); |
| // here we print original strings, for nicer output |
| System.out.println("\n\n---\nexpected:\n" + wantedStr); |
| System.out.println("\n\n---\ngot:\n" + e.getValue()); |
| |
| try { |
| // write expected string to file |
| BufferedWriter expectedFile = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(expectedFileName))); |
| if (wantedStr != null) |
| expectedFile.write(wantedStr); |
| expectedFile.close(); |
| |
| // write translated string to file |
| BufferedWriter gotFile = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(gotFileName))); |
| gotFile.write(e.getValue().replace("<br>", "")); |
| gotFile.close(); |
| } catch (IOException ex) { |
| System.out.println("Error when writing output to file: " + ex); |
| } |
| |
| errorCount++; |
| } |
| } |
| |
| if (m_parsedComments.size() != wantedComments.size()) { |
| System.out.println("Mismatch in the number of comments!\n Expected: " + |
| wantedComments.size() + "\n Parsed: " + |
| m_parsedComments.size()); |
| System.out.println("Expected keys: "); |
| printKeys(wantedComments); |
| System.out.println("Parsed keys: "); |
| printKeys(m_parsedComments); |
| |
| errorCount++; |
| } |
| |
| return errorCount > 0 ? 1 : 0; |
| } |
| |
| |
| private static void printKeys(Map<String, String> map) { |
| |
| Set<String> keys = map.keySet(); |
| for (String key : keys) { |
| System.out.println(" " + key); |
| } |
| } |
| |
| |
| public static void printCommentListForJavaSource() { |
| |
| Iterator< Entry<String, String> > it = m_parsedComments.entrySet().iterator(); |
| |
| while (it.hasNext()) { |
| |
| Entry<String, String> e = (Entry<String, String>) it.next(); |
| String commentText = e.getValue(); |
| commentText = commentText.replace("\\", "\\\\"); |
| commentText = commentText.replace("\"", "\\\""); |
| commentText = commentText.replace("\n", "\\n\" +\n\t\t\""); |
| System.out.format("wantedComments.put(\"%s\",\n\t\t\"%s\");\n", e.getKey(), commentText); |
| } |
| } |
| |
| public static void parse(String sourcefile) { |
| ToolProvider javadoc = ToolProvider.findFirst("javadoc").orElseThrow(); |
| int result = javadoc.run(System.out, System.err, new String[]{"-quiet", "-doclet", "CommentParser", sourcefile}); |
| if (result != 0) { |
| System.err.println("Executing javadoc failed."); |
| System.exit(result); |
| } |
| } |
| |
| |
| public static void main(String argv[]) { |
| |
| if (argv.length<1) { |
| System.out.format("Usage:\n\tCommentParser <package to parse>\n"); |
| System.exit(1); |
| } |
| |
| parse(argv[0]); |
| |
| // if we are run as standalone app, print the list of found comments as it would appear in java source |
| |
| printCommentListForJavaSource(); |
| } |
| } |