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