0

我维护一个 Java 程序来帮助 Mozilla 本地化。除其他格式外,Mozilla 本地化使用 DTD 文件(在运行时,所选语言环境的 DTD 被注入到定义 UI 的 XML 文件中,从而产生本地化界面)。

DTD 有时会以 PE 引用的形式包含其他文件,如下所示:

<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;

但是,出于我的程序目的,当我使用这样的 PE 引用解析 DTD 文件的 en-US 版本时,我只需要将这些行复制到目标本地化文件中。

我继承了这个程序,它使用 SAX(JDK 中捆绑的 Xerces)做了一些聪明的(对我来说)解析 DTD 的技巧:

public class DTDReadHelper extends DefaultHandler2 {
    private static final String dummyXml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<!DOCTYPE dialog SYSTEM \"MozillaTranslator\">" +
            "<dialog></dialog>";
(...)
    @Override
    public InputSource resolveEntity(java.lang.String name, java.lang.String publicId,
            java.lang.String baseURI, java.lang.String systemId) {

        if ((name != null) && (name.startsWith("%"))) {
           return new InputSource(new StringReader(""));
        } else {
            return new InputSource(is);
        }
    }

问题是在典型的解析过程中会两次到达 else 分支,因此如果文件小于 8192 字节,则会导致来自 Xerces 的 IOException,否则会导致 SAXParseException。

我可以处理 IOException,因为当它发生时整个文件已经被解析,但我无法使用 SAXParseException 来处理它,因为它发生在解析的最开始(lineNumber 1,columnNumber 2)。

“有问题的”DTD 文件可以在这里找到:

http://hg.mozilla.org/mozilla-central/file/13fe5ad0364d/browser/locales/en-US/chrome/overrides/netError.dtd

并且程序的整个源代码都在这里:

https://kenai.com/projects/moztrans/sources

我的“SCCE”包含四个文件(上述存储库中的简化版本),如果需要,我可以将它们上传到某个地方。

在所有这些细节之后,问题是:有什么方法可以让 SAX 避免尝试解析 PE 引用?

TIA

4

1 回答 1

0

我终于找到了解决方案(如果您查看存储库,您会注意到我忘记更新这个问题,因为我几个月前提交了修复程序)。:-) 解决方案在于以下代码:

// private static final String brandDummyDtd = "<!ENTITY brandDTD \"\">";
private static final String brandDummyDtd = "";
(...)
@Override
public InputSource resolveEntity(java.lang.String name, java.lang.String publicId,
        java.lang.String baseURI, java.lang.String systemId) {

    if (name == null) {
        switch (systemId) {
            // Trick to get a SAX XML Parser to parse a DTD
            case "MozillaTranslator":
                return new InputSource(is);

            // Trick to resolve references to brand.dtd without
            // actually having to resolve, load and parse
            // the chrome: URI
            case "chrome://branding/locale/brand.dtd":
            default:
                return new InputSource(new ByteArrayInputStream(brandDummyDtd.getBytes()));
        }
    } else {
        return new InputSource(new StringReader(""));
    }
}

解决方案是返回一个空的 InputSource(brandDummyDtd 属性,在顶部定义为 private static final)。我已将注释版本包含在触发 resolveEntity 方法的实体中,以解释使用此类解决方案不起作用。如果我没记错的话,如果解析的 DTD 包含实际的 brandDTD 实体,这样做会失败。

于 2015-03-04T21:45:26.313 回答