001/************************* PROJECT RON *************************/ 002/* Copyright (c) 2026 StuyPulse Robotics. All rights reserved. */ 003/* Use of this source code is governed by an MIT-style license */ 004/* that can be found in the repository LICENSE file. */ 005/***************************************************************/ 006package tools.DogLogASTParser; 007 008import com.github.javaparser.ParserConfiguration; 009import com.github.javaparser.StaticJavaParser; 010import com.github.javaparser.ast.CompilationUnit; 011import com.github.javaparser.ast.NodeList; 012import com.github.javaparser.ast.body.MethodDeclaration; 013import com.github.javaparser.ast.expr.BooleanLiteralExpr; 014import com.github.javaparser.ast.expr.MethodCallExpr; 015import com.github.javaparser.ast.expr.NameExpr; 016import com.github.javaparser.ast.expr.ObjectCreationExpr; 017import com.github.javaparser.ast.type.ClassOrInterfaceType; 018import java.io.File; 019import java.nio.file.Files; 020import java.nio.file.Path; 021 022/** 023 * 024 * 025 * <h2>DogLogASTParser | A tool for replacing SmartDashboard calls with DogLog 026 * calls</h2> 027 * 028 * <p> 029 * The main entry point for DogLogASTParser. It uses an abstract syntax tree 030 * (AST) parser called 031 * <a href="https://javaparser.org">JavaParser</a> to read and modify the java 032 * files in the robot 033 * code. 034 * 035 * <p> 036 * SmartDashboard calls of <code>putNumber</code>, <code>putBoolean</code>, and 037 * <code>putString 038 * </code> are replaced with <code>DogLog.log</code> calls. 039 * <code>SmartDashboard.putData</code> 040 * remains unchanged for more complex data. 041 * 042 * <p> 043 * Works with a Github Action workflow (doglog-replacement.yml) to automate the 044 * replacement with 045 * every push onto the main branch. 046 * 047 * <p> 048 * To execute, manually run the workflow in Github or push to main. It's not 049 * recommended to run 050 * this tool locally as the purpose of this tool is to automate the replacement 051 * and push to the 052 * <code>doglog</code> branch for testing. 053 */ 054public class Main { 055 056 /** 057 * 058 * 059 * <h4>Entry Point</h4> 060 * 061 * <p> 062 * Walks through the Java files in the robot code, filtering the tools 063 * directory, and invokes 064 * the {@link #processFile(Path)} method for AST parsing. 065 */ 066 public static void main(String[] args) { 067 StaticJavaParser.getConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17); 068 final Path root = Path.of("src/main/java"); 069 try { 070 Files.walk(root) 071 .filter(Files::isRegularFile) 072 .filter(p -> !p.toString().contains("tools/")) 073 .filter(p -> p.toString().endsWith(".java")) 074 .forEach(Main::processFile); 075 configureDogLog(); 076 } catch (java.io.IOException e) { 077 e.printStackTrace(); 078 } 079 } 080 081 /** 082 * 083 * 084 * <h4>Processes individual Java files.</h4> 085 * 086 * <p> 087 * Walks through the java files in the robot code, excluding the tools 088 * directory, and invokes 089 * the {@link #processFile(Path)} method for AST parsing. 090 * 091 * <p> 092 * It makes a {@link CompilationUnit} for the file, then applies the 093 * {@link DogLogRewriter} to 094 * replace SmartDashboard calls with DogLog calls. 095 * 096 * <p> 097 * When {@link DogLogRewriter} is done, the modified {@link CompilationUnit} is 098 * written back to 099 * the file path, replacing the original file. 100 * 101 * @param filePath the path of the Java file to be processed 102 */ 103 public static void processFile(Path filePath) { 104 try { 105 CompilationUnit parsed = StaticJavaParser.parse(filePath); 106 DogLogRewriter.applyReplacement(parsed); 107 108 Files.writeString(filePath, parsed.toString()); 109 110 System.out.println("Replaced file: " + filePath + " with DogLog calls."); 111 112 } catch (Exception e) { 113 String errorReason = e.getClass().getSimpleName() + ": " + e.getMessage(); 114 System.out.println( 115 "Error processing file: " + filePath + ", will be skipped: " + errorReason); 116 } 117 } 118 119 /** 120 * 121 * 122 * <h4>Adds a DogLog configuration line to Robot.java's robotInit method</h4> 123 */ 124 private static void configureDogLog() { 125 try { 126 final File file = new File("src/main/java/com/stuypulse/robot/Robot.java"); 127 final CompilationUnit parsed = StaticJavaParser.parse(file); 128 parsed.addImport("dev.doglog.DogLogOptions"); 129 130 final ObjectCreationExpr doglogOptions = new ObjectCreationExpr( 131 null, new ClassOrInterfaceType(null, "DogLogOptions"), new NodeList<>()); 132 133 final MethodCallExpr withCaptureDs = new MethodCallExpr(doglogOptions, "withCaptureDs") 134 .addArgument(new BooleanLiteralExpr(true)); 135 136 final MethodCallExpr doglogConfigCall = new MethodCallExpr(new NameExpr("DogLog"), "setOptions") 137 .addArgument(withCaptureDs); 138 139 parsed 140 .findFirst( 141 MethodDeclaration.class, method -> method.getNameAsString().equals("robotInit")) 142 .ifPresent( 143 method -> method.getBody().ifPresent(body -> body.addStatement(doglogConfigCall))); 144 145 Files.writeString(file.toPath(), parsed.toString()); 146 } catch (Exception e) { 147 String errorReason = e.getClass().getSimpleName() + ": " + e.getMessage(); 148 System.out.println("Error processing Robot.java, it will be skipped: " + errorReason); 149 } 150 } 151}