Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@

package com.sun.codemodel;

import java.util.AbstractMap;
import com.sun.codemodel.util.Util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

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

private static final long serialVersionUID = 1L;
private static final List<AbstractMap.SimpleImmutableEntry<String, String>> ESCAPED_XML_JAVADOC = new ArrayList<>();
static {
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("&", "&amp;"));
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("<", "&lt;"));
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>(">", "&gt;"));
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("@", "&#064;"));
}

protected JCommentPart() {
}
Expand All @@ -63,7 +56,7 @@ public JCommentPart append(Object o) {
* Otherwise it will be converted to String via {@link Object#toString()}.
*/
public JCommentPart appendXML(String s) {
add(escapeXML(s));
add(Util.escapeXML(s));
return this;
}

Expand Down Expand Up @@ -133,29 +126,6 @@ protected void format( JFormatter f, String indent ) {
f.nl();
}

/**
* Escapes the XML tags for Javadoc compatibility
*/
private String escapeXML(String s) {
if (s == null) {
return s;
}
for (AbstractMap.SimpleImmutableEntry<String, String> entry : ESCAPED_XML_JAVADOC) {
int entryKeyLength = entry.getKey().length();
int entryValueLength = entry.getValue().length();
int idx = -1;
while (true) {
idx = s.indexOf(entry.getKey(), idx);
if (idx < 0) {
break;
}
s = s.substring(0, idx) + entry.getValue() + s.substring(idx + entryKeyLength);
idx += entryValueLength;
}
}
return s;
}

/**
* Escapes the appearance of the comment terminator.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

package com.sun.codemodel.util;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;

/**
* This class aims to provide some util methods used in codemodel and dependencies project.
* <br><br>
* For now, it allows user to escape some dangerous characters for javadoc generation processing
*/
public class Util {

private static final List<AbstractMap.SimpleImmutableEntry<String, String>> ESCAPED_XML_JAVADOC = new ArrayList<>();
static {
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("&", "&amp;"));
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("<", "&lt;"));
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>(">", "&gt;"));
ESCAPED_XML_JAVADOC.add(new AbstractMap.SimpleImmutableEntry<>("@", "&#064;"));
}

private Util() {}

/**
* Escapes the XML tags for Javadoc compatibility
*/
public static String escapeXML(String s) {
if (s == null) {
return s;
}
for (AbstractMap.SimpleImmutableEntry<String, String> entry : ESCAPED_XML_JAVADOC) {
int entryKeyLength = entry.getKey().length();
int entryValueLength = entry.getValue().length();
int idx = -1;
while (true) {
idx = s.indexOf(entry.getKey(), idx);
if (idx < 0) {
break;
}
s = s.substring(0, idx) + entry.getValue() + s.substring(idx + entryKeyLength);
idx += entryValueLength;
}
}
return s;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ private void generateClassBody(ClassOutlineImpl cc) {
}

// generate some class level javadoc
cc.ref.javadoc().appendXML(target.javadoc);
cc.ref.javadoc().append(target.javadoc);

cc._package().objectFactoryGenerator().populate(cc);
}
Expand Down Expand Up @@ -616,7 +616,7 @@ private EnumOutline generateEnumDef(CEnumLeafInfo e) {

type = getClassFactory().createClass(
getContainer(e.parent, EXPOSED), e.shortName, e.getLocator(), ClassType.ENUM);
type.javadoc().appendXML(e.javadoc);
type.javadoc().append(e.javadoc);

return new EnumOutline(e, type) {

Expand Down Expand Up @@ -683,7 +683,7 @@ private void generateEnumBody(EnumOutline eo) {

// set javadoc
if (mem.javadoc != null) {
constRef.javadoc().appendXML(mem.javadoc);
constRef.javadoc().append(mem.javadoc);
}

eo.constants.add(new EnumConstantOutline(mem, constRef) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
Expand All @@ -20,6 +21,7 @@
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JJavaName;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.util.Util;
import com.sun.istack.NotNull;
import com.sun.tools.xjc.model.CBuiltinLeafInfo;
import com.sun.tools.xjc.model.CClassInfo;
Expand Down Expand Up @@ -368,7 +370,7 @@ private void addSchemaFragmentJavadoc( CClassInfo bean, XSComponent sc ) {
// first, pick it up from <documentation> if any.
String doc = builder.getDocumentation(sc);
if (doc != null) {
append(bean, doc);
append(bean, Util.escapeXML(doc));
}

// then the description of where this component came from
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
Expand All @@ -23,6 +24,7 @@
import java.util.Set;
import java.util.Stack;

import com.sun.codemodel.util.Util;
import jakarta.activation.MimeTypeParseException;
import jakarta.xml.bind.DatatypeConverter;

Expand Down Expand Up @@ -367,7 +369,7 @@ private TypeUse find( XSSimpleType type ) {
// list and union cannot be mapped to a type-safe enum,
// so in this stage we can safely cast it to XSRestrictionSimpleType
return bindToTypeSafeEnum( (XSRestrictionSimpleType)type,
en.className, en.javadoc, en.members,
en.className, Util.escapeXML(en.javadoc), en.members,
getEnumMemberMode().getModeWithEnum(),
en.getLocation() );
}
Expand All @@ -394,7 +396,7 @@ private TypeUse find( XSSimpleType type ) {
String documentation = "";
String extDocumentation = builder.getDocumentation(type);
if (extDocumentation != null) {
documentation = extDocumentation;
documentation = Util.escapeXML(extDocumentation);
}

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

for( XSFacet facet : type.getDeclaredFacets(XSFacet.FACET_ENUMERATION)) {
String name=null;
String mdoc= builder.getDocumentation(facet);
String mdoc= Util.escapeXML(builder.getDocumentation(facet));

if(!enums.add(facet.getValue().value))
continue; // ignore the 2nd occasion
Expand Down
67 changes: 67 additions & 0 deletions jaxb-ri/xjc/src/test/java/com/sun/tools/xjc/CodeGenTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,73 @@ public void testIssue1788() throws FileNotFoundException, URISyntaxException {
}
}

/**
* Test issues #1865 for good javadoc generation
*
* @throws FileNotFoundException When the test schema or binding file cannot be read.
* @throws URISyntaxException When the test {@link InputSource} cannot be parsed.
*
* @see <a href="https://github.com/eclipse-ee4j/jaxb-ri/issues/1788">Issue #1788</a>
*/
public void testIssue1865() throws FileNotFoundException, URISyntaxException {
String schemaFileName = "/schemas/issue1865/simple.xsd";
String packageName = "org.example.issue1865";
String someClassName = packageName + ".TestEscapeJavadoc";
String someEnumName = packageName + ".FooEnum";

ErrorListener errorListener = new ConsoleErrorReporter();

// Parse the XML schema.
SchemaCompiler sc = XJC.createSchemaCompiler();
sc.setErrorListener(errorListener);
sc.forcePackageName(packageName);
sc.parseSchema(getInputSource(schemaFileName));

// Generate the defined class.
S2JJAXBModel model = sc.bind();
Plugin[] extensions = null;
JCodeModel cm = model.generateCode(extensions, errorListener);
JDefinedClass dc = cm._getClass(someClassName);
assertNotNull(someClassName, dc);
StringWriter swClass = new StringWriter();
dc.javadoc().generate(new JFormatter(swClass));
assertNotNull(swClass.toString());
assertTrue(swClass.toString().contains("Test escape Javadoc on Class &lt; &gt; &amp; ' \" *<!---->/ Hello World"));
assertTrue(swClass.toString().contains(" * <pre>{@code" + System.lineSeparator() +
" * <complexType name=\"testEscapeJavadoc\">" + System.lineSeparator() +
" * <complexContent>" + System.lineSeparator() +
" * <restriction base=\"{http://www.w3.org/2001/XMLSchema}anyType\">" + System.lineSeparator() +
" * <sequence>" + System.lineSeparator() +
" * <element name=\"lt\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>" + System.lineSeparator() +
" * <element name=\"gt\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>" + System.lineSeparator() +
" * <element name=\"amp\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>" + System.lineSeparator() +
" * <element name=\"listLtGtAmp\" type=\"{http://www.w3.org/2001/XMLSchema}string\" maxOccurs=\"unbounded\" minOccurs=\"0\"/>" + System.lineSeparator() +
" * <element name=\"apos\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>" + System.lineSeparator() +
" * <element name=\"quot\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>" + System.lineSeparator() +
" * <element name=\"comment\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>" + System.lineSeparator() +
" * <element name=\"enum\" type=\"{}FooEnum\"/>" + System.lineSeparator() +
" * </sequence>" + System.lineSeparator() +
" * </restriction>" + System.lineSeparator() +
" * </complexContent>" + System.lineSeparator() +
" * </complexType>" + System.lineSeparator() +
" * }</pre>"));

JDefinedClass ec = cm._getClass(someEnumName);
assertNotNull(someEnumName, ec);
StringWriter swEnum = new StringWriter();
ec.javadoc().generate(new JFormatter(swEnum));
assertNotNull(swEnum.toString());
assertTrue(swEnum.toString().contains("Test escape Javadoc on Enum &lt; &gt; &amp; ' \" *<!---->/ Hello World"));
assertTrue(swEnum.toString().contains(" * <pre>{@code" + System.lineSeparator() +
" * <simpleType name=\"FooEnum\">" + System.lineSeparator() +
" * <restriction base=\"{http://www.w3.org/2001/XMLSchema}string\">" + System.lineSeparator() +
" * <enumeration value=\"BAR\"/>" + System.lineSeparator() +
" * <enumeration value=\"BAZ\"/>" + System.lineSeparator() +
" * </restriction>" + System.lineSeparator() +
" * </simpleType>" + System.lineSeparator() +
" * }</pre>"));
}

private void assertNonEmptyJavadocBlocks(String cmString) throws IOException {
int lineNo = 0;
try ( LineNumberReader lnr = new LineNumberReader(new StringReader(cmString)) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

<xsd:complexType name="testEscapeJavadoc">
<xsd:annotation>
<xsd:documentation>Test escape Javadoc &lt; &gt; &amp; &apos; &quot; */ Hello World</xsd:documentation>
<xsd:documentation>Test escape Javadoc on Class &lt; &gt; &amp; &apos; &quot; */ Hello World</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="lt" type="xsd:string">
Expand Down Expand Up @@ -54,7 +54,33 @@
<xsd:documentation>Test escape */</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="enum" type="FooEnum">
<xsd:annotation>
<xsd:documentation>Test enum</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="FooEnum">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>Test escape Javadoc on Enum &lt; &gt; &amp; &apos; &quot; */ Hello World</xsd:documentation>
</xsd:appinfo>
</xsd:annotation>

<xsd:restriction base="xsd:string">
<xsd:enumeration value="BAR">
<xsd:annotation>
<xsd:documentation>BAR &lt; enum value</xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="BAZ">
<xsd:annotation>
<xsd:documentation>BAZ &gt; enum value</xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

Loading