Skip to content

Commit dbb5191

Browse files
[#1865] fix javadoc for class / enums + add test case to check javadoc stability
1 parent 78a4691 commit dbb5191

File tree

8 files changed

+248
-41
lines changed

8 files changed

+248
-41
lines changed

jaxb-ri/codemodel/codemodel/src/main/java/com/sun/codemodel/JCommentPart.java

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111

1212
package com.sun.codemodel;
1313

14-
import java.util.AbstractMap;
14+
import com.sun.codemodel.util.Util;
15+
1516
import java.util.ArrayList;
1617
import java.util.Collection;
1718
import java.util.Iterator;
18-
import java.util.List;
1919

2020
/**
2121
* A part is a part of a javadoc comment, and it is a list of values.
@@ -34,13 +34,6 @@
3434
public class JCommentPart extends ArrayList<Object> {
3535

3636
private static final long serialVersionUID = 1L;
37-
private static final List<AbstractMap.SimpleImmutableEntry<String, String>> ESCAPED_XML_JAVADOC = new ArrayList<>();
38-
static {
39-
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("&", "&amp;"));
40-
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("<", "&lt;"));
41-
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>(">", "&gt;"));
42-
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("@", "&#064;"));
43-
}
4437

4538
protected JCommentPart() {
4639
}
@@ -63,7 +56,7 @@ public JCommentPart append(Object o) {
6356
* Otherwise it will be converted to String via {@link Object#toString()}.
6457
*/
6558
public JCommentPart appendXML(String s) {
66-
add(escapeXML(s));
59+
add(Util.escapeXML(s));
6760
return this;
6861
}
6962

@@ -133,29 +126,6 @@ protected void format( JFormatter f, String indent ) {
133126
f.nl();
134127
}
135128

136-
/**
137-
* Escapes the XML tags for Javadoc compatibility
138-
*/
139-
private String escapeXML(String s) {
140-
if (s == null) {
141-
return s;
142-
}
143-
for (AbstractMap.SimpleImmutableEntry<String, String> entry : ESCAPED_XML_JAVADOC) {
144-
int entryKeyLength = entry.getKey().length();
145-
int entryValueLength = entry.getValue().length();
146-
int idx = -1;
147-
while (true) {
148-
idx = s.indexOf(entry.getKey(), idx);
149-
if (idx < 0) {
150-
break;
151-
}
152-
s = s.substring(0, idx) + entry.getValue() + s.substring(idx + entryKeyLength);
153-
idx += entryValueLength;
154-
}
155-
}
156-
return s;
157-
}
158-
159129
/**
160130
* Escapes the appearance of the comment terminator.
161131
*/
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Distribution License v. 1.0, which is available at
6+
* http://www.eclipse.org/org/documents/edl-v10.php.
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
package com.sun.codemodel.util;
12+
13+
import java.util.AbstractMap;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
/**
18+
* This class aims to provide some util methods used in codemodel and dependencies project.
19+
* <br><br>
20+
* For now, it allows user to escape some dangerous characters for javadoc generation processing
21+
*/
22+
public class Util {
23+
24+
private static final List<AbstractMap.SimpleImmutableEntry<String, String>> ESCAPED_XML_JAVADOC = new ArrayList<>();
25+
static {
26+
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("&", "&amp;"));
27+
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("<", "&lt;"));
28+
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>(">", "&gt;"));
29+
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("@", "&#064;"));
30+
}
31+
32+
private Util() {}
33+
34+
/**
35+
* Escapes the XML tags for Javadoc compatibility
36+
*/
37+
public static String escapeXML(String s) {
38+
if (s == null) {
39+
return s;
40+
}
41+
for (AbstractMap.SimpleImmutableEntry<String, String> entry : ESCAPED_XML_JAVADOC) {
42+
int entryKeyLength = entry.getKey().length();
43+
int entryValueLength = entry.getValue().length();
44+
int idx = -1;
45+
while (true) {
46+
idx = s.indexOf(entry.getKey(), idx);
47+
if (idx < 0) {
48+
break;
49+
}
50+
s = s.substring(0, idx) + entry.getValue() + s.substring(idx + entryKeyLength);
51+
idx += entryValueLength;
52+
}
53+
}
54+
return s;
55+
}
56+
}

jaxb-ri/xjc/src/main/java/com/sun/tools/xjc/generator/bean/BeanGenerator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ private void generateClassBody(ClassOutlineImpl cc) {
557557
}
558558

559559
// generate some class level javadoc
560-
cc.ref.javadoc().appendXML(target.javadoc);
560+
cc.ref.javadoc().append(target.javadoc);
561561

562562
cc._package().objectFactoryGenerator().populate(cc);
563563
}
@@ -616,7 +616,7 @@ private EnumOutline generateEnumDef(CEnumLeafInfo e) {
616616

617617
type = getClassFactory().createClass(
618618
getContainer(e.parent, EXPOSED), e.shortName, e.getLocator(), ClassType.ENUM);
619-
type.javadoc().appendXML(e.javadoc);
619+
type.javadoc().append(e.javadoc);
620620

621621
return new EnumOutline(e, type) {
622622

@@ -683,7 +683,7 @@ private void generateEnumBody(EnumOutline eo) {
683683

684684
// set javadoc
685685
if (mem.javadoc != null) {
686-
constRef.javadoc().appendXML(mem.javadoc);
686+
constRef.javadoc().append(mem.javadoc);
687687
}
688688

689689
eo.constants.add(new EnumConstantOutline(mem, constRef) {

jaxb-ri/xjc/src/main/java/com/sun/tools/xjc/reader/xmlschema/ClassSelector.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
56
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -20,6 +21,7 @@
2021
import com.sun.codemodel.JCodeModel;
2122
import com.sun.codemodel.JJavaName;
2223
import com.sun.codemodel.JPackage;
24+
import com.sun.codemodel.util.Util;
2325
import com.sun.istack.NotNull;
2426
import com.sun.tools.xjc.model.CBuiltinLeafInfo;
2527
import com.sun.tools.xjc.model.CClassInfo;
@@ -368,7 +370,7 @@ private void addSchemaFragmentJavadoc( CClassInfo bean, XSComponent sc ) {
368370
// first, pick it up from <documentation> if any.
369371
String doc = builder.getDocumentation(sc);
370372
if (doc != null) {
371-
append(bean, doc);
373+
append(bean, Util.escapeXML(doc));
372374
}
373375

374376
// then the description of where this component came from

jaxb-ri/xjc/src/main/java/com/sun/tools/xjc/reader/xmlschema/SimpleTypeBuilder.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
56
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -23,6 +24,7 @@
2324
import java.util.Set;
2425
import java.util.Stack;
2526

27+
import com.sun.codemodel.util.Util;
2628
import jakarta.activation.MimeTypeParseException;
2729
import jakarta.xml.bind.DatatypeConverter;
2830

@@ -367,7 +369,7 @@ private TypeUse find( XSSimpleType type ) {
367369
// list and union cannot be mapped to a type-safe enum,
368370
// so in this stage we can safely cast it to XSRestrictionSimpleType
369371
return bindToTypeSafeEnum( (XSRestrictionSimpleType)type,
370-
en.className, en.javadoc, en.members,
372+
en.className, Util.escapeXML(en.javadoc), en.members,
371373
getEnumMemberMode().getModeWithEnum(),
372374
en.getLocation() );
373375
}
@@ -394,7 +396,7 @@ private TypeUse find( XSSimpleType type ) {
394396
String documentation = "";
395397
String extDocumentation = builder.getDocumentation(type);
396398
if (extDocumentation != null) {
397-
documentation = extDocumentation;
399+
documentation = Util.escapeXML(extDocumentation);
398400
}
399401

400402
// see if this type should be mapped to a type-safe enumeration by default.
@@ -649,7 +651,7 @@ private List<CEnumConstant> buildCEnumConstants(XSRestrictionSimpleType type, bo
649651

650652
for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) {
651653
String name=null;
652-
String mdoc= builder.getDocumentation(facet);
654+
String mdoc= Util.escapeXML(builder.getDocumentation(facet));
653655

654656
if(!enums.add(facet.getValue().value))
655657
continue; // ignore the 2nd occasion

jaxb-ri/xjc/src/test/java/com/sun/tools/xjc/CodeGenTest.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,73 @@ public void testIssue1788() throws FileNotFoundException, URISyntaxException {
223223
}
224224
}
225225

226+
/**
227+
* Test issues #1865 for good javadoc generation
228+
*
229+
* @throws FileNotFoundException When the test schema or binding file cannot be read.
230+
* @throws URISyntaxException When the test {@link InputSource} cannot be parsed.
231+
*
232+
* @see <a href="https://github.com/eclipse-ee4j/jaxb-ri/issues/1788">Issue #1788</a>
233+
*/
234+
public void testIssue1865() throws FileNotFoundException, URISyntaxException {
235+
String schemaFileName = "/schemas/issue1865/simple.xsd";
236+
String packageName = "org.example.issue1865";
237+
String someClassName = packageName + ".TestEscapeJavadoc";
238+
String someEnumName = packageName + ".FooEnum";
239+
240+
ErrorListener errorListener = new ConsoleErrorReporter();
241+
242+
// Parse the XML schema.
243+
SchemaCompiler sc = XJC.createSchemaCompiler();
244+
sc.setErrorListener(errorListener);
245+
sc.forcePackageName(packageName);
246+
sc.parseSchema(getInputSource(schemaFileName));
247+
248+
// Generate the defined class.
249+
S2JJAXBModel model = sc.bind();
250+
Plugin[] extensions = null;
251+
JCodeModel cm = model.generateCode(extensions, errorListener);
252+
JDefinedClass dc = cm._getClass(someClassName);
253+
assertNotNull(someClassName, dc);
254+
StringWriter swClass = new StringWriter();
255+
dc.javadoc().generate(new JFormatter(swClass));
256+
assertNotNull(swClass.toString());
257+
assertTrue(swClass.toString().contains("Test escape Javadoc on Class &lt; &gt; &amp; ' \" *<!---->/ Hello World"));
258+
assertTrue(swClass.toString().contains(" * <pre>{@code\n" +
259+
" * <complexType name=\"testEscapeJavadoc\">\n" +
260+
" * <complexContent>\n" +
261+
" * <restriction base=\"{http://www.w3.org/2001/XMLSchema}anyType\">\n" +
262+
" * <sequence>\n" +
263+
" * <element name=\"lt\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n" +
264+
" * <element name=\"gt\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n" +
265+
" * <element name=\"amp\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n" +
266+
" * <element name=\"listLtGtAmp\" type=\"{http://www.w3.org/2001/XMLSchema}string\" maxOccurs=\"unbounded\" minOccurs=\"0\"/>\n" +
267+
" * <element name=\"apos\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n" +
268+
" * <element name=\"quot\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n" +
269+
" * <element name=\"comment\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n" +
270+
" * <element name=\"enum\" type=\"{}FooEnum\"/>\n" +
271+
" * </sequence>\n" +
272+
" * </restriction>\n" +
273+
" * </complexContent>\n" +
274+
" * </complexType>\n" +
275+
" * }</pre>"));
276+
277+
JDefinedClass ec = cm._getClass(someEnumName);
278+
assertNotNull(someEnumName, ec);
279+
StringWriter swEnum = new StringWriter();
280+
ec.javadoc().generate(new JFormatter(swEnum));
281+
assertNotNull(swEnum.toString());
282+
assertTrue(swEnum.toString().contains("Test escape Javadoc on Enum &lt; &gt; &amp; ' \" *<!---->/ Hello World"));
283+
assertTrue(swEnum.toString().contains(" * <pre>{@code\n" +
284+
" * <simpleType name=\"FooEnum\">\n" +
285+
" * <restriction base=\"{http://www.w3.org/2001/XMLSchema}string\">\n" +
286+
" * <enumeration value=\"BAR\"/>\n" +
287+
" * <enumeration value=\"BAZ\"/>\n" +
288+
" * </restriction>\n" +
289+
" * </simpleType>\n" +
290+
" * }</pre>"));
291+
}
292+
226293
private void assertNonEmptyJavadocBlocks(String cmString) throws IOException {
227294
int lineNo = 0;
228295
try ( LineNumberReader lnr = new LineNumberReader(new StringReader(cmString)) ) {

jaxb-ri/xjc/src/test/resources/com/sun/tools/xjc/resources/simple.xsd

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
<xsd:complexType name="testEscapeJavadoc">
1818
<xsd:annotation>
19-
<xsd:documentation>Test escape Javadoc &lt; &gt; &amp; &apos; &quot; */ Hello World</xsd:documentation>
19+
<xsd:documentation>Test escape Javadoc on Class &lt; &gt; &amp; &apos; &quot; */ Hello World</xsd:documentation>
2020
</xsd:annotation>
2121
<xsd:sequence>
2222
<xsd:element name="lt" type="xsd:string">
@@ -54,7 +54,33 @@
5454
<xsd:documentation>Test escape */</xsd:documentation>
5555
</xsd:annotation>
5656
</xsd:element>
57+
<xsd:element name="enum" type="FooEnum">
58+
<xsd:annotation>
59+
<xsd:documentation>Test enum</xsd:documentation>
60+
</xsd:annotation>
61+
</xsd:element>
5762
</xsd:sequence>
5863
</xsd:complexType>
64+
65+
<xsd:simpleType name="FooEnum">
66+
<xsd:annotation>
67+
<xsd:appinfo>
68+
<xsd:documentation>Test escape Javadoc on Enum &lt; &gt; &amp; &apos; &quot; */ Hello World</xsd:documentation>
69+
</xsd:appinfo>
70+
</xsd:annotation>
71+
72+
<xsd:restriction base="xsd:string">
73+
<xsd:enumeration value="BAR">
74+
<xsd:annotation>
75+
<xsd:documentation>BAR &lt; enum value</xsd:documentation>
76+
</xsd:annotation>
77+
</xsd:enumeration>
78+
<xsd:enumeration value="BAZ">
79+
<xsd:annotation>
80+
<xsd:documentation>BAZ &gt; enum value</xsd:documentation>
81+
</xsd:annotation>
82+
</xsd:enumeration>
83+
</xsd:restriction>
84+
</xsd:simpleType>
5985
</xsd:schema>
6086

0 commit comments

Comments
 (0)