package application; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; import de.lcag.common.HashTable; import de.lcag.common.Table.TableColumn; import routines.LcagFileTools; import routines.LcagLogger; public class CommentExtractor { private static final String BUGFIX_COMMAND = "BUGFIX_"; // Fix of a Agile Defect in Trackspace private static final String STORY_COMMAND = "BUGFIX_"; // User Story in Trackspace to implement private static final String HOW_TO_COMMAND = "HOWTO_"; private static final String DOCUMENT_FLOW_COMMAND = "FLOW_"; private static final String TODO_COMMAND = "TODO_"; private static final String[] COMMAD_LIST = new String[] { HOW_TO_COMMAND, DOCUMENT_FLOW_COMMAND, TODO_COMMAND, BUGFIX_COMMAND, STORY_COMMAND }; private static final String COL_TYPE = "Type"; private static final String COL_ID = "Id"; private static final String COL_KIND = "Kind"; private static final String COL_STEP = "StepNo"; private static final String COL_DESC = "Description;style=width: 85%"; private static final String COL_FILE = "File"; private static final String COL_LINE_NO = "LineNo"; public static final String JAVA_CODE_BASE_PATH = "/workspace/ebx_lufthansa/ebx_lufthansa-lib/src/main/java/com/lufthansa/ebx"; public static final String SQL_CODE_BASE_PATH = "/workspace/ebx_lufthansa/ebx_lufthansa-resources/src/main/sql"; public static final String XSD_CODE_BASE_PATH = "/workspace/MDM-Datamodel"; public static final String JAVA_CODE_BASE_FULLPATH = "V:/EBX" + JAVA_CODE_BASE_PATH; public static final String SQL_CODE_BASE_FULLPATH = "V:/EBX" + SQL_CODE_BASE_PATH; public static final String XSD_CODE_BASE_FULLPATH = "V:/EBX" + XSD_CODE_BASE_PATH; public static final String CODE_BASE_GITHUB_DIR = "https://dev.azure.com/LCAGDevOps/MDM/_git/EBX?path=%s&version=GB%s&line=%d&lineEnd=%d&lineStartColumn=1&lineEndColumn=1&lineStyle=plain&_a=contents"; public static final String DOC_FILE = "V:/EBX/workspace/ebx_lufthansa/ebx_lufthansa-lib/HowToImplement"; private static HashTable resultTable = new HashTable(COL_TYPE, COL_ID, COL_KIND, COL_STEP, COL_DESC, COL_FILE, COL_LINE_NO); private static String gitBranchName = "main"; private static LcagLogger logger = LcagLogger.getLogger(CommentExtractor.class); public static void main(String[] args) { String rootDirPath; logger.setJavaLogLevel(LcagLogger.LOG_TRACE); if (args.length < 1) { rootDirPath = JAVA_CODE_BASE_FULLPATH; logger.info("Using default Root directory to scan code: %s", rootDirPath); } else { rootDirPath = args[0]; } try { gitBranchName = getCurrentGitBranch(); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } resultTable.setName("MDM EBX Coding HowTo"); try { extractCodeExamples(rootDirPath); TableColumn sortedResults = resultTable.sort(COL_ID, COL_STEP); String txt = resultTable.asCSV(sortedResults); String fileName = DOC_FILE + ".csv"; LcagFileTools.writeTextFile(fileName, txt); writeHtmlFile(sortedResults); System.out.println(String.format("Saved Code examples to file: %s", DOC_FILE)); } catch (IOException e) { logger.error("Error reading file: %s", e.getMessage()); } } private static void writeHtmlFile(TableColumn sortedResults) throws IOException { String fileName = DOC_FILE + ".html"; for (int i = 0; i < resultTable.lenght(); i++) { int lineNo = (int) resultTable.getCellValue(i, COL_LINE_NO); String colId = (String) resultTable.getCellValue(i, COL_ID); String filePath = (String) resultTable.getCellValue(i, COL_FILE); String gitURL = String.format(CODE_BASE_GITHUB_DIR, filePath, gitBranchName, lineNo, lineNo + 1); String lineNoURL = String.format("%d", gitURL, lineNo); if (filePath.endsWith(".java")) { filePath = filePath.substring(JAVA_CODE_BASE_PATH.length() + 1); } else if (filePath.endsWith(".sql")) { filePath = filePath.substring(SQL_CODE_BASE_PATH.length() + 1); } else if (filePath.endsWith(".xsd")) { filePath = filePath.substring(XSD_CODE_BASE_PATH.length() + 1); } resultTable.setCellValue(i, COL_FILE, filePath); resultTable.setCellValue(i, COL_LINE_NO, lineNoURL); String kind = (String) resultTable.getCellValue(i, COL_KIND); if (kind.equals("Title")) { String disc = String.format("%s", (String) resultTable.getCellValue(i, COL_DESC)); resultTable.setCellValue(i, COL_DESC, disc); // For BUGFIX or STORY: Add link into trackspace: String digits = colId.replaceAll(".*[BS](\\d+).*", "$1"); // Extract digits after 'B' if (!Objects.equals(colId, digits)) { String htmlLink = "" + colId + ""; resultTable.setCellValue(i, COL_ID, htmlLink); } } } String txt = resultTable.asHTML(sortedResults); LcagFileTools.writeTextFile(fileName, txt); String md = FlexmarkHtmlConverter.builder().build().convert(txt); String mdFileName = DOC_FILE + ".md"; LcagFileTools.writeTextFile(mdFileName, md); } public static String getCurrentGitBranch() throws IOException, InterruptedException { Process process = Runtime.getRuntime().exec("git rev-parse --abbrev-ref HEAD"); process.waitFor(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); return reader.readLine(); } private static void extractCodeExamples(String pRootDirPath) throws IOException { List fileList = LcagFileTools.findFilesWithExtension(pRootDirPath, ".java"); final List sqlFileList = LcagFileTools.findFilesWithExtension(SQL_CODE_BASE_FULLPATH, ".sql"); final List xsdFileList = LcagFileTools.findFilesWithExtension(XSD_CODE_BASE_FULLPATH, ".xsd"); fileList.addAll(xsdFileList); fileList.addAll(sqlFileList); for (File javaFile : fileList) { String filename = javaFile.getName(); BufferedReader reader = new BufferedReader(new FileReader(javaFile)); StringBuilder currentComment = new StringBuilder(); boolean inMultilineComment = false; boolean inSingleLineComment = false; String line; int lineNo = 0; int commentStartLineNo = 0; logger.trace("Processing file %s...", filename); while ((line = reader.readLine()) != null) { lineNo += 1; line = line.trim(); final boolean singleLineComment = line.startsWith("//") /* java */ || line.startsWith("--") /* SQL */; if (!inMultilineComment && singleLineComment) { final String comment = line.substring(2).trim(); inSingleLineComment = true; } else if (line.startsWith("/*") /* java */ || line.startsWith("") /* xml */ )) { String comment = line.endsWith("*/") ? line.substring(0, line.length() - 2) : line.substring(0, line.length() - 3); if (isFirstLineInComment) { comment = currentComment.toString().trim(); comment = comment.endsWith("*/") ? comment.substring(0, comment.length() - 2) : comment.substring(0, comment.length() - 3); } else { currentComment.append(" ").append(comment.trim()); comment = currentComment.toString().trim(); } parseComment(comment, javaFile, lineNo); inMultilineComment = false; commentStartLineNo = 0; currentComment.setLength(0); } else if (inMultilineComment && !isFirstLineInComment) { if (line.startsWith("*")) line = line.substring(1).trim(); currentComment.append(" ").append(line); } } reader.close(); } } private static void parseComment(String pComment, File pInFile, int pLineNo) { String command = null; String type = null; String regEx = String.join("|", COMMAD_LIST); Pattern pattern = Pattern.compile(regEx); Matcher matcher = pattern.matcher(pComment); List cmdList = new ArrayList<>(); List startPositionList = new ArrayList<>(); while (matcher.find()) { final int start = matcher.start(); String cmd = matcher.group(); cmdList.add(cmd); startPositionList.add(start); } if (cmdList.isEmpty()) return; for (int n = 0; n < cmdList.size(); n++) { String cmd = cmdList.get(n); int start = startPositionList.get(n); int end = 0; if (n == cmdList.size() - 1) { end = pComment.length(); } else { String nextCmd = (String) cmdList.get(n + 1); end = startPositionList.get(n + 1); } start += cmd.length(); command = pComment.substring(start, end); // FIXME: Problem if comment text also includes "_" command = command.replace("_", ";"); command = command.replaceFirst(":", ";"); List split = new ArrayList<>(Arrays.asList(command.split(";"))); if (split.size() < 2) { logger.warn("Unexpected HowTo format '%s', should be '%s H: [Title:|.] ", pComment, cmd); return; } type = cmd.substring(0, cmd.length() - 1); String id = split.remove(0); String kind = split.remove(0).trim(); String descr = String.join(" ", split).trim(); String absoluteSourceCodeFilePath = pInFile.getAbsolutePath(); absoluteSourceCodeFilePath = absoluteSourceCodeFilePath.replace("\\", "/"); absoluteSourceCodeFilePath = absoluteSourceCodeFilePath.replaceFirst("V:/EBX", ""); String stepNoStr = "00"; if (!"Title".equals(kind)) { stepNoStr = kind.replace("Step", ""); kind = "Step"; } int rowNo = resultTable.newRow(); resultTable.setCellValue(rowNo, COL_TYPE, type); resultTable.setCellValue(rowNo, COL_ID, id); resultTable.setCellValue(rowNo, COL_KIND, kind); resultTable.setCellValue(rowNo, COL_STEP, stepNoStr); resultTable.setCellValue(rowNo, COL_FILE, absoluteSourceCodeFilePath); resultTable.setCellValue(rowNo, COL_LINE_NO, pLineNo); resultTable.setCellValue(rowNo, COL_DESC, descr); } } }