001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package org.eclipse.golo.doc; 012 013import org.eclipse.golo.compiler.GoloCompiler; 014import org.eclipse.golo.compiler.PackageAndClass; 015import gololang.ir.*; 016 017import java.util.*; 018import java.io.File; 019 020public class ModuleDocumentation implements DocumentationElement { 021 022 private String sourceFile; 023 private PackageAndClass moduleName; 024 private int moduleDefLine; 025 private String moduleDocumentation; 026 027 private final Map<String, Integer> imports = new TreeMap<>(); 028 private final Map<String, Integer> moduleStates = new TreeMap<>(); 029 private final SortedSet<FunctionDocumentation> functions = new TreeSet<>(); 030 private final SortedSet<FunctionDocumentation> macros = new TreeSet<>(); 031 private final Map<String, AugmentationDocumentation> augmentations = new TreeMap<>(); 032 private final SortedSet<StructDocumentation> structs = new TreeSet<>(); 033 private final SortedSet<UnionDocumentation> unions = new TreeSet<>(); 034 private final Set<NamedAugmentationDocumentation> namedAugmentations = new TreeSet<>(); 035 036 /** 037 * {@inheritDoc} 038 */ 039 @Override 040 public String type() { 041 return "module"; 042 } 043 044 ModuleDocumentation(GoloModule module) { 045 module.accept(new ModuleVisitor()); 046 } 047 048 ModuleDocumentation() { 049 050 } 051 052 public static ModuleDocumentation load(File file, GoloCompiler compiler) throws java.io.IOException { 053 return new ModuleDocumentation(compiler.expand(compiler.transform(compiler.parse(file)))); 054 } 055 056 public static ModuleDocumentation empty(String name) { 057 if (name == null || name.trim().isEmpty()) { 058 throw new IllegalArgumentException("Can't create empty module documentation without name"); 059 } 060 ModuleDocumentation doc = new ModuleDocumentation(); 061 doc.moduleName = PackageAndClass.of(name); 062 return doc; 063 } 064 065 public boolean isEmpty() { 066 return moduleStates.isEmpty() 067 && functions.isEmpty() 068 && macros.isEmpty() 069 && augmentations.isEmpty() 070 && structs.isEmpty() 071 && unions.isEmpty() 072 && namedAugmentations.isEmpty(); 073 } 074 075 public String goloVersion() { 076 return org.eclipse.golo.cli.command.Metadata.VERSION; 077 } 078 079 public SortedSet<StructDocumentation> structs() { 080 return structs; 081 } 082 083 public SortedSet<UnionDocumentation> unions() { 084 return unions; 085 } 086 087 public SortedSet<FunctionDocumentation> functions() { 088 return functions(false); 089 } 090 091 public SortedSet<FunctionDocumentation> functions(boolean withLocal) { 092 if (withLocal) { 093 return functions; 094 } 095 TreeSet<FunctionDocumentation> pubFunctions = new TreeSet<>(); 096 for (FunctionDocumentation f : functions) { 097 if (!f.local()) { 098 pubFunctions.add(f); 099 } 100 } 101 return pubFunctions; 102 } 103 104 public String sourceFile() { 105 return this.sourceFile; 106 } 107 108 public SortedSet<FunctionDocumentation> macros() { 109 return macros; 110 } 111 112 public String moduleName() { 113 return moduleName.toString(); 114 } 115 116 public String packageName() { 117 return moduleName.packageName(); 118 } 119 120 /** 121 * {@inheritDoc} 122 */ 123 @Override 124 public String label() { 125 return moduleName.toString(); 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public String name() { 133 return moduleName.className(); 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override 140 public String fullName() { 141 return moduleName.toString(); 142 } 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override 148 public String id() { 149 return ""; 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public DocumentationElement parent() { 157 return this; 158 } 159 160 public int moduleDefLine() { 161 return moduleDefLine; 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override 168 public int line() { 169 return moduleDefLine; 170 } 171 172 public String moduleDocumentation() { 173 return (moduleDocumentation != null) ? moduleDocumentation : "\n"; 174 } 175 176 public ModuleDocumentation moduleDocumentation(String doc) { 177 this.moduleDocumentation = doc; 178 return this; 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public String documentation() { 186 return moduleDocumentation(); 187 } 188 189 public Map<String, Integer> moduleStates() { 190 return moduleStates; 191 } 192 193 public Collection<AugmentationDocumentation> augmentations() { 194 return augmentations.values(); 195 } 196 197 public Collection<NamedAugmentationDocumentation> namedAugmentations() { 198 return namedAugmentations; 199 } 200 201 public Map<String, Integer> imports() { 202 return imports; 203 } 204 205 private class ModuleVisitor implements GoloIrVisitor { 206 207 private final Deque<Set<FunctionDocumentation>> functionContext = new LinkedList<>(); 208 private UnionDocumentation currentUnion; 209 private MemberHolder currentMemberHolder; 210 private final Deque<DocumentationElement> parents = new LinkedList<>(); 211 212 @Override 213 public void visitModule(GoloModule module) { 214 functionContext.push(functions); 215 parents.push(ModuleDocumentation.this); 216 moduleName = module.getPackageAndClass(); 217 moduleDefLine = module.positionInSourceCode().getStartLine(); 218 moduleDocumentation = module.documentation(); 219 sourceFile = module.sourceFile(); 220 module.walk(this); 221 } 222 223 @Override 224 public void visitModuleImport(ModuleImport moduleImport) { 225 if (!moduleImport.isImplicit()) { 226 imports.put(moduleImport.getPackageAndClass().toString(), moduleImport.positionInSourceCode().getStartLine()); 227 } 228 } 229 230 @Override 231 public void visitStruct(Struct struct) { 232 StructDocumentation doc = new StructDocumentation() 233 .parent(parents.peek()) 234 .name(struct.getName()) 235 .documentation(struct.documentation()) 236 .line(struct.positionInSourceCode().getStartLine()); 237 structs.add(doc); 238 currentMemberHolder = doc; 239 parents.push(doc); 240 struct.walk(this); 241 parents.pop(); 242 currentMemberHolder = null; 243 } 244 245 @Override 246 public void visitUnion(Union union) { 247 this.currentUnion = new UnionDocumentation() 248 .parent(parents.peek()) 249 .name(union.getName()) 250 .documentation(union.documentation()) 251 .line(union.positionInSourceCode().getStartLine()); 252 unions.add(this.currentUnion); 253 parents.push(currentUnion); 254 union.walk(this); 255 parents.pop(); 256 } 257 258 @Override 259 public void visitUnionValue(UnionValue value) { 260 UnionDocumentation.UnionValueDocumentation doc = this.currentUnion.addValue(value.getName()) 261 .parent(parents.peek()) 262 .documentation(value.documentation()) 263 .line(value.positionInSourceCode().getStartLine()); 264 currentMemberHolder = doc; 265 parents.push(doc); 266 value.walk(this); 267 parents.pop(); 268 currentMemberHolder = null; 269 } 270 271 @Override 272 public void visitAugmentation(Augmentation augment) { 273 String target = augment.getTarget().toString(); 274 if (!augmentations.containsKey(target)) { 275 augmentations.put(target, new AugmentationDocumentation() 276 .target(target) 277 .parent(parents.peek()) 278 .augmentationNames(augment.getNames()) 279 .line(augment.positionInSourceCode().getStartLine()) 280 ); 281 } 282 AugmentationDocumentation ad = augmentations.get(target); 283 if (augment.documentation() != null && !augment.documentation().isEmpty()) { 284 ad.documentation(String.join("\n", ad.documentation(), augment.documentation())); 285 } 286 functionContext.push(ad); 287 parents.push(ad); 288 augment.walk(this); 289 functionContext.pop(); 290 parents.pop(); 291 } 292 293 @Override 294 public void visitNamedAugmentation(NamedAugmentation augment) { 295 NamedAugmentationDocumentation augmentDoc = new NamedAugmentationDocumentation() 296 .parent(parents.peek()) 297 .name(augment.getName()) 298 .documentation(augment.documentation()) 299 .line(augment.positionInSourceCode().getStartLine()); 300 namedAugmentations.add(augmentDoc); 301 functionContext.push(augmentDoc); 302 parents.push(augmentDoc); 303 augment.walk(this); 304 functionContext.pop(); 305 parents.pop(); 306 } 307 308 @Override 309 public void visitFunction(GoloFunction function) { 310 if (!GoloModule.MODULE_INITIALIZER_FUNCTION.equals(function.getName())) { 311 FunctionDocumentation fdoc = new FunctionDocumentation(function.isMacro()) 312 .parent(parents.peek()) 313 .name(function.getName()) 314 .documentation(function.documentation()) 315 .augmentation(function.isInAugment()) 316 .line(function.positionInSourceCode().getStartLine()) 317 .local(function.isLocal()) 318 .arguments(function.getParameterNames()) 319 .varargs(function.isVarargs()); 320 (function.isMacro() ? macros : functionContext.peek()).add(fdoc); 321 } 322 } 323 324 @Override 325 public void visitLocalReference(LocalReference localRef) { 326 if (localRef.isModuleState()) { 327 moduleStates.put(localRef.getName(), localRef.positionInSourceCode().getStartLine()); 328 } 329 } 330 331 @Override 332 public void visitMember(Member member) { 333 currentMemberHolder.addMember(member.getName()) 334 .parent(parents.peek()) 335 .documentation(member.documentation()) 336 .line(member.positionInSourceCode().getStartLine()); 337 } 338 339 @Override 340 public void visitDecorator(Decorator decorator) { 341 } 342 343 @Override 344 public void visitBlock(Block block) { 345 } 346 347 @Override 348 public void visitConstantStatement(ConstantStatement constantStatement) { 349 } 350 351 @Override 352 public void visitReturnStatement(ReturnStatement returnStatement) { 353 } 354 355 @Override 356 public void visitFunctionInvocation(FunctionInvocation functionInvocation) { 357 } 358 359 @Override 360 public void visitMethodInvocation(MethodInvocation methodInvocation) { 361 } 362 363 @Override 364 public void visitAssignmentStatement(AssignmentStatement assignmentStatement) { 365 } 366 367 @Override 368 public void visitDestructuringAssignment(DestructuringAssignment assignment) { 369 } 370 371 @Override 372 public void visitReferenceLookup(ReferenceLookup referenceLookup) { 373 } 374 375 @Override 376 public void visitConditionalBranching(ConditionalBranching conditionalBranching) { 377 } 378 379 @Override 380 public void visitBinaryOperation(BinaryOperation binaryOperation) { 381 } 382 383 @Override 384 public void visitUnaryOperation(UnaryOperation unaryOperation) { 385 } 386 387 @Override 388 public void visitLoopStatement(LoopStatement loopStatement) { 389 } 390 391 @Override 392 public void visitForEachLoopStatement(ForEachLoopStatement foreachStatement) { 393 } 394 395 @Override 396 public void visitCaseStatement(CaseStatement caseStatement) { 397 } 398 399 @Override 400 public void visitMatchExpression(MatchExpression matchExpression) { 401 } 402 403 @Override 404 public void visitWhenClause(WhenClause<?> whenClause) { 405 } 406 407 @Override 408 public void visitThrowStatement(ThrowStatement throwStatement) { 409 } 410 411 @Override 412 public void visitTryCatchFinally(TryCatchFinally tryCatchFinally) { 413 } 414 415 @Override 416 public void visitClosureReference(ClosureReference closureReference) { 417 } 418 419 @Override 420 public void visitLoopBreakFlowStatement(LoopBreakFlowStatement loopBreakFlowStatement) { 421 } 422 423 @Override 424 public void visitCollectionLiteral(CollectionLiteral collectionLiteral) { 425 } 426 427 @Override 428 public void visitCollectionComprehension(CollectionComprehension collectionComprehension) { 429 } 430 431 @Override 432 public void visitNamedArgument(NamedArgument namedArgument) { 433 } 434 435 @Override 436 public void visitMacroInvocation(MacroInvocation call) { 437 } 438 439 @Override 440 public void visitNoop(Noop noop) { 441 } 442 443 @Override 444 public void visitToplevelElements(ToplevelElements toplevels) { 445 toplevels.walk(this); 446 } 447 } 448}