
org.sdmlib.doc.util.JavaDocFileVisitor Maven / Gradle / Ivy
package org.sdmlib.doc.util;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class is the visitor which analyse the java doc in a single file
*
*
* @author Sebastian Copei
*
*/
public class JavaDocFileVisitor implements FileVisitor {
/**
* The suffix of a java file as constant for easer use
*/
private static final String JAVA_FILE_SUFFIX = ".java";
/**
* The current package of the current file
*/
private String currentPackage;
/**
* The current file name
*/
private String currentFileName;
/**
* The informations about the current searching
*/
private ArrayList traceInf;
/**
* Should everything printed on console
*/
private boolean printOnConsole;
/**
* Should be check everything or just always written java doc
*/
private boolean fullCheck;
/**
* The recognize a change in the list, just for beauty print
*/
private int lastTraceInfSize;
/**
* Construct a new java doc file visitor which checks the current file for the java doc
*
* @param traceInf The informations about the current searching
* @param printOnConsole Should everything printed on console
* @param fullCheck If true everything will checked in every class, if false only created java doc will validate.
*/
public JavaDocFileVisitor(ArrayList traceInf, boolean printOnConsole, boolean fullCheck) {
//Initialize all attributes
this.traceInf = traceInf;
this.printOnConsole = printOnConsole;
this.fullCheck = fullCheck;
}
//Validates a single java file for the java doc
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
//Check if it is a java file
if(file.getFileName().toString().endsWith(JAVA_FILE_SUFFIX)) {
//Create a string
String text = "";
//Set the last trace information size
lastTraceInfSize = traceInf.size();
//Get all lines and add them to the string builder
List lines = Files.readAllLines(file);
for(String s : lines) { text += s + "\n"; }
//Extract the package name of the current java file
for(String s : lines) { if(s.contains("package")) { currentPackage = s.replace("package", ""); break; }};
currentPackage = currentPackage.substring(0, currentPackage.length() - 1);
//Set the current file name
currentFileName = file.getFileName().toString();
//Check the class java doc
checkClassJavaDoc(text.replaceAll("[\t]", ""), traceInf, lines, printOnConsole);
//Check all method java doc
checkMethodsJavaDoc(text.replaceAll("[\t]", ""), traceInf, lines, printOnConsole);
}
//Finish with the current file just push a new line to the console if wanted to make it more structure
if(printOnConsole && traceInf.size() > lastTraceInfSize) {
System.out.println();
}
//Look at the next file
return FileVisitResult.CONTINUE;
}
/**
* Checks if there is a java doc over the class declaration
*
* @param text The text which should be checked
* @param traceInf The current trace information
* @param lines All lines of the current checked file
* @param printOnConsole Should founded warnings or error printed on the console
*/
private void checkClassJavaDoc(String text, ArrayList traceInf, List lines, boolean printOnConsole) {
//First check if the class has java doc, first get the index of "public class"
int start = text.indexOf("public class");
//if the start -1 the class could be abstract
if(start == -1) {
//Get the index of "public abstract class"
start = text.indexOf("public abstract class");
}
//If the start -1 the class is a enumeration
if(start == -1) {
//Get the index of "public enum"
start = text.indexOf("public enum");
}
//If the start -1 the class is a interface
if(start == -1) {
//Get the index of "public interface"
start = text.indexOf("public interface");
}
//Maybe there is just a class
if(start == -1) {
//Get the index of "class"
start = text.indexOf("class ");
}
//Get the line of the class definition to jump there if there is no comment
int lineClass = -1;
for(String s : lines) { if(s.contains("public class")) { lineClass = lines.indexOf(s) + 1; break; }}
//Check if line class is still -1, this means class could be abstract
for(String s : lines) { if(s.contains("public abstract class")) { lineClass = lines.indexOf(s) + 1; break; }}
//Check if line class is still -1, this means class is a interface
if(lineClass == -1) {
for(String s : lines) { if(s.contains("public interface")) { lineClass = lines.indexOf(s) + 1; break; }}
}
//Check if line class is still -1, this means class is a enumeration
if(lineClass == -1) {
for(String s : lines) { if(s.contains("public enum")) { lineClass = lines.indexOf(s) + 1; break; }}
}
//Maybe there is just a class
if(lineClass == -1) {
for(String s : lines) { if(s.contains("class ")) { lineClass = lines.indexOf(s) + 1; break; }}
}
//Check if the char before the method declaration is a end of a comment
if(lines.get(lineClass - 2).contains("*/")) {
//Get the java doc comment, 0 because the first comment is the class comment
String classDoc = extractJavaDocComment(text, start);
//There is no comment or a wrong one
if(classDoc.isEmpty() && fullCheck) {
//Put the missing java doc to the trace informations
traceInf.add("ERROR:" + currentPackage + ".missing.ClassDoc(" + currentFileName + ":" + lineClass + ")");
//Print if you want to
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
//There is a comment check if there is a @author tag and text in the comment
} else {
//Check if there is a line like * some text
if(!Pattern.compile("\\u002A \\w+").matcher(classDoc).find()) {
//There is no text, put a missing doc description error
traceInf.add("WARNING:" + currentPackage + ".missing.ClassDocText(" + currentFileName + ":" + lineClass + ")");
//Print if you want to
if(printOnConsole) {
System.out.println(traceInf.get(traceInf.size() - 1));
}
}
//Check if there is the @author tag with some text
if(classDoc.split("@author").length == 1) {
//There is no tag and no text
traceInf.add("ERROR:" + currentPackage + ".missing.AuthorTag(" + currentFileName + ":" + lineClass + ")");
//Check if this should be printed
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
//There is the tag but no text
} else if(classDoc.split("@author [a-zA-Z]+").length == 1) {
//There is no text after the tag, create warning
traceInf.add("WARNING:" + currentPackage + ".missing.AuthoTagText(" + currentFileName + ":" + lineClass + ")");
//Check if this should be printed
if(printOnConsole) {
System.out.println(traceInf.get(traceInf.size() - 1));
}
}
}
//Return we found a doc or a incomplete doc
return;
}
//Only if full check is enable
if(fullCheck) {
//There is no comment in any case over the class definition, therefore create error
traceInf.add("ERROR:" + currentPackage + ".missing.ClassDoc(" + currentFileName + ":" + lineClass + ")");
//Print if you want to
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
}
}
/**
* Checks if there is a java doc over all method declarations
*
* @param text The text which should be checked
* @param traceInf The current trace information
* @param lines All lines of the current checked file
* @param printOnConsole Should founded warnings or error printed on the console
*/
private void checkMethodsJavaDoc(String text, ArrayList traceInf, List lines, boolean printOnConsole) {
//Create a matcher to find all method declarations
Matcher methodPattern = Pattern.compile("((public|private|protected|static|final|native|synchronized|abstract|transient)+\\s)+[\\$_\\w\\<\\>\\[\\]]*\\s+[\\$_\\w]+\\([^\\)]*\\)?\\s*\\{?").matcher(text);
//Go through all matches
while(methodPattern.find()) {
//Get the current match
String match = methodPattern.group();
//Get the index of the current match in the text
int start = methodPattern.start();
//Get the line of the method definition to jump there if there is no comment
int lineMethod = -1;
for(String s : lines) { if(s.contains(match)) { lineMethod = lines.indexOf(s) + 1; break; }}
//If lineMethod still -1 the { is in the next row therefore cut at \n and try again
if(lineMethod == -1) {
for(String s : lines) { if(s.contains(match.split("\n")[0])) { lineMethod = lines.indexOf(s) + 1; break; }}
}
//Check if there is a annotation over the method, if so go to the next method
if(lines.get(lineMethod - 2).contains("@")) { continue; }
//Check if the char before the method declaration is a end of a comment
if(lines.get(lineMethod - 2).contains("*/")) {
//Get the java doc comment
String methodDoc = extractJavaDocComment(text, start);
//There is no comment or a wrong one
if(methodDoc.isEmpty() && fullCheck) {
//Put the missing java doc to the trace informations
traceInf.add("ERROR:" + currentPackage + ".missing.MethodDoc(" + currentFileName + ":" + lineMethod + ")");
//Print if you want to
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
//There is a comment check if there are all needed tags
} else {
//Check if there is a line like * some text
if(!Pattern.compile("\\u002A \\w+").matcher(methodDoc).find()) {
//There is no text, put a missing doc description error
traceInf.add("WARNING:" + currentPackage + ".missing.MethodDocText(" + currentFileName + ":" + lineMethod + ")");
//Print if you want to
if(printOnConsole) {
System.out.println(traceInf.get(traceInf.size() - 1));
}
}
//Check if there are parameters in the method declaration
if(!match.contains("()")) {
//Get all parameters from method declaration
String temp = match.substring(match.indexOf("(") + 1, match.indexOf(")")).replaceAll("<[^\\)]*>", "");
String[] parameters = temp.split(",");
//Go through all parameters and check the @param tag
for(String s : parameters) {
String parameterName = s.trim().split(" ")[1];
//Check if there is a @param tag with the current parameter name
if(methodDoc.split("@param " + parameterName).length == 1) {
//There is no tag and no text
traceInf.add("ERROR:" + currentPackage + ".missing.ParamTag(" + currentFileName + ":" + lineMethod + ")");
//Check if this should be printed
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
//There is the tag but no text
} else if(methodDoc.split("@param " + parameterName + " [a-zA-Z]+").length == 1) {
//There is no text after the tag, create warning
traceInf.add("WARNING:" + currentPackage + ".missing.ParamTagText(" + currentFileName + ":" + lineMethod + ")");
//Check if this should be printed
if(printOnConsole) {
System.out.println(traceInf.get(traceInf.size() - 1));
}
}
}
}
//Check if there is a return type in the method
if(!match.contains("void")) {
//There is no tag and text
if(methodDoc.split("@return").length == 1) {
//There is no tag and no text
traceInf.add("ERROR:" + currentPackage + ".missing.ReturnTag(" + currentFileName + ":" + lineMethod + ")");
//Check if this should be printed
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
//There is the tag but no text
} else if(methodDoc.split("@return [a-zA-Z]+").length == 1) {
//There is no text after the tag, create warning
traceInf.add("WARNING:" + currentPackage + ".missing.ReturnTagText(" + currentFileName + ":" + lineMethod + ")");
//Check if this should be printed
if(printOnConsole) {
System.out.println(traceInf.get(traceInf.size() - 1));
}
}
}
}
//Look at the next match
continue;
}
//Only if full check is enable
if(fullCheck) {
//There is no comment in any case over the method definition, therefore create error
traceInf.add("ERROR:" + currentPackage + ".missing.MethodDoc(" + currentFileName + ":" + lineMethod + ")");
//Print if you want to
if(printOnConsole) {
System.err.println(traceInf.get(traceInf.size() - 1));
}
}
}
}
/**
* Extract a java doc comment from a given string
*
* @param extractFrom The text where the comment should be extracted from
* @param searchIndex The index from which the search will start
* @return The extracted comment as string
*/
private String extractJavaDocComment(String extractFrom, int searchIndex) {
//Initialize attribute which describe the range of the doc
int begin = -1;
int end = -1;
//Go back from the searchIndex
for(int i = searchIndex; i > 0; i--) {
//Found the start of the java doc
if(extractFrom.charAt(i) == '*' && extractFrom.charAt(i - 1) == '*' && extractFrom.charAt(i - 2) == '/') {
//The start of the comment
begin = i - 2;
//Found the end of the java doc
} else if(extractFrom.charAt(i) == '/' && extractFrom.charAt(i - 1) == '*') {
//The end of the comment
end = i;
}
//If both values found break
if(begin != -1 && end != -1) {
break;
}
}
//Wrong comment just a /* or there is no comment
if(begin == -1){
return "";
}
return extractFrom.substring(begin, end + 2);
}
//--------------------------------------NOT USED METHODS FROM FILE VISITOR--------------------------------------
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; }
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; }
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { return FileVisitResult.CONTINUE; }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy