ソースレビュー自動化ツール作成

目的

ソースレビューの数が多く大変なため、単純なパターンは自動的にチェックするためのツールを作成したい。
※仕掛中

copied!

package check;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class JavaCodingConventionChecker {

//	private static final String INDENTATION = "    ";

	private static final Pattern PATTERN_CLASS_NAME = Pattern.compile("^[A-Z][a-zA-Z0-9]*$");
	private static final Pattern PATTERN_METHOD_NAME = Pattern.compile("^[a-z][a-zA-Z0-9]*$");
	private static final Pattern PATTERN_VARIABLE_NAME = Pattern.compile("^[a-z][a-zA-Z0-9]*$");
	private static final Pattern PATTERN_CONSTANT_NAME = Pattern.compile("^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$");
	private static final Pattern PATTERN_COMMENT = Pattern.compile("^\\s*(//|/\\*|\\*)?\\s*(.*)$");

	public static void main(String[] args) throws IOException {
		if (args.length != 1) {
			System.err.println("Usage: JavaCodingConventionChecker ");
			System.exit(1);
		}
		File directory = new File(args[0]);
		if (!directory.isDirectory()) {
			System.err.println("Not a directory: " + directory.getAbsolutePath());
			System.exit(1);
		}
		checkDirectory(directory);
		System.out.println("Java coding convention check passed!");
	}

	private static void checkDirectory(File directory) throws IOException {
		for (File file : directory.listFiles()) {
			if (file.isDirectory()) {
				checkDirectory(file);
			} else if (file.getName().endsWith(".java")) {
				checkFile(file);
			}
		}
	}

	private static void checkFile(File file) throws IOException {
		int lineNumber = 0;
		try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
			String line;
			while ((line = reader.readLine()) != null) {
				lineNumber++;
				checkLine(line, lineNumber);
			}
		}
	}

	private static void checkLine(String line, int lineNumber) {
		//クラス名が大文字始まりかどうか、キャメルケースであるかどうかをチェックする
		checkClassName(line, lineNumber);
		//メソッド名が小文字始まりかどうか、キャメルケースであるかどうかをチェックする
		checkMethodName(line, lineNumber);
		//変数名が小文字始まりかどうか、キャメルケースであるかどうかをチェックする
		checkVariableName(line, lineNumber);
		//定数名が大文字であり、単語の区切りにはアンダースコアを使用しているかどうかをチェックする
		checkConstantName(line, lineNumber);
		//インデントがタブまたは4つのスペースで行われているかどうかをチェックする
		checkIndentation(line, lineNumber);
		//コメントのインデントが正しいかどうか、コメントがピリオドで終わっているかどうかをチェックする
		checkComment(line, lineNumber);
	}

	private static void checkClassName(String line, int lineNumber) {
		if (line.contains("class ")) {
			String className = line.split("class ")[1].split(" ")[0];
			if (!PATTERN_CLASS_NAME.matcher(className).matches()) {
				System.err.println("Invalid class name on line " + lineNumber + ": " + className);
			}
		}
	}

	private static void checkMethodName(String line, int lineNumber) {
		if (line.contains("void ")) {
			String methodName = line.split("void ")[1].split("\\(")[0];
			if (!PATTERN_METHOD_NAME.matcher(methodName).matches()) {
				System.err.println("Invalid method name on line " + lineNumber + ": " + methodName);
			}
		}
	}

	private static void checkVariableName(String line, int lineNumber) {
		if (line.contains("=") && !line.contains("==") && !line.contains("!=")) {
			String variableName = line.split("=")[0].trim().split(" ")[1];
			if (!PATTERN_VARIABLE_NAME.matcher(variableName).matches()) {
				System.err.println("Invalid variable name on line " + lineNumber + ": " + variableName);
			}
		}
	}

	private static void checkConstantName(String line, int lineNumber) {
		if (line.contains("final ")) {
			String[] parts = line.split("final ");
			for (int i = 1; i < parts.length; i++) {
				String constantName = parts[i].split(" ")[0];
				if (!PATTERN_CONSTANT_NAME.matcher(constantName).matches()) {
					System.err.println("Invalid constant name on line " + lineNumber + ": " + constantName);
				}
			}
		}
	}

	private static void checkIndentation(String line, int lineNumber) {
		// コメントアウト行の場合はチェックをスキップする
		if (line.trim().startsWith("//") || line.trim().startsWith("/*") || line.trim().startsWith("*")) {
			return;
		}
		int indentationLevel = 0;
		for (int i = 0; i < line.length(); i++) {
			if (line.charAt(i) == ' ') {
				indentationLevel++;
			} else if (line.charAt(i) == '\t') {
				indentationLevel += 4;
			} else {
				break;
			}
		}
		if (indentationLevel % 4 != 0) {
			System.err.println("Invalid indentation on line " + lineNumber + ": " + line);
		}
	}

	private static void checkComment(String line, int lineNumber) {
        Matcher matcher = PATTERN_COMMENT.matcher(line);

		if (matcher.matches()) {
			String comment = "";
			if (matcher.find()) {
				comment = matcher.group(2);
			}
			if (comment.startsWith(" ")) {
				System.err.println("Invalid comment indentation on line " + lineNumber + ": " + line);
			}
			if (comment.length() > 0 && !comment.endsWith(".")) {
				System.err.println("Missing period at end of comment on line " + lineNumber + ": " + line);
			}
		}
	}
}

RJC

RJCはエンジニアが主役。 RJCのエンジニアはお客様の未来のために、スキルを高め続けています。 RJCはそんなエンジニアを全力で応援し、エンジニアと共にお客様にハッピーを届けます。