diff --git a/plugins/org.eclipse.n4js.jsdoc2spec/.classpath b/plugins/org.eclipse.n4js.jsdoc2spec/.classpath
index a2e7d69e3d..3628e33687 100644
--- a/plugins/org.eclipse.n4js.jsdoc2spec/.classpath
+++ b/plugins/org.eclipse.n4js.jsdoc2spec/.classpath
@@ -6,7 +6,6 @@
-
diff --git a/plugins/org.eclipse.n4js.jsdoc2spec/build.properties b/plugins/org.eclipse.n4js.jsdoc2spec/build.properties
index 0470e65b92..fb36160409 100644
--- a/plugins/org.eclipse.n4js.jsdoc2spec/build.properties
+++ b/plugins/org.eclipse.n4js.jsdoc2spec/build.properties
@@ -1,5 +1,4 @@
-source.. = src/,\
- xtend-gen/
+source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
diff --git a/plugins/org.eclipse.n4js.jsdoc2spec/pom.xml b/plugins/org.eclipse.n4js.jsdoc2spec/pom.xml
index 4750cb5dd4..b3206816bc 100644
--- a/plugins/org.eclipse.n4js.jsdoc2spec/pom.xml
+++ b/plugins/org.eclipse.n4js.jsdoc2spec/pom.xml
@@ -49,10 +49,6 @@ Contributors:
-->
-
- org.eclipse.xtend
- xtend-maven-plugin
-
diff --git a/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocFactory.xtend b/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocFactory.java
similarity index 60%
rename from plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocFactory.xtend
rename to plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocFactory.java
index 21f14197a8..8b175069e6 100644
--- a/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocFactory.xtend
+++ b/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocFactory.java
@@ -10,9 +10,11 @@
*/
package org.eclipse.n4js.jsdoc2spec.adoc;
-import com.google.inject.Inject
-import org.eclipse.n4js.jsdoc.N4JSDocHelper
-import java.util.Map
+import java.util.Map;
+
+import org.eclipse.n4js.jsdoc.N4JSDocHelper;
+
+import com.google.inject.Inject;
/**
* Creates AsciiDoc spec fragments for spec region entries.
@@ -25,20 +27,21 @@ public class ADocFactory {
@Inject
ADocSerializer ADocSerializer;
-
/**
* Creates the spec of the given entry for the AsciiDoc document.
*/
- public def CharSequence createSpecRegionString(SpecRequirementSection spec, Map specsByKey) {
- return ADocSerializer.process(spec, specsByKey);
+ public CharSequence createSpecRegionString(SpecRequirementSection spec,
+ @SuppressWarnings("unused") Map specsByKey) {
+ return ADocSerializer.process(spec);
}
/**
* Creates the spec of the given entry for the AsciiDoc document.
*/
- public def CharSequence createSpecRegionString(SpecIdentifiableElementSection spec, Map specsByKey) {
- if (spec.getDoclet === null) {
- spec.doclet = n4jsDocHelper.getDoclet(spec.identifiableElement);
+ public CharSequence createSpecRegionString(SpecIdentifiableElementSection spec,
+ Map specsByKey) {
+ if (spec.getDoclet() == null) {
+ spec.setDoclet(n4jsDocHelper.getDoclet(spec.getIdentifiableElement()));
}
return ADocSerializer.process(spec, specsByKey);
}
diff --git a/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocSerializer.java b/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocSerializer.java
new file mode 100644
index 0000000000..fcc0a417c7
--- /dev/null
+++ b/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocSerializer.java
@@ -0,0 +1,726 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.jsdoc2spec.adoc;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_CODE;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_LINK;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_REQID;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_SPEC;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_SPECFROMDESCR;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_TASK;
+import static org.eclipse.n4js.jsdoc.N4JSDocletParser.TAG_TODO;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.groupBy;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.isNullOrEmpty;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.sortBy;
+import static org.eclipse.xtext.xbase.lib.StringExtensions.toFirstUpper;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.n4js.AnnotationDefinition;
+import org.eclipse.n4js.jsdoc.dom.Composite;
+import org.eclipse.n4js.jsdoc.dom.Doclet;
+import org.eclipse.n4js.jsdoc.dom.InlineTag;
+import org.eclipse.n4js.jsdoc.dom.LineTag;
+import org.eclipse.n4js.jsdoc.dom.Literal;
+import org.eclipse.n4js.jsdoc.dom.SimpleTypeReference;
+import org.eclipse.n4js.jsdoc.dom.TagValue;
+import org.eclipse.n4js.jsdoc.dom.Text;
+import org.eclipse.n4js.jsdoc.dom.VariableReference;
+import org.eclipse.n4js.jsdoc.tags.DefaultLineTagDefinition;
+import org.eclipse.n4js.jsdoc2spec.KeyUtils;
+import org.eclipse.n4js.jsdoc2spec.RepoRelativePath;
+import org.eclipse.n4js.jsdoc2spec.SpecTestInfo;
+import org.eclipse.n4js.ts.types.ContainerType;
+import org.eclipse.n4js.ts.types.FieldAccessor;
+import org.eclipse.n4js.ts.types.IdentifiableElement;
+import org.eclipse.n4js.ts.types.SyntaxRelatedTElement;
+import org.eclipse.n4js.ts.types.TAnnotation;
+import org.eclipse.n4js.ts.types.TClassifier;
+import org.eclipse.n4js.ts.types.TEnum;
+import org.eclipse.n4js.ts.types.TFunction;
+import org.eclipse.n4js.ts.types.TInterface;
+import org.eclipse.n4js.ts.types.TMember;
+import org.eclipse.n4js.ts.types.TMemberWithAccessModifier;
+import org.eclipse.n4js.ts.types.TMethod;
+import org.eclipse.n4js.ts.types.TN4Classifier;
+import org.eclipse.n4js.ts.types.TVariable;
+import org.eclipse.n4js.typesystem.utils.AllSuperTypesCollector;
+import org.eclipse.n4js.utils.DeclMergingHelper;
+import org.eclipse.n4js.utils.Strings;
+import org.eclipse.n4js.validation.N4JSElementKeywordProvider;
+import org.eclipse.n4js.validation.ValidatorMessageHelper;
+
+import com.google.inject.Inject;
+
+/**
+ * Print AsciiDoc code of specification JSDoc. Start and end markers are printed by client.
+ *
+ * Needs to be injected.
+ */
+class ADocSerializer {
+ @Inject
+ Html2ADocConverter html2aDocConverter;
+ @Inject
+ ValidatorMessageHelper validatorMessageHelper;
+ @Inject
+ N4JSElementKeywordProvider keywordProvider;
+ @Inject
+ RepoRelativePathHolder repoPathHolder;
+ @Inject
+ DeclMergingHelper declMergingHelper;
+
+ String process(SpecRequirementSection spec) {
+ StringBuilder strb = new StringBuilder();
+ appendSpecElementPost(strb, spec);
+ return Strings.stripAllTrailing(strb.toString());
+ }
+
+ String process(SpecIdentifiableElementSection spec, Map specsByKey) {
+ StringBuilder strb = new StringBuilder();
+ appendSpecElementPre(strb, spec);
+ appendSpec(strb, spec);
+ appendSpecElementPost(strb, spec, specsByKey);
+ return Strings.stripAllTrailing(strb.toString());
+ }
+
+ private StringBuilder appendSpecElementPost(StringBuilder strb, SpecRequirementSection spec) {
+ if (!isNullOrEmpty(spec.getTestInfosForType())) {
+ Map> groupdTests = groupBy(spec.getTestInfosForType(), sti -> sti.testTitle);
+ appendApiConstraints(strb, groupdTests);
+ }
+ return strb;
+ }
+
+ private StringBuilder appendSpec(StringBuilder strb, SpecIdentifiableElementSection spec) {
+ strb.append("\n");
+
+ boolean addedTaskLinks = false;
+ List taskTags = spec.getDoclet().lineTags(TAG_TASK.getTitle());
+ for (LineTag tag : taskTags) {
+ String taskID = TAG_TASK.getValue(tag, "");
+ if (!taskID.isEmpty()) {
+ if (taskID.startsWith("*")) {
+ appendTaskLink(strb, taskID.substring(1));
+ } else {
+ appendTaskLink(strb, taskID);
+ }
+ strb.append(" ");
+ addedTaskLinks = true;
+ }
+ }
+ if (addedTaskLinks)
+ strb.append("\n\n");
+
+ appendSpecDescription(strb, spec);
+ return strb;
+ }
+
+ private StringBuilder appendSpecDescription(StringBuilder strb, SpecIdentifiableElementSection spec) {
+ Doclet doclet = spec.getDoclet();
+ boolean bSpecFromDescr = doclet.hasLineTag(TAG_SPECFROMDESCR.getTitle());
+ String reqID = getReqId(doclet);
+ List specTags = doclet.lineTags(TAG_SPEC.getTitle());
+
+ if (specTags.isEmpty() && !bSpecFromDescr && reqID.isEmpty()) {
+ return strb;
+ }
+
+ if (!(spec.idElement instanceof TN4Classifier || spec.idElement instanceof TEnum))
+ strb.append("==== Description\n\n");
+
+ if (!reqID.isEmpty()) {
+ strb.append("See req:" + reqID + "[].\n");
+ }
+
+ appendContents(strb, doclet);
+ for (LineTag tag : specTags) {
+ appendContents(strb, tag.getValueByKey(DefaultLineTagDefinition.CONTENTS));
+ }
+
+ strb.append("\n");
+
+ return strb;
+ }
+
+ private StringBuilder appendSpecDescriptions(StringBuilder strb, Iterable doclets) {
+ for (Doclet doclet : doclets) {
+ boolean bSpecFromDescr = doclet.hasLineTag(TAG_SPECFROMDESCR.getTitle());
+ List specTags = doclet.lineTags(TAG_SPEC.getTitle());
+ if (!specTags.isEmpty() || bSpecFromDescr) {
+ appendContents(strb, doclet);
+ for (LineTag tag : specTags) {
+ appendContents(strb, tag.getValueByKey(DefaultLineTagDefinition.CONTENTS));
+ }
+ }
+ }
+ return strb;
+ }
+
+ private StringBuilder appendContents(StringBuilder strb, Composite composite) {
+ boolean contentAdded = false;
+ for (EObject c : composite.eContents()) {
+ String newContent = processContent(c).toString();
+ strb.append(newContent);
+ contentAdded |= !newContent.isBlank();
+ }
+ if (contentAdded) {
+ strb.append("\n");
+ }
+ return strb;
+ }
+
+ private CharSequence processContent(EObject node) {
+ if (node instanceof Text) {
+ return processContent((Text) node);
+ }
+ if (node instanceof Literal) {
+ return processContent((Literal) node);
+ }
+ if (node instanceof SimpleTypeReference) {
+ return processContent((SimpleTypeReference) node);
+ }
+ if (node instanceof VariableReference) {
+ return processContent((VariableReference) node);
+ }
+ if (node instanceof InlineTag) {
+ return processContent((InlineTag) node);
+ }
+
+ return "";
+ }
+
+ private CharSequence processContent(Text node) {
+ return html2aDocConverter.transformHTML(node.getText());
+ }
+
+ private CharSequence processContent(Literal node) {
+ return html2aDocConverter.transformHTML(node.getValue());
+ }
+
+ private CharSequence processContent(SimpleTypeReference node) {
+ return html2aDocConverter.passThenMonospace(html2aDocConverter.transformHTML(node.getTypeName()));
+ }
+
+ private CharSequence processContent(VariableReference node) {
+ return html2aDocConverter.passThenMonospace(html2aDocConverter.transformHTML(node.getVariableName()));
+ }
+
+ private CharSequence processContent(InlineTag node) {
+ if (Objects.equals(TAG_CODE.getTitle(), node.getTitle().getTitle())) {
+ return html2aDocConverter.passThenMonospace(html2aDocConverter.transformHTML(TAG_CODE.getValue(node, "")));
+ }
+ if (Objects.equals(TAG_LINK.getTitle(), node.getTitle().getTitle())) {
+ return html2aDocConverter.passThenMonospace(html2aDocConverter.transformHTML(TAG_LINK.getValue(node, "")));
+ }
+
+ StringBuilder strb = new StringBuilder();
+ for (TagValue tv : node.getValues()) {
+ appendContents(strb, tv);
+ }
+ return strb;
+ }
+
+ private StringBuilder appendSpecElementPre(StringBuilder strb, SpecIdentifiableElementSection spec) {
+ IdentifiableElement element = spec.getIdentifiableElement();
+ if (element instanceof TMember) {
+ return appendElementCodePre(strb, (TMember) element, spec);
+ }
+ if (element instanceof TMethod) {
+ return appendElementCodePre(strb, (TMethod) element, spec);
+ }
+ if (element instanceof TFunction) {
+ return appendElementCodePre(strb, (TFunction) element, spec);
+ }
+ if (element instanceof TVariable) {
+ return appendElementCodePre(strb, (TVariable) element, spec);
+ }
+ return appendElementCodePre(strb, element, spec);
+ }
+
+ /**
+ * E.g. classes
+ */
+ private StringBuilder appendElementCodePre(StringBuilder strb,
+ @SuppressWarnings("unused") IdentifiableElement element, SpecIdentifiableElementSection spec) {
+
+ if (hasTodo(spec.getDoclet())) {
+ strb.append(getTodoLink(spec.getDoclet()));
+ }
+ return strb;
+ }
+
+ private StringBuilder appendElementCodePre(StringBuilder strb, TMember element,
+ SpecIdentifiableElementSection spec) {
+ return appendMemberOrVarOrFuncPre(strb,
+ validatorMessageHelper.shortDescription(element),
+ element,
+ spec);
+ }
+
+ private StringBuilder appendElementCodePre(StringBuilder strb, TMethod element,
+ SpecIdentifiableElementSection spec) {
+ return appendMemberOrVarOrFuncPre(strb,
+ validatorMessageHelper.shortDescription((TMember) element),
+ element,
+ spec);
+ }
+
+ private StringBuilder appendElementCodePre(StringBuilder strb, TFunction element,
+ SpecIdentifiableElementSection spec) {
+ return appendMemberOrVarOrFuncPre(strb,
+ validatorMessageHelper.shortDescription(element),
+ element,
+ spec);
+ }
+
+ private StringBuilder appendElementCodePre(StringBuilder strb, TVariable element,
+ SpecIdentifiableElementSection spec) {
+ return appendMemberOrVarOrFuncPre(strb,
+ validatorMessageHelper.shortDescription(element),
+ element,
+ spec);
+ }
+
+ private StringBuilder appendMemberOrVarOrFuncPre(StringBuilder strb, String shortDescr,
+ SyntaxRelatedTElement element, SpecIdentifiableElementSection spec) {
+
+ boolean isIntegratedFromPolyfill = !Objects.equals(spec.sourceEntry.trueFolder, spec.sourceEntry.folder);
+ String trueSrcFolder = spec.sourceEntry.repository + ":" + spec.sourceEntry.trueFolder;
+ String todoLink = hasTodo(spec.getDoclet()) ? "\n" + getTodoLink(spec.getDoclet()) : "";
+ String polyfill = isIntegratedFromPolyfill
+ ? "\n\n[.small]#(Integrated from static polyfill aware class in: %s)#".formatted(trueSrcFolder)
+ : "";
+
+ strb.append("""
+
+ [[gsec:spec_%s]]
+ [role=memberdoc]
+ === %s%s%s
+
+ ==== Signature
+
+ %s
+
+ """.formatted(
+ spec.sourceEntry.getAdocCompatibleAnchorID(),
+ html2aDocConverter.pass(toFirstUpper(shortDescr)),
+ todoLink,
+ polyfill,
+ codeLink(element)));
+ return strb;
+ }
+
+ private CharSequence codeLink(EObject element) {
+ if (element instanceof TVariable) {
+ return codeLink((TVariable) element);
+ }
+ if (element instanceof TMethod) {
+ return codeLink((TMethod) element);
+ }
+ if (element instanceof TFunction) {
+ return codeLink((TFunction) element);
+ }
+ if (element instanceof TMember) {
+ return codeLink((TMember) element);
+ }
+
+ throw new IllegalArgumentException();
+ }
+
+ private CharSequence codeLink(TMember member) {
+ return doCodeLink(member, fullSignature(member));
+ }
+
+ private CharSequence codeLink(TMethod method) {
+ return doCodeLink(method, fullSignature(method));
+ }
+
+ private CharSequence codeLink(TFunction func) {
+ return doCodeLink(func, fullSignature(func));
+ }
+
+ private CharSequence codeLink(TVariable tvar) {
+ return doCodeLink(tvar, fullSignature(tvar));
+ }
+
+ private String fullSignature(TMember member) {
+ StringBuilder strb = new StringBuilder();
+ for (TAnnotation a : filter(member.getAnnotations(),
+ ann -> !AnnotationDefinition.INTERNAL.name.equals(ann.getName()))) {
+
+ strb.append(a.getAnnotationAsString() + " ");
+ }
+ strb.append(keywordProvider.keyword(member.getMemberAccessModifier()) + " ");
+ if (member.isAbstract()) {
+ strb.append("@abstract ");
+ }
+ strb.append(member.getMemberAsString());
+
+ return strb.toString();
+ }
+
+ private String fullSignature(TMethod method) {
+ return validatorMessageHelper.fullFunctionSignature(method);
+ }
+
+ private String fullSignature(TFunction func) {
+ return validatorMessageHelper.fullFunctionSignature(func);
+ }
+
+ private String fullSignature(TVariable tvar) {
+ if (tvar.getTypeRef() == null) {
+ return tvar.getName();
+ }
+ return tvar.getName() + ": " + tvar.getTypeRef().getTypeRefAsString();
+ }
+
+ private CharSequence doCodeLink(IdentifiableElement element, String signature) {
+ RepoRelativePath rrp = repoPathHolder.get(element);
+ StringBuilder strb = new StringBuilder();
+
+ if (rrp != null) {
+ SourceEntry se = SourceEntryFactory.create(repoPathHolder, rrp, element);
+ appendSourceLink(strb, se, html2aDocConverter.passThenMonospace(signature));
+ }
+
+ return strb.toString();
+ }
+
+ private boolean isInSpec(TMember member, Map specsByKey) {
+ if (member == null) {
+ return false;
+ }
+ return specsByKey.containsKey(KeyUtils.getSpecKey(repoPathHolder, member));
+ }
+
+ private String getFormattedID(Entry> entry, List superTypes) {
+ int index = superTypes.indexOf(entry.getKey().getContainingType());
+ return String.format("%04d", index) + entry.getKey().getName();
+ }
+
+ private StringBuilder appendSpecElementPost(StringBuilder strb, SpecIdentifiableElementSection specRegion,
+ Map specsByKey) {
+
+ IdentifiableElement element = specRegion.getIdentifiableElement();
+ if (element instanceof TMember) {
+ return appendElementPost(strb, (TMember) element, specRegion, specsByKey);
+ }
+ if (element instanceof TMethod) {
+ return appendElementPost(strb, (TMethod) element, specRegion, specsByKey);
+ }
+ if (element instanceof TFunction) {
+ return appendElementPost(strb, (TFunction) element, specRegion, specsByKey);
+ }
+ if (element instanceof TVariable) {
+ return appendElementPost(strb, (TVariable) element, specRegion, specsByKey);
+ }
+ return appendElementPost(strb, element, specRegion, specsByKey);
+ }
+
+ private StringBuilder appendElementPost(StringBuilder strb,
+ IdentifiableElement element, SpecIdentifiableElementSection specRegion,
+ Map specsByKey) {
+
+ if (element instanceof ContainerType>) {
+ Map> testsForInherited = specRegion.getTestInfosForInheritedMember();
+ if (testsForInherited == null || testsForInherited.isEmpty()) {
+ return strb;
+ }
+
+ String typeName = element.getName();
+ List superTypes = AllSuperTypesCollector.collect((ContainerType>) element,
+ declMergingHelper);
+
+ Iterable>> tests = filter(testsForInherited.entrySet(),
+ e -> e.getValue() != null && !e.getValue().isEmpty());
+ Iterable>> sortedTests = sortBy(tests,
+ e -> getFormattedID(e, superTypes));
+
+ for (Entry> tmemberSpecs : sortedTests) {
+ String shortDescr = validatorMessageHelper.shortDescription(tmemberSpecs.getKey());
+ String secSpecLink = typeName + "." + validatorMessageHelper.shortQualifiedName(tmemberSpecs.getKey());
+ String secSpecLinkEsc = SourceEntry.getEscapedAdocAnchorString(secSpecLink);
+ String description = validatorMessageHelper.description(tmemberSpecs.getKey().getContainingType());
+ String shortQualName = validatorMessageHelper.shortQualifiedName(tmemberSpecs.getKey());
+ String gsecSpec = isInSpec(tmemberSpecs.getKey(), specsByKey)
+ ? "\n\t<>\n".formatted(html2aDocConverter.pass(shortQualName))
+ : "";
+
+ strb.append("""
+
+ [[gsec:spec_%s]]
+ [role=memberdoc]
+ === %s
+
+ Inherited from
+ %s%s
+
+ """.formatted(
+ secSpecLinkEsc,
+ html2aDocConverter.pass(toFirstUpper(shortDescr)),
+ html2aDocConverter.pass(description),
+ gsecSpec));
+
+ appendConstraints(strb, tmemberSpecs.getKey(), specRegion, tmemberSpecs.getValue(), false);
+ }
+
+ }
+ return strb;
+
+ }
+
+ private StringBuilder appendElementPost(StringBuilder strb, TMember element,
+ SpecIdentifiableElementSection specRegion,
+ @SuppressWarnings("unused") Map specsByKey) {
+
+ appendConstraints(strb, element, specRegion, specRegion.getTestInfosForMember(),
+ !hasReqId(specRegion.getDoclet()));
+ return strb;
+ }
+
+ private StringBuilder appendElementPost(StringBuilder strb, TMethod element,
+ SpecIdentifiableElementSection specRegion,
+ @SuppressWarnings("unused") Map specsByKey) {
+
+ appendConstraints(strb, element, specRegion, specRegion.getTestInfosForMember(),
+ !hasReqId(specRegion.getDoclet()));
+ return strb;
+ }
+
+ private StringBuilder appendElementPost(StringBuilder strb, TFunction element,
+ SpecIdentifiableElementSection specRegion,
+ @SuppressWarnings("unused") Map specsByKey) {
+
+ if (!isNullOrEmpty(specRegion.getTestInfosForType())) {
+ Map> groupdTests = groupBy(specRegion.getTestInfosForType(),
+ sti -> sti.testTitle);
+
+ strb.append("==== Semantics\n");
+ appendApiConstraints(strb, groupdTests);
+ } else {
+
+ String reqID = getReqId(specRegion.getDoclet());
+ if (reqID.isEmpty()) {
+ String todoLink = getTodoLink(
+ "Add tests specifying semantics for " + html2aDocConverter.passThenMonospace(element.getName()),
+ "test function " + html2aDocConverter.pass(element.getName()));
+
+ strb.append("""
+
+ ==== Semantics
+ %s
+ """.formatted(todoLink));
+ } else {
+ strb.append("% tests see " + reqID);
+ }
+ }
+
+ return strb;
+ }
+
+ private StringBuilder appendElementPost(StringBuilder strb, @SuppressWarnings("unused") TVariable element,
+ SpecIdentifiableElementSection specRegion,
+ @SuppressWarnings("unused") Map specsByKey) {
+
+ if (!isNullOrEmpty(specRegion.getTestInfosForType())) {
+ Map> groupdTests = groupBy(specRegion.getTestInfosForType(),
+ sti -> sti.testTitle);
+
+ strb.append("==== Semantics\n");
+ appendApiConstraints(strb, groupdTests);
+ }
+ return strb; // test are optional for variables.
+ }
+
+ private StringBuilder appendConstraints(StringBuilder strb, TMember element,
+ SpecIdentifiableElementSection specRegion, Set specTestInfos, boolean addTodo) {
+
+ if (!isNullOrEmpty(specTestInfos)) {
+ Map> groupdTests = groupBy(specTestInfos, sti -> sti.testTitle);
+ strb.append("==== Semantics\n");
+ appendApiConstraints(strb, groupdTests);
+
+ } else if (addTodo) {
+
+ if (elementMayNeedsTest(element, specRegion)) {
+ String todoLink = getTodoLink(
+ "Add tests specifying semantics for "
+ + html2aDocConverter.passThenMonospace(element.getMemberAsString()),
+ "test " + html2aDocConverter
+ .pass(element.getContainingType().getName() + "." + element.getName()));
+
+ strb.append("""
+
+ ==== Semantics
+ %s
+ """.formatted(todoLink));
+ }
+ }
+ return strb;
+ }
+
+ private boolean elementMayNeedsTest(TMember element, SpecIdentifiableElementSection spec) {
+ // there are tests, so we show them
+ if (!isNullOrEmpty(spec.getTestInfosForType())) {
+ return true;
+ }
+ if ((element instanceof TMethod) || (element instanceof FieldAccessor)) {
+ if (element.getContainingType() instanceof TInterface) {
+ if (element instanceof TMemberWithAccessModifier) {
+ return !((TMemberWithAccessModifier) element).isHasNoBody();
+ }
+ }
+ return !element.isAbstract();
+ }
+ return false;
+ }
+
+ /**
+ * List of tests in apiConstraint macros.
+ */
+ private StringBuilder appendApiConstraints(StringBuilder strb,
+ Map> groupdTests) {
+ for (Map.Entry> group : sortBy(groupdTests.entrySet(),
+ e -> e.getKey().toString())) {
+ strb.append("\n");
+ strb.append(". *");
+ String key = group.getKey().toString();
+ String keyWithoutPrecedingNumber = removePrecedingNumber(key);
+ strb.append(html2aDocConverter.pass(keyWithoutPrecedingNumber));
+ strb.append("* (");
+ Iterator iter = group.getValue().iterator();
+ while (iter.hasNext()) {
+ SpecTestInfo testSpec = iter.next();
+ strb.append(nfgitTest(testSpec));
+ if (iter.hasNext()) {
+ strb.append(", \n");
+ }
+ }
+ strb.append(")\n");
+ Iterable doclets = map(filter(group.getValue(), spi -> spi.doclet != null), spi -> spi.doclet);
+ StringBuilder strbTmp = new StringBuilder();
+ appendSpecDescriptions(strbTmp, doclets);
+ if (strbTmp.length() > 0) {
+ strb.append("+\n");
+ strb.append("[.generatedApiConstraint]\n");
+ strb.append("====\n\n");
+ strb.append(strbTmp);
+ strb.append("\n====\n");
+ }
+ }
+ return strb;
+ }
+
+ private CharSequence nfgitTest(SpecTestInfo testInfo) {
+ StringBuilder strb = new StringBuilder();
+ if (testInfo.rrp == null) {
+ strb.append(small(testInfo.testModuleSpec() + "."));
+ strb.append(testInfo.testMethodTypeName() + "." + testInfo.testMethodName());
+ } else {
+ SourceEntry pc = SourceEntryFactory.create(testInfo);
+ String strCase = "Test";
+ if (!isNullOrEmpty(testInfo.testCase)) {
+ String formattedCase = removePrecedingNumber(testInfo.testCase);
+ if (isNullOrEmpty(formattedCase)) {
+ formattedCase = testInfo.testCase;
+ }
+ html2aDocConverter.pass(formattedCase);
+ }
+ StringBuilder strbTmp = new StringBuilder();
+ appendSourceLink(strbTmp, pc, strCase);
+ strb.append(small(strbTmp));
+ }
+ return strb.toString();
+ }
+
+ /**
+ * Reminder: Escaping the caption using the method {@link Html2ADocConverter#pass} is recommended.
+ */
+ private StringBuilder appendSourceLink(StringBuilder strb, SourceEntry pc, String caption) {
+ strb.append("srclnk:++" + pc.toPQN() + "++[" + caption + "]");
+ return strb;
+ }
+
+ /**
+ * Returns req id, may be an empty string but never null.
+ */
+ private String getReqId(Doclet doclet) {
+ return TAG_REQID.getValue(doclet, "");
+ }
+
+ /**
+ * Returns true, if spec contains a reference to a requirement id.
+ */
+ private boolean hasReqId(Doclet doclet) {
+ return !getReqId(doclet).isEmpty();
+ }
+
+ /**
+ * Returns true, if spec contains a reference to a todo.
+ */
+ private boolean hasTodo(Doclet doclet) {
+ return !getTodo(doclet).isEmpty();
+ }
+
+ /**
+ * Returns todo, may be an empty string but never null.
+ */
+ private String getTodo(Doclet doclet) {
+ return TAG_TODO.getValue(doclet, "");
+ }
+
+ private String getTodoLink(String todoText, String sideText) {
+ String str = isNullOrEmpty(sideText) ? "" : ", title=\"" + sideText + "\"";
+ String todo = """
+
+ [TODO%s]
+ --
+ %s
+ --
+ """.formatted(str, todoText);
+ return todo;
+ }
+
+ private String getTodoLink(Doclet doclet) {
+ return getTodoLink(getTodo(doclet), "");
+ }
+
+ private StringBuilder appendTaskLink(StringBuilder strb, String taskID) {
+ strb.append("task:" + taskID + "[]");
+ return strb;
+ }
+
+ private String small(CharSequence smallString) {
+ return "[.small]#" + smallString + "#";
+ }
+
+ private String removePrecedingNumber(String key) {
+ for (var i = 0; i < key.length(); i++) {
+ String stringAt = Character.toString(key.charAt(i));
+ if (!"0123456789 ".contains(stringAt)) {
+ return key.substring(i);
+ }
+ }
+ return "";
+ }
+
+}
diff --git a/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocSerializer.xtend b/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocSerializer.xtend
deleted file mode 100644
index b5bc565c81..0000000000
--- a/plugins/org.eclipse.n4js.jsdoc2spec/src/org/eclipse/n4js/jsdoc2spec/adoc/ADocSerializer.xtend
+++ /dev/null
@@ -1,607 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.jsdoc2spec.adoc;
-
-import com.google.inject.Inject
-import java.util.Collection
-import java.util.List
-import java.util.Map
-import java.util.Map.Entry
-import java.util.Set
-import java.util.SortedSet
-import org.eclipse.n4js.AnnotationDefinition
-import org.eclipse.n4js.jsdoc.dom.Composite
-import org.eclipse.n4js.jsdoc.dom.ContentNode
-import org.eclipse.n4js.jsdoc.dom.Doclet
-import org.eclipse.n4js.jsdoc.dom.InlineTag
-import org.eclipse.n4js.jsdoc.dom.Literal
-import org.eclipse.n4js.jsdoc.dom.SimpleTypeReference
-import org.eclipse.n4js.jsdoc.dom.Text
-import org.eclipse.n4js.jsdoc.dom.VariableReference
-import org.eclipse.n4js.jsdoc.tags.DefaultLineTagDefinition
-import org.eclipse.n4js.jsdoc2spec.KeyUtils
-import org.eclipse.n4js.jsdoc2spec.RepoRelativePath
-import org.eclipse.n4js.jsdoc2spec.SpecTestInfo
-import org.eclipse.n4js.ts.types.ContainerType
-import org.eclipse.n4js.ts.types.FieldAccessor
-import org.eclipse.n4js.ts.types.IdentifiableElement
-import org.eclipse.n4js.ts.types.SyntaxRelatedTElement
-import org.eclipse.n4js.ts.types.TClassifier
-import org.eclipse.n4js.ts.types.TEnum
-import org.eclipse.n4js.ts.types.TFunction
-import org.eclipse.n4js.ts.types.TInterface
-import org.eclipse.n4js.ts.types.TMember
-import org.eclipse.n4js.ts.types.TMemberWithAccessModifier
-import org.eclipse.n4js.ts.types.TMethod
-import org.eclipse.n4js.ts.types.TN4Classifier
-import org.eclipse.n4js.ts.types.TVariable
-import org.eclipse.n4js.typesystem.utils.AllSuperTypesCollector
-import org.eclipse.n4js.utils.DeclMergingHelper
-import org.eclipse.n4js.utils.Strings
-import org.eclipse.n4js.validation.N4JSElementKeywordProvider
-import org.eclipse.n4js.validation.ValidatorMessageHelper
-
-import static org.eclipse.n4js.jsdoc.N4JSDocletParser.*
-
-/**
- * Print AsciiDoc code of specification JSDoc. Start and end markers are printed by client.
- *
- * Needs to be injectd.
- */
-class ADocSerializer {
- @Inject extension Html2ADocConverter;
- @Inject ValidatorMessageHelper validatorMessageHelper;
- @Inject N4JSElementKeywordProvider keywordProvider;
- @Inject RepoRelativePathHolder repoPathHolder;
- @Inject DeclMergingHelper declMergingHelper;
-
-
- def String process(SpecRequirementSection spec, Map specsByKey) {
- val strb = new StringBuilder();
- strb.appendSpecElementPost(spec, specsByKey);
- return Strings.stripAllTrailing(strb.toString());
- }
-
- def String process(SpecIdentifiableElementSection spec, Map specsByKey) {
- val strb = new StringBuilder();
- strb.appendSpecElementPre(spec);
- strb.appendSpec(spec);
- strb.appendSpecElementPost(spec, specsByKey);
- return Strings.stripAllTrailing(strb.toString());
- }
-
- private def StringBuilder appendSpecElementPost(StringBuilder strb, SpecRequirementSection spec, Map map) {
- if (! spec.getTestInfosForType.isNullOrEmpty) {
- val Map> groupdTests = spec.getTestInfosForType.groupBy[testTitle];
- strb.appendApiConstraints(groupdTests);
- }
- return strb
- }
-
- private def StringBuilder appendSpec(StringBuilder strb, SpecIdentifiableElementSection spec) {
- strb.append("\n");
-
- var addedTaskLinks = false;
- val taskTags = spec.getDoclet.lineTags(TAG_TASK.title);
- for (tag : taskTags) {
- val taskID = TAG_TASK.getValue(tag, "");
- if (!taskID.empty) {
- if (taskID.startsWith("*")) {
- strb.appendTaskLink(taskID.substring(1));
- } else {
- strb.appendTaskLink(taskID);
- }
- strb.append(" ");
- addedTaskLinks = true;
- }
- }
- if(addedTaskLinks)
- strb.append("\n\n");
-
- strb.appendSpecDescription(spec);
- return strb
- }
-
- private def StringBuilder appendSpecDescription(StringBuilder strb, SpecIdentifiableElementSection spec) {
- val doclet = spec.getDoclet;
- val bSpecFromDescr = doclet.hasLineTag(TAG_SPECFROMDESCR.title);
- val reqID = getReqId(doclet);
- val specTags = doclet.lineTags(TAG_SPEC.title);
-
- if (specTags.empty && !bSpecFromDescr && reqID.isEmpty)
- return strb;
-
- if (!(spec.idElement instanceof TN4Classifier || spec.idElement instanceof TEnum))
- strb.append("==== Description\n\n");
-
- if (!reqID.isEmpty) {
- strb.append("See req:"+reqID +"[].\n");
- }
-
- strb.appendContents(doclet);
- for (tag : specTags) {
- strb.appendContents(tag.getValueByKey(DefaultLineTagDefinition.CONTENTS));
- }
-
- strb.append("\n");
-
- return strb;
- }
-
- private def StringBuilder appendSpecDescriptions(StringBuilder strb, Iterable doclets) {
- for (doclet : doclets) {
- var bSpecFromDescr = doclet.hasLineTag(TAG_SPECFROMDESCR.title);
- val specTags = doclet.lineTags(TAG_SPEC.title);
- if (! specTags.empty || bSpecFromDescr) {
- strb.appendContents(doclet);
- for (tag : specTags) {
- strb.appendContents(tag.getValueByKey(DefaultLineTagDefinition.CONTENTS));
- }
- }
- }
- return strb;
- }
-
- private def StringBuilder appendContents(StringBuilder strb, Composite composite) {
- for (c : composite.contents) {
- strb.append(processContent(c));
- }
- if (!composite.contents.isEmpty) {
- strb.append("\n");
- }
- return strb;
- }
-
-
- private def dispatch CharSequence processContent(ContentNode node) {}
- private def dispatch CharSequence processContent(Text node) {
- return transformHTML(node.text);
- }
- private def dispatch CharSequence processContent(Literal node) {
- return transformHTML(node.value);
- }
- private def dispatch CharSequence processContent(SimpleTypeReference node) {
- return passThenMonospace(transformHTML(node.typeName));
- }
- private def dispatch CharSequence processContent(VariableReference node) {
- return passThenMonospace(transformHTML(node.variableName));
- }
- private def dispatch CharSequence processContent(InlineTag node) {
- switch (node.title.title) {
- case TAG_CODE.title: passThenMonospace(transformHTML(TAG_CODE.getValue(node, "")))
- case TAG_LINK.title: passThenMonospace(transformHTML(TAG_LINK.getValue(node, "")))
- default: {
- val StringBuilder strb = new StringBuilder();
- node.values.forEach[strb.appendContents(it)];
- return strb;
- }
- }
- }
-
-
- private def StringBuilder appendSpecElementPre(StringBuilder strb, SpecIdentifiableElementSection spec) {
- return strb.appendElementCodePre(spec.identifiableElement, spec);
- }
-
-
- /**
- * E.g. classes
- */
- private def dispatch StringBuilder appendElementCodePre(StringBuilder strb, IdentifiableElement element, SpecIdentifiableElementSection spec) {
- if (hasTodo(spec.doclet))
- strb.append(getTodoLink(spec.doclet));
- return strb;
- }
- private def dispatch StringBuilder appendElementCodePre(StringBuilder strb, TMember element, SpecIdentifiableElementSection spec) {
- return strb.appendMemberOrVarOrFuncPre(
- validatorMessageHelper.shortDescription(element),
- validatorMessageHelper.shortQualifiedName(element),
- element.memberAsString,
- element,
- spec
- );
- }
- private def dispatch StringBuilder appendElementCodePre(StringBuilder strb, TMethod element, SpecIdentifiableElementSection spec) {
- return strb.appendMemberOrVarOrFuncPre(
- validatorMessageHelper.shortDescription(element as TMember),
- validatorMessageHelper.shortQualifiedName(element as TMember),
- element.memberAsString,
- element,
- spec
- );
- }
- private def dispatch StringBuilder appendElementCodePre(StringBuilder strb, TFunction element, SpecIdentifiableElementSection spec) {
- return strb.appendMemberOrVarOrFuncPre(
- validatorMessageHelper.shortDescription(element),
- validatorMessageHelper.shortQualifiedName(element),
- element.name,
- element,
- spec
- );
- }
- private def dispatch StringBuilder appendElementCodePre(StringBuilder strb, TVariable element, SpecIdentifiableElementSection spec) {
- return strb.appendMemberOrVarOrFuncPre(
- validatorMessageHelper.shortDescription(element),
- validatorMessageHelper.shortQualifiedName(element),
- element.name,
- element,
- spec
- );
- }
-
-
- private def StringBuilder appendMemberOrVarOrFuncPre(StringBuilder strb, String shortDescr, String shortQN,
- String reqName, SyntaxRelatedTElement element, SpecIdentifiableElementSection spec) {
-
- val boolean isIntegratedFromPolyfill = spec.sourceEntry.trueFolder != spec.sourceEntry.folder;
- val String trueSrcFolder = spec.sourceEntry.repository + ":" + spec.sourceEntry.trueFolder;
-
- strb.append(
- '''
-
- [[gsec:spec_«spec.sourceEntry.adocCompatibleAnchorID»]]
- [role=memberdoc]
- === «pass(shortDescr.toFirstUpper)»
- «IF hasTodo(spec.doclet)»
- «getTodoLink(spec.doclet)»
- «ENDIF»
- «IF isIntegratedFromPolyfill»
-
- [.small]#(Integrated from static polyfill aware class in: «trueSrcFolder»)#
- «ENDIF»
-
- ==== Signature
-
- «codeLink(element)»
-
- ''');
- return strb;
- }
-
-
- private def dispatch CharSequence codeLink(TMember member) {
- return doCodeLink(member, fullSignature(member));
- }
- private def dispatch CharSequence codeLink(TMethod method) {
- return doCodeLink(method, fullSignature(method));
- }
- private def dispatch CharSequence codeLink(TFunction func) {
- return doCodeLink(func, fullSignature(func));
- }
- private def dispatch CharSequence codeLink(TVariable tvar) {
- return doCodeLink(tvar, fullSignature(tvar));
- }
-
-
- private def fullSignature(TMember member) {
- val StringBuilder strb = new StringBuilder();
- for (a: member.annotations.filter[it.name!=AnnotationDefinition.INTERNAL.name]) {
- strb.append(a.annotationAsString + " ");
- }
- strb.append(keywordProvider.keyword(member.memberAccessModifier) + " ");
- if (member.abstract) {
- strb.append("@abstract ");
- }
- strb.append(member.memberAsString);
-
- return strb.toString;
- }
- private def fullSignature(TMethod method) {
- return validatorMessageHelper.fullFunctionSignature(method);
- }
- private def fullSignature(TFunction func) {
- return validatorMessageHelper.fullFunctionSignature(func);
- }
- private def fullSignature(TVariable tvar) {
- if (tvar.typeRef===null) {
- return tvar.name;
- }
- return '''«tvar.name»: «tvar.typeRef.typeRefAsString»''';
- }
-
-
- private def CharSequence doCodeLink(IdentifiableElement element, String signature) {
- val RepoRelativePath rrp = repoPathHolder.get(element);
- val strb = new StringBuilder();
-
- if (rrp !== null) {
- val SourceEntry se = SourceEntryFactory.create(repoPathHolder, rrp, element);
- strb.appendSourceLink(se, passThenMonospace(signature));
- }
-
- return strb.toString();
- }
-
-
- private def StringBuilder appendSpecElementPost(StringBuilder strb, SpecIdentifiableElementSection spec, Map map) {
- return strb.appendElementPost(spec.identifiableElement, spec, map);
- }
-
- private def dispatch StringBuilder appendElementPost(StringBuilder strb,
- IdentifiableElement element, SpecIdentifiableElementSection specRegion, Map specsByKey) {
-
- if (element instanceof ContainerType>) {
- var Map> testsForInherited = specRegion.getTestInfosForInheritedMember
- if (testsForInherited === null || testsForInherited.empty) {
- return strb;
- }
-
- val typeName = element.name;
- val superTypes = AllSuperTypesCollector.collect(
- element, declMergingHelper
- )
-
- val tests = testsForInherited.entrySet.filter[value!==null && !value.empty];
- val sortedTests = tests.sortBy[getFormattedID(it, superTypes)];
- for (tmemberSpecs : sortedTests) {
- val shortDescr = validatorMessageHelper.shortDescription(tmemberSpecs.key);
- val secSpecLink = typeName + "." + validatorMessageHelper.shortQualifiedName(tmemberSpecs.key);
- val secSpecLinkEsc = SourceEntry.getEscapedAdocAnchorString(secSpecLink);
- val description = validatorMessageHelper.description(tmemberSpecs.key.containingType);
- val shortQualName = validatorMessageHelper.shortQualifiedName(tmemberSpecs.key);
-
- strb.append(
- '''
-
- [[gsec:spec_«secSpecLinkEsc»]]
- [role=memberdoc]
- === «pass(shortDescr.toFirstUpper)»
-
- Inherited from
- «pass(description)»
- «IF isInSpec(tmemberSpecs.key, specsByKey)»
- <>
- «ENDIF»
-
- ''');
-
- strb.appendConstraints(tmemberSpecs.key, specRegion, tmemberSpecs.value, false);
- }
- }
- return strb
- }
-
-
- private def boolean isInSpec(TMember member, Map specsByKey) {
- if (member === null) {
- return false;
- }
- return specsByKey.containsKey(KeyUtils.getSpecKey(repoPathHolder, member));
- }
-
- private def String getFormattedID(Entry> entry, List superTypes) {
- val index = superTypes.indexOf(entry.key.containingType);
- return String.format("%04d", index) + entry.key.name
- }
-
-
- private def dispatch StringBuilder appendElementPost(StringBuilder strb, TMember element, SpecIdentifiableElementSection specRegion, Map specsByKey) {
- strb.appendConstraints(element, specRegion, specRegion.getTestInfosForMember, ! hasReqId(specRegion.getDoclet));
- return strb;
- }
- private def dispatch StringBuilder appendElementPost(StringBuilder strb, TMethod element, SpecIdentifiableElementSection specRegion, Map specsByKey) {
- strb.appendConstraints(element, specRegion, specRegion.getTestInfosForMember, ! hasReqId(specRegion.getDoclet));
- return strb;
- }
- private def dispatch StringBuilder appendElementPost(StringBuilder strb, TFunction element, SpecIdentifiableElementSection specRegion, Map specsByKey) {
-
- if (! specRegion.getTestInfosForType.isNullOrEmpty) {
- val Map> groupdTests = specRegion.getTestInfosForType.groupBy[testTitle];
- strb.append("==== Semantics\n");
- strb.appendApiConstraints(groupdTests);
- } else {
-
- val reqID = getReqId(specRegion.getDoclet);
- if (reqID.isEmpty) {
- val String todoLink = getTodoLink(
- "Add tests specifying semantics for " + passThenMonospace(element.name),
- "test function " + pass(element.name));
-
- strb.append(
- '''
-
- ==== Semantics
- «todoLink»
- ''');
- } else {
- strb.append('''% tests see «reqID»''');
- }
- }
-
- return strb;
- }
- private def dispatch StringBuilder appendElementPost(StringBuilder strb, TVariable element, SpecSection specRegion, Map specsByKey) {
- if (! specRegion.getTestInfosForType.isNullOrEmpty) {
- val Map> groupdTests = specRegion.getTestInfosForType.groupBy[testTitle];
- strb.append("==== Semantics\n");
- strb.appendApiConstraints(groupdTests)
- }
- return strb; // test are optional for variables.
- }
-
-
- private def StringBuilder appendConstraints(StringBuilder strb, TMember element, SpecIdentifiableElementSection specRegion, Set specTestInfos, boolean addTodo) {
- if (! specTestInfos.isNullOrEmpty) {
- val Map> groupdTests = specTestInfos.groupBy[testTitle];
- strb.append("==== Semantics\n");
- strb.appendApiConstraints(groupdTests);
-
- } else if (addTodo) {
-
- if (elementMayNeedsTest(element, specRegion)) {
- val String todoLink = getTodoLink(
- "Add tests specifying semantics for " + passThenMonospace(element.memberAsString),
- "test " + pass(element.containingType.name + "." + element.name));
-
- strb.append(
- '''
-
- ==== Semantics
- «todoLink»
- ''');
- }
- }
- return strb
- }
-
- private def boolean elementMayNeedsTest(TMember element, SpecIdentifiableElementSection spec) {
- // there are tests, so we show them
- if (! spec.getTestInfosForType.isNullOrEmpty) {
- return true;
- }
- if ((element instanceof TMethod) || (element instanceof FieldAccessor)) {
- if (element.containingType instanceof TInterface) {
- if (element instanceof TMemberWithAccessModifier) {
- return ! element.hasNoBody
- }
- }
- return !element.isAbstract;
- }
- return false;
- }
-
- /**
- * List of tests in apiConstraint macros.
- */
- private def StringBuilder appendApiConstraints(StringBuilder strb, Map> groupdTests) {
- for (group : groupdTests.entrySet.sortBy[it.key.toString]) {
- strb.append("\n");
- strb.append(". *");
- val key = group.key.toString;
- val keyWithoutPrecedingNumber = removePrecedingNumber(key);
- strb.append(pass(keyWithoutPrecedingNumber));
- strb.append("* (");
- val iter = group.value.iterator;
- while (iter.hasNext) {
- val SpecTestInfo testSpec = iter.next;
- strb.append(nfgitTest(testSpec));
- if (iter.hasNext) {
- strb.append(", \n");
- }
- }
- strb.append(")\n");
- val Iterable doclets = group.value.filter[doclet !== null].map[doclet];
- val strbTmp = new StringBuilder();
- strbTmp.appendSpecDescriptions(doclets);
- if (strbTmp.length > 0) {
- strb.append("+\n");
- strb.append("[.generatedApiConstraint]\n");
- strb.append("====\n\n");
- strb.append(strbTmp);
- strb.append("\n====\n");
- }
- }
- return strb
- }
-
- private def CharSequence nfgitTest(SpecTestInfo testInfo) {
- val strb = new StringBuilder();
- if (testInfo.rrp === null) {
- strb.append(small(testInfo.testModuleSpec() + "."));
- strb.append(testInfo.testMethodTypeName() + "." + testInfo.testMethodName());
- } else {
- val pc = SourceEntryFactory.create(testInfo);
- val strCase = if (testInfo.testCase.nullOrEmpty) "Test" else {
- var formattedCase = removePrecedingNumber(testInfo.testCase);
- if (formattedCase.nullOrEmpty) {
- formattedCase = testInfo.testCase;
- }
- pass(formattedCase);
- }
- val strbTmp = new StringBuilder();
- strbTmp.appendSourceLink(pc, strCase);
- strb.append(small(strbTmp));
- }
- return strb.toString();
- }
-
- /**
- * Reminder: Escaping the caption using the method {@link Html2ADocConverter#pass} is recommended.
- */
- private def StringBuilder appendSourceLink(StringBuilder strb, SourceEntry pc, String caption) {
- strb.append(
- '''srclnk:++«
- pc.toPQN
- »++[«
- caption
- »]''');
- return strb;
- }
-
- /**
- * Returns req id, may be an empty string but never null.
- */
- private def String getReqId(Doclet doclet) {
- return TAG_REQID.getValue(doclet, "");
- }
-
- /**
- * Returns true, if spec contains a reference to a requirement id.
- */
- private def boolean hasReqId(Doclet doclet) {
- return ! getReqId(doclet).isEmpty;
- }
-
- /**
- * Returns true, if spec contains a reference to a todo.
- */
- private def boolean hasTodo(Doclet doclet) {
- return ! getTodo(doclet).isEmpty;
- }
-
- /**
- * Returns todo, may be an empty string but never null.
- */
- private def String getTodo(Doclet doclet) {
- return TAG_TODO.getValue(doclet, "");
- }
-
- private def String getTodoLink(String todoText, String sideText) {
- val todo =
- '''
-
- [TODO«
- IF !sideText.isNullOrEmpty
- », title="«sideText»"«
- ENDIF
- »]
- --
- «todoText»
- --
-
- '''
- return todo;
- }
-
- private def String getTodoLink(Doclet doclet) {
- return getTodoLink(getTodo(doclet), "");
- }
-
- private def StringBuilder appendTaskLink(StringBuilder strb, String taskID) {
- strb.append('''task:«taskID»[]''');
- return strb;
- }
-
- private def String small(CharSequence smallString) {
- return '''[.small]#«smallString»#''';
- }
-
- private def String removePrecedingNumber(String key) {
- for (var i=0; i