Skip to content

Commit 9e21d54

Browse files
authored
Fix value substitution for parameters and forwards (#194)
1 parent 7b03166 commit 9e21d54

File tree

10 files changed

+357
-20
lines changed

10 files changed

+357
-20
lines changed

frank-doc-doclet/src/main/java/org/frankframework/frankdoc/Utils.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2020 - 2023 WeAreFrank!
2+
Copyright 2020 - 2024 WeAreFrank!
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -72,8 +72,8 @@ public final class Utils {
7272
private static final String JAVA_SHORT = "java.lang.Short";
7373

7474
private static final String JAVADOC_LINK_START_DELIMITER = "{@link";
75-
private static final String JAVADOC_VALUE_START_DELIMITER = "{@value";
76-
private static final String JAVADOC_SUBSTITUTION_PATTERN_STOP_DELIMITER = "}";
75+
public static final String JAVADOC_VALUE_START_DELIMITER = "{@value";
76+
public static final String JAVADOC_SUBSTITUTION_PATTERN_STOP_DELIMITER = "}";
7777

7878
private static Map<String, String> primitiveToBoxed = new HashMap<>();
7979

frank-doc-doclet/src/main/java/org/frankframework/frankdoc/model/FrankElement.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,11 @@ void handleAncestorMethod(FrankClass ancestorClass) {
196196
}
197197

198198
private void handlePossibleParameters(FrankClass clazz) {
199-
this.meaningOfParameters = clazz.getJavaDocTag(JAVADOC_PARAMETERS);
199+
try {
200+
this.meaningOfParameters = Utils.replaceClassFieldValue(clazz.getJavaDocTag(JAVADOC_PARAMETERS), clazz);
201+
} catch(FrankDocException e) {
202+
log.error("Error parsing the meaning of parameters", e);
203+
}
200204
assembleParsedJavaDocTags(clazz, JAVADOC_PARAMETER, p -> this.specificParameters.add(p));
201205
}
202206

@@ -221,7 +225,7 @@ private void assembleParsedJavaDocTags(FrankClass clazz, String tagName, Consume
221225
for (String arguments : clazz.getAllJavaDocTagsOf(tagName)) {
222226
ParsedJavaDocTag parsed;
223227
try {
224-
parsed = ParsedJavaDocTag.getInstance(arguments);
228+
parsed = ParsedJavaDocTag.getInstance(arguments, s -> Utils.replaceClassFieldValue(s, clazz));
225229
} catch (FrankDocException e) {
226230
log.error("Error parsing a [{}] tag of class [{}]", tagName, fullName, e);
227231
continue;

frank-doc-doclet/src/main/java/org/frankframework/frankdoc/model/ParsedJavaDocTag.java

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2021 WeAreFrank!
2+
Copyright 2021, 2024 WeAreFrank!
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -19,29 +19,42 @@
1919
import lombok.Getter;
2020
import org.apache.commons.lang3.StringUtils;
2121
import org.apache.logging.log4j.Logger;
22+
import org.frankframework.frankdoc.Utils;
23+
import org.frankframework.frankdoc.util.FrankDocThrowingFunction;
2224
import org.frankframework.frankdoc.util.LogUtil;
2325
import org.frankframework.frankdoc.wrapper.FrankDocException;
2426

27+
import java.util.Arrays;
28+
import java.util.List;
29+
import java.util.function.Function;
30+
2531
public class ParsedJavaDocTag {
26-
private static Logger log = LogUtil.getLogger(ParsedJavaDocTag.class);
32+
private static final Logger log = LogUtil.getLogger(ParsedJavaDocTag.class);
2733

2834
private static final String QUOTE = "\"";
2935

3036
private final @Getter String name;
3137
private final @Getter String description;
3238

33-
static ParsedJavaDocTag getInstance(String javaDocTagParameter) throws FrankDocException {
39+
static ParsedJavaDocTag getInstance(String javaDocTagParameter, FrankDocThrowingFunction valueSubstitutor) throws FrankDocException {
3440
if(StringUtils.isAllBlank(javaDocTagParameter)) {
3541
throw new FrankDocException("Tag has no arguments", null);
3642
}
43+
ParsedJavaDocTag raw = null;
3744
// The doclet API already trimmed the argument. We do not have to care about leading spaces.
3845
if(javaDocTagParameter.startsWith(QUOTE)) {
39-
return getInstanceQuoteDelimited(javaDocTagParameter);
46+
raw = getInstanceQuoteDelimited(javaDocTagParameter);
47+
} else {
48+
raw = getInstanceSpaceDelimited(javaDocTagParameter);
49+
}
50+
if (raw.description == null) {
51+
return new ParsedJavaDocTag(valueSubstitutor.apply(raw.name), null);
52+
} else {
53+
return new ParsedJavaDocTag(valueSubstitutor.apply(raw.name), valueSubstitutor.apply(raw.description));
4054
}
41-
return getInstanceSpaceDelimited(javaDocTagParameter);
4255
}
4356

44-
private static ParsedJavaDocTag getInstanceQuoteDelimited(String javaDocTagParameter) {
57+
private static ParsedJavaDocTag getInstanceQuoteDelimited(String javaDocTagParameter) throws FrankDocException {
4558
int startQuoteIdx = javaDocTagParameter.indexOf(QUOTE);
4659
int endQuoteIdx = javaDocTagParameter.indexOf(QUOTE, startQuoteIdx+1);
4760
if(endQuoteIdx < 0) {
@@ -56,8 +69,8 @@ private static ParsedJavaDocTag getInstanceQuoteDelimited(String javaDocTagParam
5669
return new ParsedJavaDocTag(name, description);
5770
}
5871

59-
private static ParsedJavaDocTag getInstanceSpaceDelimited(String javaDocTagParameter) {
60-
int idx = javaDocTagParameter.indexOf(" ");
72+
private static ParsedJavaDocTag getInstanceSpaceDelimited(String javaDocTagParameter) throws FrankDocException {
73+
int idx = ParsedJavaDocTag.getNameDescriptionSplitIndex(javaDocTagParameter);
6174
if(idx < 0) {
6275
return new ParsedJavaDocTag(javaDocTagParameter, null);
6376
}
@@ -69,6 +82,39 @@ private static ParsedJavaDocTag getInstanceSpaceDelimited(String javaDocTagParam
6982
return new ParsedJavaDocTag(name, description);
7083
}
7184

85+
private static int getNameDescriptionSplitIndex(String javadocTagParameter) throws FrankDocException {
86+
int idx = 0;
87+
while (idx < javadocTagParameter.length()) {
88+
idx = ParsedJavaDocTag.getIndexFirstMatchFromIndex(javadocTagParameter, idx, " ", Utils.JAVADOC_VALUE_START_DELIMITER);
89+
if (idx < 0) {
90+
// Not found
91+
return idx;
92+
}
93+
if (javadocTagParameter.charAt(idx) == ' ') {
94+
// Found the first space
95+
return idx;
96+
}
97+
// We do not have the space that splits the name and the description, but a {@value ... } pattern
98+
// Skip that.
99+
idx = javadocTagParameter.indexOf(Utils.JAVADOC_SUBSTITUTION_PATTERN_STOP_DELIMITER, idx);
100+
if (idx == -1) {
101+
throw new FrankDocException(String.format("Value substitution pattern %s not finished by %s",
102+
Utils.JAVADOC_VALUE_START_DELIMITER,
103+
Utils.JAVADOC_SUBSTITUTION_PATTERN_STOP_DELIMITER), null);
104+
}
105+
idx = idx + 1;
106+
}
107+
return -1;
108+
}
109+
110+
private static int getIndexFirstMatchFromIndex(String subject, int fromIndex, String ...searchItemsArg) {
111+
List<String> searchItems = Arrays.asList(searchItemsArg);
112+
return searchItems.stream().map(item -> subject.indexOf(item, fromIndex))
113+
.filter(i -> i >= 0)
114+
.min(Integer::compare)
115+
.orElse(-1);
116+
}
117+
72118
private ParsedJavaDocTag(String name, String description) {
73119
this.name = name;
74120
this.description = description;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
Copyright 2024 WeAreFrank!
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package org.frankframework.frankdoc.util;
17+
18+
import org.frankframework.frankdoc.wrapper.FrankDocException;
19+
20+
@FunctionalInterface
21+
public interface FrankDocThrowingFunction {
22+
String apply(String value) throws FrankDocException;
23+
}

frank-doc-doclet/src/test/java/org/frankframework/frankdoc/DocWriterNewAndJsonGenerationExamplesTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ public static Collection<Object[]> data() {
8888
{XsdVersion.STRICT, AttributeTypeStrategy.ALLOW_PROPERTY_REF, "general-test-digester-rules.xml", "org.frankframework.frankdoc.testtarget.examples.labels.Master", null, "labels.json"},
8989
{XsdVersion.STRICT, AttributeTypeStrategy.ALLOW_PROPERTY_REF, "general-test-digester-rules.xml", "org.frankframework.frankdoc.testtarget.examples.exclude.from.type.Master", "excludeFromType.xsd", "excludeFromType.json"},
9090
{XsdVersion.STRICT, AttributeTypeStrategy.ALLOW_PROPERTY_REF, "general-test-digester-rules.xml", "org.frankframework.frankdoc.testtarget.packageprivate.override.Child", "child.xsd", null},
91-
{XsdVersion.STRICT, AttributeTypeStrategy.ALLOW_PROPERTY_REF, "general-test-digester-rules.xml", "org.frankframework.frankdoc.testtarget.featurepackage.Documented", "documented.xsd", "documented.json"}
91+
{XsdVersion.STRICT, AttributeTypeStrategy.ALLOW_PROPERTY_REF, "general-test-digester-rules.xml", "org.frankframework.frankdoc.testtarget.featurepackage.Documented", "documented.xsd", "documented.json"},
92+
{XsdVersion.STRICT, AttributeTypeStrategy.ALLOW_PROPERTY_REF, "general-test-digester-rules.xml", "org.frankframework.frankdoc.testtarget.examples.valueSubs.WithValueSubstitutions", "valueSubs.xsd", "valueSubs.json"}
9293
});
9394
}
9495

frank-doc-doclet/src/test/java/org/frankframework/frankdoc/model/ParsedJavaDocTagTest.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,50 @@
77
import static org.junit.jupiter.api.Assertions.assertNull;
88
import static org.junit.jupiter.api.Assertions.assertThrows;
99

10+
/**
11+
* We do not test substituting \{@value \... \} in these tests. That would require
12+
* FrankClass objects, which would make this test too complicated.
13+
*/
1014
public class ParsedJavaDocTagTest {
1115
@Test
1216
public void whenParamTagHasNoSpaceThenOnlyName() throws Exception {
13-
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("myName");
17+
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("myName", s -> s);
1418
assertNull(p.getDescription());
1519
assertEquals("myName", p.getName());
1620
}
1721

1822
@Test
1923
public void whenParamTagHasSpacesAfterNameThenStillDescriptionNull() throws Exception {
20-
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("myName ");
24+
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("myName ", s -> s);
2125
assertNull(p.getDescription());
2226
assertEquals("myName", p.getName());
2327
}
2428

2529
@Test
2630
public void whenParamTagHasMultipleWordsThenNameAndDescription() throws Exception {
27-
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("myName Description ");
31+
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("myName Description ", s -> s);
2832
assertEquals("myName", p.getName());
2933
assertEquals("Description", p.getDescription());
3034
}
3135

3236
@Test
3337
public void whenAnnotationValueStartsWithQuoteThenNameIsStringUntilEndQuote() throws Exception {
34-
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("\"My quoted name\" Description");
38+
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("\"My quoted name\" Description", s -> s);
3539
assertEquals("My quoted name", p.getName());
3640
assertEquals("Description", p.getDescription());
3741
}
3842

3943
@Test
4044
public void whenQuotedForwardNameIsEmptyStringAndDescriptionRightAfterLastQuoteThenNoError() throws Exception {
41-
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("\"\"Description");
45+
ParsedJavaDocTag p = ParsedJavaDocTag.getInstance("\"\"Description", s -> s);
4246
assertEquals("", p.getName());
4347
assertEquals("Description", p.getDescription());
4448
}
4549

4650
@Test
4751
public void whenJavaDocHasNoParametersThenError() throws Exception {
4852
assertThrows(FrankDocException.class, () -> {
49-
ParsedJavaDocTag.getInstance("");
53+
ParsedJavaDocTag.getInstance("", s -> s);
5054
});
5155
}
5256
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.frankframework.frankdoc.testtarget.examples.valueSubs;
2+
3+
/**
4+
* In the class description, we substitute {@value WithValueSubstitutions#MY_CONSTANT}
5+
*
6+
* @ff.parameters In the text that defines the meaning of parameters, we substitute {@value WithValueSubstitutions#MY_CONSTANT}
7+
*
8+
* @ff.parameter {@value WithValueSubstitutions#MY_CONSTANT} Description of this parameter is {@value WithValueSubstitutions#MY_CONSTANT}
9+
*
10+
* @ff.forward {@value WithValueSubstitutions#MY_CONSTANT} Forward description {@value WithValueSubstitutions#MY_CONSTANT}
11+
*/
12+
public class Other {
13+
14+
/**
15+
* Attribute description that cites {@value WithValueSubstitutions#MY_CONSTANT}
16+
* @param value
17+
*/
18+
public void setMyAttribute(String value) {
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.frankframework.frankdoc.testtarget.examples.valueSubs;
2+
3+
/**
4+
* In the class description, we substitute {@value #MY_CONSTANT}
5+
*
6+
* @ff.parameters In the text that defines the meaning of parameters, we substitute {@value #MY_CONSTANT}
7+
*
8+
* @ff.parameter {@value #MY_CONSTANT} Description of this parameter is {@value #MY_CONSTANT}
9+
*
10+
* @ff.forward {@value #MY_CONSTANT} Forward description {@value #MY_CONSTANT}
11+
*/
12+
public class WithValueSubstitutions {
13+
public static final String MY_CONSTANT = "my-constant";
14+
15+
/**
16+
* In an attribute description, we substitute {@value #MY_CONSTANT}
17+
*/
18+
public void setMyAttribute(String value) {
19+
}
20+
21+
public void registerA(Other child) {
22+
}
23+
}

0 commit comments

Comments
 (0)