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.ast.CompilationUnit; 009import com.github.javaparser.ast.expr.MethodCallExpr; 010import com.github.javaparser.ast.expr.NameExpr; 011import com.github.javaparser.ast.visitor.ModifierVisitor; 012 013/** 014 * 015 * 016 * <h2>DogLogRewriter | Actual functionality</h2> 017 * 018 * <p> 019 * The actual class that performs the AST parsing and replacement of 020 * <code>SmartDashboard</code> 021 * calls with <code>DogLog</code> calls, invoked by the {@link Main} class. 022 * 023 * <p> 024 * Extends {@link ModifierVisitor} to "visit" and modify the calls. 025 */ 026public class DogLogRewriter extends ModifierVisitor<Void> { 027 028 private boolean fileChanged = false; 029 030 /** 031 * 032 * 033 * <h4>Visit Method</h4> 034 * 035 * <p> 036 * Overrides the {@link visit} method for method calls. 037 * 038 * <p> 039 * It loops through every {@link MethodCallExpr} to check for a 040 * <code>SmartDashboard</code> 041 * call with the methods <code>putNumber</code>, <code>putBoolean</code>, or 042 * <code>putString 043 * </code>. If so, it replaces the scope with <code>DogLog</code> and changes 044 * the method name to 045 * <code>log</code>. 046 * 047 * <p> 048 * Sets variable <code>fileChanged</code> to check if any changes were made to 049 * the file to 050 * determine whether to import <code>DogLog</code> at the end of the file. 051 * 052 * @param methodCall the method call expression being visited 053 * @param arg additional argument (not used in this case) 054 * @return the modified method call expression 055 */ 056 @Override 057 public MethodCallExpr visit(MethodCallExpr methodCall, Void arg) { 058 super.visit(methodCall, arg); 059 060 if (!methodCall.getScope().isPresent()) { 061 return methodCall; 062 } 063 064 boolean isSmartDashboardCall = methodCall 065 .getScope() 066 .filter(s -> s instanceof NameExpr) 067 .map(s -> ((NameExpr) s).getNameAsString()) 068 .filter(name -> name.equals("SmartDashboard")) 069 .isPresent(); 070 071 if (isSmartDashboardCall) { 072 String methodName = methodCall.getNameAsString(); 073 boolean shouldBeReplaced = switch (methodName) { 074 case "putNumber", "putBoolean", "putString" -> true; 075 default -> false; 076 }; 077 078 if (!shouldBeReplaced) { 079 return methodCall; 080 } 081 082 methodCall.setScope(new NameExpr("DogLog")); 083 methodCall.setName("log"); 084 fileChanged = true; 085 } 086 087 return methodCall; 088 } 089 090 /** 091 * 092 * 093 * <h4>Applies the replacement</h4> 094 * 095 * <p> 096 * Applies the <code>SmartDashboard</code> to <code>DogLog</code> replacement to 097 * the given 098 * {@link CompilationUnit}. 099 * 100 * <p> 101 * If the <code>fileChanged</code> flag is true, it checks if the files already 102 * has an import 103 * for <code>DogLog</code>. If not, it adds the import statement. If the flag is 104 * false, it does 105 * nothing to avoid unnecessary changes and imports. 106 * 107 * @param parsed the {@link CompilationUnit} to process 108 */ 109 public static void applyReplacement(CompilationUnit parsed) { 110 DogLogRewriter doglogRewriter = new DogLogRewriter(); 111 parsed.accept(doglogRewriter, null); 112 113 if (!doglogRewriter.fileChanged) { 114 return; 115 } 116 117 String doglogImport = "dev.doglog.DogLog"; 118 boolean hasDogLogImport = parsed.getImports().stream().anyMatch(i -> i.getNameAsString().equals(doglogImport)); 119 120 if (!hasDogLogImport) { 121 parsed.addImport(doglogImport); 122 } 123 } 124}