Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.
Merged
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
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.13.4</junit.jupiter.version>
<beetl.version>3.19.2.RELEASE</beetl.version>
<mockito.version>4.11.0</mockito.version>
<beetl.version>3.19.2.RELEASE</beetl.version>
<common-util.version>2.2.9</common-util.version>
<qdox.version>2.0.3.5</qdox.version>
<datafaker.version>1.9.0</datafaker.version>
Expand All @@ -61,6 +62,12 @@
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
Expand Down
28 changes: 22 additions & 6 deletions src/main/java/com/ly/doc/utils/DocUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2024 smart-doc
* Copyright (C) 2018-2025 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
Expand All @@ -18,6 +18,7 @@
* specific language governing permissions and limitations
* under the License.
*/

package com.ly.doc.utils;

import com.ly.doc.builder.WordDocBuilder;
Expand Down Expand Up @@ -745,12 +746,27 @@ private static Map<String, String> getCommentsByTag(List<DocletTag> paramTags, f
if (DocTags.PARAM.equals(tagName) || DocTags.EXTENSION.equals(tagName)) {
String pName = value;
String pValue = DocGlobalConstants.NO_COMMENTS_FOUND;
int idx = value.indexOf(" ");
// existed \n
if (idx > -1) {
pName = value.substring(0, idx);
pValue = value.substring(idx + 1);

// Fixed #1129
// Split the value by the first sequence of whitespace
// (space,newline,tab, etc.) only into two parts.
String[] parts = value.trim().split("\\s+", 2);
// Successfully split into parameter name and description
if (parts.length == 2) {
// Parameter name
pName = parts[0];
// Description, preserving internal newlines and formatting
pValue = parts[1];
}
// Only one part found, likely just the parameter name
// without a description, OR an empty/whitespace-only value after trim
else if (parts.length == 1) {
// Covers both "paramName" and "" cases
// pValue remains DocGlobalConstants.NO_COMMENTS_FOUND
// Set pName based on whether the single part is empty
pName = parts[0];
}

if ("|".equals(StringUtil.trim(pValue)) && StringUtil.isNotEmpty(className)) {
throw new RuntimeException(tagValErrorMsg);
}
Expand Down
149 changes: 148 additions & 1 deletion src/test/java/com/ly/doc/util/DocUtilTest.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
package com.ly.doc.util;

import com.ly.doc.constants.DocGlobalConstants;
import com.ly.doc.constants.DocLanguage;
import com.ly.doc.constants.DocTags;
import com.ly.doc.enums.IEnum;
import com.ly.doc.enums.OrderEnum;
import com.ly.doc.utils.DocUtil;
import com.ly.doc.constants.DocLanguage;
import com.thoughtworks.qdox.model.DocletTag;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

/**
* @author yu 2018/12/10.
*/
public class DocUtilTest {

/**
* Assuming DocTags.PARAM is "param"
*/
private static final String TAG_NAME_PARAM = DocTags.PARAM;

/**
* Assuming DocTags.EXTENSION is "extension"
*/
private static final String TAG_NAME_EXTENSION = DocTags.EXTENSION;

/**
* Assuming DocGlobalConstants.NO_COMMENTS_FOUND
*/
private static final String NO_COMMENTS_FOUND = DocGlobalConstants.NO_COMMENTS_FOUND;

@Test
public void test() {
System.setProperty(DocGlobalConstants.DOC_LANGUAGE, DocLanguage.CHINESE.getCode());
Expand Down Expand Up @@ -80,4 +104,127 @@ public void testReplaceGenericParameter() {
System.out.println(result); // Output: com.Test<List<Use
}

// Helper method to create a mock DocletTag
private DocletTag createMockTag(String value) {
DocletTag mockTag = Mockito.mock(DocletTag.class);
when(mockTag.getValue()).thenReturn(value);
return mockTag;
}

@Test
public void testGetCommentsByTag_ParamWithNewlines() {
// Scenario 1: Normal case - param name and description on same line
DocletTag tag1 = createMockTag("id user identifier");
// Scenario 2: Description starts on a new line with indentation
DocletTag tag2 = createMockTag("name\n user's full name");
// Scenario 3: Description has multiple newlines and complex formatting
DocletTag tag3 = createMockTag(
"age user's age in years\n Default is 25.\n NOTE: Must be > 0.");
// Scenario 4: Only parameter name, no description
DocletTag tag4 = createMockTag("flag");
// Scenario 5: Parameter name with leading/trailing whitespace in the raw value
// part that's not the name itself
DocletTag tag5 = createMockTag(" email user's email address ");
// Scenario 6: Description starts immediately after parameter name WITH A SPACE
// (Standard Javadoc format)
DocletTag tag6 = createMockTag("status Active user status flag");
// Scenario 7: Description contains leading whitespace after the parameter name
DocletTag tag7 = createMockTag("count Number of items to retrieve");

List<DocletTag> tags = Arrays.asList(tag1, tag2, tag3, tag4, tag5, tag6, tag7);

Map<String, String> result = DocUtil.getCommentsByTag(tags, TAG_NAME_PARAM);

// Verify results
assertEquals("user identifier", result.get("id"), "Scenario 1: Description should be 'user identifier'");
assertEquals("user's full name", result.get("name"),
"Scenario 2: Description should be 'user's full name', preserving content after newline.");
assertEquals("user's age in years\n Default is 25.\n NOTE: Must be > 0.",
result.get("age"), "Scenario 3: Description should preserve newlines and formatting.");
assertEquals(NO_COMMENTS_FOUND, result.get("flag"), "Scenario 4: Should return NO_COMMENTS_FOUND for 'flag'");
assertEquals("user's email address", result.get("email"),
"Scenario 5: Name should be 'email', description should be 'user's email address'");
assertEquals("Active user status flag", result.get("status"),
"Scenario 6: Description should be 'Active user status flag'");
assertEquals("Number of items to retrieve", result.get("count"),
"Scenario 7: Description should be 'Number of items to retrieve'");

}

@Test
public void testGetCommentsByTag_ExtensionWithNewlines() {
// Similar scenarios for @extension
DocletTag tag1 = createMockTag("extId extension identifier");
DocletTag tag2 = createMockTag("extName\n extension name");
DocletTag tag3 = createMockTag("extConfig extension configuration details\n with multiple lines.");
DocletTag tag4 = createMockTag("extFlag");

List<DocletTag> tags = Arrays.asList(tag1, tag2, tag3, tag4);

Map<String, String> result = DocUtil.getCommentsByTag(tags, TAG_NAME_EXTENSION);

// Verify results
assertEquals("extension identifier", result.get("extId"));
assertEquals("extension name", result.get("extName"));
assertEquals("extension configuration details\n with multiple lines.", result.get("extConfig"));
assertEquals(NO_COMMENTS_FOUND, result.get("extFlag"));
}

@Test
public void testGetCommentsByTag_ParamWithCarriageReturn() {
// Scenario: Description has Windows-style CRLF (\r\n)
String valueWithCRLF = "token\r\n Authentication token,\r\n required for access.";
DocletTag tag = createMockTag(valueWithCRLF);

List<DocletTag> tags = Collections.singletonList(tag);
Map<String, String> result = DocUtil.getCommentsByTag(tags, TAG_NAME_PARAM);

assertEquals("Authentication token,\r\n required for access.", result.get("token"),
"Should handle CRLF correctly, preserving the newline and content.");
}

@Test
public void testGetCommentsByTag_ParamOnlyName() {
// Scenario: Only parameter name exists, no space/description
DocletTag tag = createMockTag("justParamName");
List<DocletTag> tags = Collections.singletonList(tag);
Map<String, String> result = DocUtil.getCommentsByTag(tags, TAG_NAME_PARAM);

assertEquals(NO_COMMENTS_FOUND, result.get("justParamName"),
"Should return NO_COMMENTS_FOUND for 'justParamName'");
}

@Test
public void testGetCommentsByTag_ParamEmptyValue() {
// Scenario: Empty value string
DocletTag tag = createMockTag("");
List<DocletTag> tags = Collections.singletonList(tag);
Map<String, String> result = DocUtil.getCommentsByTag(tags, TAG_NAME_PARAM);

// An empty string after trim results in [""], so parts[0] is "", and parts.length
// is 1.
// The logic assigns pName = parts[0] (which is ""), and pValue remains the
// default NO_COMMENTS_FOUND.
// The map.put(pName.trim(), pValue) effectively does map.put("",
// NO_COMMENTS_FOUND).
assertEquals(NO_COMMENTS_FOUND, result.get(""), // 修正 NO_COMMENTS_FOUND 的值
"Should handle empty value, key should be empty string.");
assertTrue(result.containsKey(""), "Map should contain an entry with an empty string key.");
}

@Test
public void testGetCommentsByTag_ParamWhitespaceOnlyValue() {
// Scenario: Value is only whitespace
DocletTag tag = createMockTag(" \t \n ");
List<DocletTag> tags = Collections.singletonList(tag);
Map<String, String> result = DocUtil.getCommentsByTag(tags, TAG_NAME_PARAM);

// Whitespace-only string after trim becomes "", same as empty value scenario.
// After trim and split, pName should be "" and pValue should be
// NO_COMMENTS_FOUND.
assertEquals(NO_COMMENTS_FOUND, result.get(""),
"Should handle whitespace-only value, key should be empty string.");
assertTrue(result.containsKey(""), "Map should contain an entry with an empty string key.");
}

}