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}