【问题标题】:char constant display in Javadoc "Constant Field Values" pageJavadoc“常量字段值”页面中的字符常量显示
【发布时间】:2023-04-04 23:32:01
【问题描述】:

有没有办法让生成的“常量字段值”页面使用可读字符而不是整数作为char 类型常量?

例如,如果一个类包含以下常量:

public static final char YES = 'Y';
public static final char NO  = 'N';
public static final char ERROR  = 'E';

我希望 javadoc 常量字段值页面具有字符表示('Y'、'N'、'E')而不是整数版本(69、78、89)。

我们有数百个这样的,所以我不想去为每个事件添加一些特定的 javadoc 注释(例如,使用 @value)。


更新:感谢下面SubOptimal的回答,我设法使用我找到hereConstantsSummaryWriterImpl的1.6版源代码,修改writeValue方法如下:

private void writeValue(FieldDoc member) {
    tdAlign("right");
    code();
    if ("char".equals(member.type().toString())) {
        print(Util.escapeHtmlChars("'" + ((char) ((Integer)member.constantValue()).intValue()) + "'"));

    } else {
        print(Util.escapeHtmlChars(member.constantValueExpression()));
    }
    codeEnd();
    tdEnd();
}

一旦编译该类并用于覆盖“tools.jar”中的类,char 常量将作为字符而不是数字输出。

【问题讨论】:

  • 你提早授予赏金太仓促了。有一种正确的方法可以做到这一点,它不会破解现有的 javadoc 工具
  • @Bohemian 需要详细说明吗?或者你只是想把那个遥不可及的吊起来?我根据我得到的答案(包括你的答案)以及从周五到今天我将离开我的电脑这一事实授予了赏金。这只是酸葡萄,因为你的答案没有得到赏金,还是你有一个实际的解决方案?
  • 我对此进行了一些研究。 “正确”的做法是创建一个自定义类来处理标签,并通过命令行选项告诉 java 将其用于 @value 标签。我将使用装饰器模式扩展基类 - 为 char const 执行自定义行为,但对于其他所有内容都遵循超类的 impl。破解基类是一个糟糕的解决方案,因为每次升级 JDK 都必须重新编辑基类。
  • 是的,但这是否意味着我必须使用@value 标签?我不是。
  • 如果你不使用@value,我仍然会尝试找到一个使用扩展ConstantsSummaryWriterImpl 或类似的自定义类的解决方案。关键是,理想情况下,您需要一个可在 java 版本之间移植的解决方案。

标签: java constants javadoc


【解决方案1】:

您可以通过稍微调整javadoc 本身来实现这一点。

  1. 获取 javadoc 工具的源代码(别担心我们只需要一个文件来调整 ;-))
    我从这里JDK 7 langtools source 拿了文件langtools-ce654f4ecfd8.tar.gz
  2. 解压文件ConstantsSummaryWriterImpl.java
  3. 修改方法getValue(FieldDoc member)

    ...
    private Content getValue(FieldDoc member) {
        Content valueContent;
        if ("char".equals(member.type().toString())) {
            valueContent = new StringContent("'" 
                + (char) ((Integer) member.constantValue()).intValue()
                + "'"
            );
        } else {
            valueContent = new StringContent(member.constantValueExpression());
        }
        Content code = HtmlTree.CODE(valueContent);
        return HtmlTree.TD(HtmlStyle.colLast, code);
    }
    ...
    
  4. 编译修改后的文件
    javac -cp ${JAVA_HOME}/lib/tools.jar -d . ConstantsSummaryWriterImpl.java

  5. 运行 javadoc
    javadoc -J-Xbootclasspath/p:.;${JAVA_HOME}/lib/tools.jar -d api/ Scratch.java

文件Scratch.java

public class Scratch {
    public static final char YES = 'Y';
    public static final char NO  = 'N';
    public static final char ERROR  = 'E';
}

【讨论】:

  • 这看起来像是一个可能的解决方案,谢谢。我会在第二天左右尝试一下,如果它对我们有用,我会给你赏金。
  • 顺便说一句,如果 char 值不是可打印字符,这可以正常工作吗?我们自己没有这些,但我只是在想,如果其他人看到这个并且他们的 char 值低于 32。
  • 好的,我找到了这个类的 1.6 版本here,但它没有一个名为“getValue”的方法。但是,它确实有一个名为“writeValue”的方法,它似乎输出了member.constantValueExpression()。我看到这实际上是一个 doclet。反正有没有用我自己的“覆盖”这个 doclet,而不是实际修改 tools.jar?互联网上似乎很少有这方面的文档。
  • 像魅力一样工作! (见我上面的更新)你赢了! :-)
  • @DuncanKinnear 这是关于编写自己的 doclet 的文档。 docs.oracle.com/javase/6/docs/technotes/guides/javadoc/doclet/…
【解决方案2】:

这是不可能的。不幸的是,javadoc 在这方面有点密集 - char 是一个 numeric 原语。

你可以通过使用字符串来解决它

public static final String YES = "Y";
public static final String NO  = "N";
public static final String ERROR  = "E";

并重构您的代码以使用您之前使用 char 常量的实用方法:

public static char asChar(String str) {
    if (str.length() != 1) {
         throw new IllegalArgumentException(str + " must be exactly 1 character long");
    }
    return str.charAt(0);
}

例如

if (ch == asChar(ERROR))

这有望使这种情况可以忍受。

注意参数检查,以确保您不会意外声明一个字符串,而不是单个字符。

【讨论】:

  • 抱歉,我对任何需要重构生产代码的解决方案都不感兴趣。我们现在太忙了,无法处理这类事情。我只是想让我们的支持部门更清楚地看到这些常量值。
  • 打印出一个 ASCII 图表并将其固定在每个支持人员的隔间中。
【解决方案3】:

后处理是否可行?

此程序遍历文件树并处理所有名为constant-values.html 的文件。它们是使用Jericho HTML Parser 加载的。检查名为constantValuesContainer 的表:如果第一列包含文本static final char,并且最后一列中的值是整数值,则将其替换为适当的字符。

请注意,这只是一个草图(但至少,在“它自己的”JavaDoc 上进行了测试;-))。更换条件可以细化。例如,应该使第一列中文本的匹配更加健壮(对于有人创建以char....开头的类的情况)。只有当整数值是一个有效的 ASCII 字符时才应该进行替换,以避免混淆像static final char CHAR_MAX = 0xFFFF 这样的常量。但是,如果(!)这样的后处理步骤是一种可行的方法,这样的调整应该很容易。

import static java.nio.file.FileVisitResult.CONTINUE;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.OutputDocument;
import net.htmlparser.jericho.Segment;
import net.htmlparser.jericho.Source;

public class ConstantsTest
{
    private static final char CHAR_A = 'A';
    protected static final char CHAR_B = 'B';
    static final char CHAR_C = 'C';
    public static final char CHAR_D = 'D';
    public static final char CHAR_E = 'E';
    public static final char CHAR_F = 'F';
    public static final int INT_65 = 65;

    public static void main(String[] args) throws MalformedURLException, IOException
    {
        Path path = FileSystems.getDefault().getPath("./doc/");
        Files.walkFileTree(path, new FileProcessor());
    }

    static class FileProcessor extends SimpleFileVisitor<Path>
    {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
        {
            //System.out.println("Visit "+file+" name '"+file.getFileName()+"'");
            if (file.getFileName().toString().equals("constant-values.html"))
            {
                process(file);
            }
            return CONTINUE;
        }
    }

    private static void process(Path file) 
    {
        try
        {
            doProcess(file);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    private static void doProcess(Path file) throws IOException
    {
        System.out.println("Processing "+file);
        Source source=new Source(file.toFile());
        OutputDocument outputDocument=new OutputDocument(source);
        for (Element element : source.getAllElementsByClass("constantValuesContainer"))
        {
            List<Element> colFirsts = element.getAllElementsByClass("colFirst");
            List<Element> colLasts = element.getAllElementsByClass("colLast");
            for (int i=0; i<colFirsts.size(); i++)
            {
                Element colFirst = colFirsts.get(i);
                Segment colFirstContent = colFirst.getContent();
                String colFirstText = colFirstContent.getTextExtractor().toString();

                // TODO Generalize this according to your needs,
                // maybe with some RegEx...
                if (colFirstText.contains("static final char"))
                {
                    Element colLast = colLasts.get(i);
                    List<Element> children = colLast.getAllElements("code");
                    if (children.size() == 1)
                    {
                        Element child = children.get(0);
                        Segment childContent = child.getContent();
                        String childText = childContent.getTextExtractor().toString();
                        try
                        {
                            int value = Integer.parseInt(childText);
                            outputDocument.replace(childContent, String.valueOf((char)value));
                        }
                        catch (NumberFormatException e)
                        {
                            System.out.println("Value is not a number: "+childText+", ignoring");
                        }
                    }
                }
            }
        }
        outputDocument.writeTo(
            new OutputStreamWriter(new FileOutputStream(file.toFile())));
        System.out.println("Processing "+file+" DONE");
    }


}

【讨论】:

  • 后处理可能是可能的,但它必须适合我们每晚生成 Javadoc 的 Jenkins 构建环境。我希望有人能告诉我这可以通过 doclet 或类似的东西来实现。
猜你喜欢
  • 1970-01-01
  • 2015-09-17
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 2017-01-12
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多