1 package org.djunits.cleanup;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.IOException;
6 import java.net.URISyntaxException;
7 import java.nio.charset.StandardCharsets;
8 import java.nio.file.Files;
9 import java.nio.file.Paths;
10 import java.util.List;
11
12 import com.github.javaparser.StaticJavaParser;
13 import com.github.javaparser.ast.CompilationUnit;
14 import com.github.javaparser.ast.body.CallableDeclaration;
15 import com.github.javaparser.ast.body.ConstructorDeclaration;
16 import com.github.javaparser.ast.body.MethodDeclaration;
17 import com.github.javaparser.ast.body.Parameter;
18 import com.github.javaparser.ast.comments.JavadocComment;
19 import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
20
21 /**
22 * Utility to add or update the type foe each parameter in the javadoc of all java files in /src/main/java in all or in selected
23 * projects in the workspace. Run this utility only from Eclipse!<br>
24 * <p>
25 * Copyright (c) 2003-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.<br>
26 * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
27 * </p>
28 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
29 */
30 public class ParamComments
31 {
32 /** the lines of the file. */
33 private List<String> lines;
34
35 /** file changed? */
36 private boolean changed;
37
38 /**
39 * @param args String[]; none
40 * @throws IOException on I/O error
41 * @throws URISyntaxException on I/O error
42 */
43 public static void main(final String[] args) throws IOException, URISyntaxException
44 {
45 new ParamComments();
46 }
47
48 /**
49 * @throws IOException on I/O error
50 * @throws URISyntaxException on I/O error
51 */
52 public ParamComments() throws IOException, URISyntaxException
53 {
54 File classFolder = new File(ParamComments.class.getResource("/").toURI());
55 File workspaceFolder = classFolder.getParentFile().getParentFile().getParentFile();
56 for (File projectFolder : workspaceFolder.listFiles())
57 {
58 if (projectFolder.isDirectory() && projectFolder.getName().startsWith("dju")
59 && new File(projectFolder, "src/main/java").exists())
60 {
61 File sourcePathFile = new File(projectFolder, "src/main/java");
62 for (File srcFolder : sourcePathFile.listFiles())
63 {
64 processDirOrFile(srcFolder);
65 }
66 }
67 }
68 }
69
70 /**
71 * @param srcFolder File; folder to look for subfolders and/or java files
72 * @throws IOException on i/o error
73 */
74 private void processDirOrFile(final File srcFolder) throws IOException
75 {
76 if (srcFolder.isDirectory())
77 {
78 for (File subFile : srcFolder.listFiles())
79 {
80 if (subFile.isDirectory())
81 {
82 processDirOrFile(subFile);
83 }
84 else if (subFile.getName().endsWith(".java") && !subFile.getName().startsWith("package-info"))
85 {
86 processJavaFile(subFile);
87 }
88 }
89 }
90 }
91
92 /**
93 * @param javaFile File; java file to process
94 * @throws IOException on error
95 */
96 private void processJavaFile(final File javaFile) throws IOException
97 {
98 System.out.println("\n" + javaFile.toURI().getPath());
99 this.changed = false;
100 this.lines = Files.readAllLines(Paths.get(javaFile.toURI()), StandardCharsets.UTF_8);
101 FileInputStream in = new FileInputStream(javaFile.toURI().getPath());
102 CompilationUnit cu = StaticJavaParser.parse(in);
103 cu.accept(new CodeVisitor(this), null);
104 if (this.changed)
105 {
106 Files.write(Paths.get(javaFile.toURI()), this.lines);
107 System.out.println("CHANGED AND WRITTEN: " + javaFile.toString());
108 }
109 }
110
111 /**
112 * @return changed
113 */
114 public final boolean isChanged()
115 {
116 return this.changed;
117 }
118
119 /**
120 * @param changed boolean; set changed
121 */
122 public final void setChanged(final boolean changed)
123 {
124 this.changed = changed;
125 }
126
127 /**
128 * @return lines
129 */
130 public final List<String> getLines()
131 {
132 return this.lines;
133 }
134
135 /**
136 * Simple visitor implementation for visiting MethodDeclaration and ConstructorDeclaration nodes.
137 */
138 protected static class CodeVisitor extends VoidVisitorAdapter<Void>
139 {
140 /** access to the lines of the file and to changed toggle. */
141 private ParamComments paramComments;
142
143 /**
144 * Constructor of the code visitor.
145 * @param paramComments ParamComments; class to be processed
146 */
147 protected CodeVisitor(final ParamComments paramComments)
148 {
149 this.paramComments = paramComments;
150 }
151
152 @Override
153 /**
154 * {@inheritDoc} <br>
155 * This method will be called for all constructors in this CompilationUnit, including constructors of inner classes.
156 */
157 public void visit(final ConstructorDeclaration constructorDeclaration, final Void arg)
158 {
159 System.out.println("\n------\nCONSTRUCTOR\n" + " " + constructorDeclaration.getName() + " : "
160 + constructorDeclaration.getDeclarationAsString());
161 processDeclaration(constructorDeclaration);
162 super.visit(constructorDeclaration, arg);
163 }
164
165 @Override
166 /**
167 * {@inheritDoc} <br>
168 * This method will be called for all methods in this CompilationUnit, including inner class methods.
169 */
170 public void visit(final MethodDeclaration methodDeclaration, final Void arg)
171 {
172 System.out
173 .println("\n------\n" + " " + methodDeclaration.getName() + " : " + methodDeclaration.getTypeAsString());
174 processDeclaration(methodDeclaration);
175 super.visit(methodDeclaration, arg);
176 }
177
178 /**
179 * Carry out the changes in the method comments or constructor comments.
180 * @param callableDeclaration CallableDeclaration<?>; the method declaration or constructor declaration
181 */
182 private void processDeclaration(final CallableDeclaration<?> callableDeclaration)
183 {
184 for (Parameter parameter : callableDeclaration.getParameters())
185 {
186 System.out.println(" " + parameter.getNameAsString() + " : " + parameter.getTypeAsString()
187 + (parameter.isVarArgs() ? "..." : ""));
188 }
189 if (callableDeclaration.getComment().isPresent())
190 {
191 System.out.print(callableDeclaration.getComment().get());
192 String parserComment = callableDeclaration.getComment().get().toString();
193 if (parserComment.contains("@param"))
194 {
195 // see how we would rebuild the comment
196 if (callableDeclaration.getComment().get().toJavadocComment().isPresent())
197 {
198 JavadocComment comment = callableDeclaration.getComment().get().toJavadocComment().get();
199 String[] commentLines = parserComment.split("\n");
200 for (String line : commentLines)
201 {
202 if (line.contains("@param") && !line.contains("@param <"))
203 {
204 // which line is it in the String[] model?
205 int fileLine = -1;
206 for (int lnr = 0; lnr < commentLines.length; lnr++)
207 {
208 if (this.paramComments.getLines().get(comment.getBegin().get().line + lnr).trim()
209 .equals(line.trim()))
210 {
211 fileLine = comment.getBegin().get().line + lnr;
212 }
213 }
214 if (fileLine == -1)
215 {
216 System.out.println("COULD NOT FIND LINE FROM COMMENT IN FILE LINES...");
217 break;
218 }
219 line = line.replaceAll("<", "<").replaceAll(">", ">");
220 // make a line without escaped HTML-sequences
221 String noHtmlLine = line.replaceAll("&\\w+;", "");
222 // variable name after @param
223 int paramIndex = line.indexOf("@param");
224 int varEndIndex = line.indexOf(' ', paramIndex + 7) == -1 ? line.length()
225 : line.indexOf(' ', paramIndex + 7);
226 String varName = line.substring(paramIndex + 6, varEndIndex).trim();
227 boolean found = false;
228 for (Parameter parameter : callableDeclaration.getParameters())
229 {
230 if (parameter.getNameAsString().equals(varName))
231 {
232 // see if type is there with a ; at the end
233 String varType = "";
234 if (noHtmlLine.indexOf(";", varEndIndex + 1) != -1)
235 {
236 varType = noHtmlLine
237 .substring(varEndIndex + 1, noHtmlLine.indexOf(';', varEndIndex + 1)).trim()
238 .replaceAll(", ", ",");
239 String parameterType =
240 parameter.getType().asString() + (parameter.isVarArgs() ? "..." : "");
241 // if there are spaces in the varType, we either have a type with spaces
242 // e.g., Type<A, B>, or we have no variable but a ; later in the line,
243 // e.g., @param var this is the Bézier variable. In these cases,
244 // we have to be super careful and not replace without warning...
245 if (!varType.equals(parameterType))
246 {
247 if (varType.contains(" "))
248 {
249 System.out.println("NO CHANGE - SPACES IN TYPE : "
250 + line.replaceAll("<", "<").replaceAll(">", ">").trim());
251 }
252 else
253 {
254 parameterType = parameterType.replaceAll(",", ", ");
255 line = line.substring(0, varEndIndex) + " " + parameterType
256 + line.substring(line.indexOf(";", varEndIndex + 1));
257 line = line.replaceAll("<", "<").replaceAll(">", ">");
258 System.out.println("CHANGED TYPE : " + line.trim());
259 this.paramComments.setChanged(true);
260 this.paramComments.getLines().set(fileLine, line.replaceAll("\\n", ""));
261 }
262 }
263 }
264 else
265 {
266 String parameterType = parameter.getType().asString()
267 + (parameter.isVarArgs() ? "..." : "").replaceAll(",", ", ");
268 line = line.substring(0, varEndIndex) + " " + parameterType + "; "
269 + line.substring(Math.min(varEndIndex + 1, line.length() - 1));
270 line = line.replaceAll("<", "<").replaceAll(">", ">");
271 System.out.println("ADDED TYPE : " + line.trim());
272 this.paramComments.setChanged(true);
273 this.paramComments.getLines().set(fileLine, line.replaceAll("\\n", ""));
274 }
275 found = true;
276 break;
277 }
278 }
279 if (!found)
280 {
281 System.out.println("XXXXXXXX @param comment for " + varName
282 + " does not match any parameters in method");
283 }
284 }
285 }
286 }
287 }
288 }
289 }
290 }
291 }