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}