资源束问题

2008年11月5日,Yoshito Umaoka向ICU团队发送了以下信息:

ICU资源包问题

大家好,

我试图修复一些资源包问题,并做了一些

调查。现在我认为ICU有几个设计问题

资源包实现。我没有任何具体的计划

进行修复/更改。现在我只想谈谈一些话题

并与您核实是否存在任何误解/历史背景

等等。

1.顶层表格与嵌套表格

语言环境包是基于语言环境继承模型组织的。任何

与键关联的资源被折叠到区域设置继承中

等级制度。表是与

钥匙。要访问资源项,应该使用ures_getByKey

定位容器资源。然而,ures_getByKey的行为

与顶级表和嵌套表不同。对于顶级

表,则涉及区域设置继承-即,如果与资源关联

由于表中缺少给定的键,实现尝试

在父语言环境包中查找资源。另一方面,如果

针对嵌套表和关联的资源调用ures_getByKey

缺少给定密钥时,错误代码U_missing_RESOURCE_error为

返回。根据区域设置访问嵌套表中的资源

继承,必须使用ures_getByKeyWithFallback,但这不是

公共API。

例如-

----------------------

英语{

month1{“一月”}

道琼斯指数{

第1天{“星期日”}

}

}

----------------------

en_US(_US){

道琼斯指数{

第2天{“星期一”}

}

}

----------------------

UResourceBundle*bundle=ures_open(MYBUNDLE,“en_US”,&status);

bundle=ures_getByKey(捆绑包,“month1”,捆绑包,&status);

此代码将返回类型为URES_STRING的UResourceBundle,其中

“January”(从捆绑包“en”中选择)。

UResourceBundle*bundle=ures_open(MYBUNDLE,“en_US”,&status);

bundle=ures_getByKey(bundle、“dow”、bundle和&status);

bundle=ures_getByKey(捆绑包,“day1”,捆绑包,&status);

然而,上述代码将返回U_MISSING_RESOURCE_ERROR,因为“dow”是

在“en_US”中可用,但不包含“day1”。

我认为返回的UResourceBundle之间没有区别

公共API(isTopLevel)中的ures_open(顶层)与ures_getByKey(嵌套)

尽管)

行为的差异不是一个好主意。

史蒂文告诉我,顶级捆绑包之间有明显的区别

以及最初开发ICU资源束时的嵌套束。而且

他指出,一些重症监护室的消费者不希望任何地区出现倒退

嵌套资源访问。然而,我们同意

嵌套项应为“带回退”。从设计角度来看,

当bundle为

打开-即ures_open(带回退)与ures_openDirect(带NO)

回退)。

2.资源迭代

如上所述,顶级项查找是通过回退完成的。

但是,迭代API-ures_getNextResource没有。来自资源

捆绑消费者的观点,回退应该是透明的。

上面的示例中,“month1”位于bundle“en”中。你可以获得资源

由ures_getByKey使用“month1”,即使您实际上打开了捆绑包

“en_US”。但是,使用ures_getNextResource,您无法到达那里。如果

顶层项目没有后退,这是有道理的,但它做到了。

3.分级密钥访问

在我们的语言环境包中,我们以分层的方式组织内容。

所有与日历相关的项目都位于“日历”下。我认为ICU消费者

也希望以这种方式组织自己的资源。使用公共API,

您必须在层次节点中导航才能到达实际的

资源对象,也就是说,必须首先调用uresopen,然后调用

ures_getByKey以向下移动到所需的叶。我认为我们应该有一个API

在一个调用中定位叶对象,而不是重复的ures_getByKey。

例如,通过指定层次键-

“calendar/gregorian/dayNames/format/simplied”,它应该到达

而不是调用ures_getByKey 5次。

4.别名解析问题(仅限ICU4J?)

我刚才提到了这个潜在的问题。假设我们有地区

捆在下面-

----------------------

根{

日历{

佛教徒{

AmPm标记{“AM”,“PM”}

dayNames:alias{“/LOCALE/calendar/gregorian/dayNames”}

}

}

}

----------------------

xx个{

日历{

格雷戈里亚人{

dayNames{“SUN”,“MON”,”TUE“,”WED“,”THU“,”FRI“,”SAT“}

}

佛教徒{

AmPm标记{“AA”,“PP”}

}

}

----------------------

xx年xx月xx日{

日历{

格雷戈里亚人{

dayNames{“NUS”,“NOM”,“EUT”,“DEW”,“UHT”,“IRF”,“TAS”}

}

}

----------------------

要获取日历/佛教/日期名称,有两种方法。

a) 使用分层键获取WithFallback

ICUResourceBundle b=ICUResurceBundle.getBundleInstance(MYLOCALEBUNDLE,

新的ULocale(“xx_YY”));

b=b.getWithFallback(“日历/佛教/日名称”);

b) 通过导航资源层次结构获取WithFallback

ICUResourceBundle b=ICUResurceBundle.getBundleInstance(MYLOCALEBUNDLE,

新ULocale(“xx_YY”);

b=b.getWithFallback(“日历”);

b=b.getWithFallback(“佛教”);

b=b.getWithFallback(“dayNames”);

主要区别是-如果你向下一步浏览层次结构

dayNames:alias(root)中的一个(case b),“/LOCALE”被解释为LOCALE

名称“xx”,而它被解析为“xx_YY”,具有单一层次结构

键“calendar/fhimer/dayNames”(案例a)。这是因为原始

打开捆绑包时,打开捆绑包的上下文不包含

一个。

5.ICU4J UResourceBundle扩展java.util。资源束??

com.ibm.icu.util文件。UResourceBundle扩展了java.util。资源束。那里

这两个类之间的一个主要设计差异是Java

ResourceBundle不支持层次键。另一方面,

UResourceBundle支持密钥层次结构。Java ResourceBundle中的“get”

只需返回一个资源对象,而UResourceBundle中的“get”返回一个

UResourceBundle。

根据约定,Java ResourceBundle的子类必须实现Object

handleGetObject(字符串键)。资源包查找框架是其中的一部分

Java ResourceBundle的。因此JavaResourceBundle的子类应该是

只是一个资源容器(支持字符串键映射)。

ICU4J UResourceBundle自带包查找逻辑

独立于Java ResourceBundle。通过Java的设计

ResourceBundle,通过遵循“父”链进行资源查找。

但是,对于当前的UResourceBundle实现,“父”总是

指向其父包的顶级表。如果.res文件具有平面

结构(所有键都属于顶级表),它应该

等效于Java ResourceBundle。然而,根据

UResourceBundle,它通常有嵌套表。所以基本收缩

Java ResourceBundle中的UResourceBundle中断。

在我看来,将UResourceBundle作为

java.util的子类。由于上述原因,ResourceBundle。

UResourceBundle复制并扩展了资源查找框架

由Java ResourceBundle定义。甚至有人想通过使用.res

java.util(实用程序)。ResourceBundle(例如,Java6允许您实现

使用ResourceBundle拥有资源格式。控件),它工作不太正常

除非.res文件只包含顶级密钥。

6.ICU4J UResourceBundle(更具体地说,ICUResourceBundle)

性能问题

实际上,这就是我开始研究ICU资源包的原因

架构紧密。我认为有几个实施问题。

-创建bundle对象时,.res的内容会复制到byte[]中。

-当请求资源时,会创建一个新的Java对象。

-键由字节[]中的ASCII字节表示,资源查找为

通过b-tree搜索完成。(紧凑,但比HashMap慢得多)

另一方面,java.util。ListResourceBundle执行以下操作-

-键/值的Java对象是在加载bundle类时创建的。

-当请求资源时,返回Java对象的引用。

-密钥被截留,HashMap用于资源查找。

在ICU语言环境包中,有许多字符串资源。当字符串

资源被请求时,它最终会复制字节以创建字符串

对象。在ICU 4.0之前,UResourceBundle每

相同资源的时间。我们在每个

在4.0中使用HashMap的层次级别,因此它不再创建新的Java

如果类型是不可变的,则为。然而,通过这种实现,

UResourceBundle对象本身被缓存,包括各种字段

而不是实际的资源数据。有些字段实际上取决于

加载上下文-因此,实现有意排除

使用非默认选项加载或缓存的ICUResourcebundle实例

由于实现错误而导致一些问题。此外,甚至

所有资源都加载到缓存中,字节[]保留整个

.res内容从未发布。所以从技术上讲,该实现保持了

Java中的重复数据(一个在包缓存中,另一个在字节[]中)

堆。

为了减少内存占用,我考虑了内存的使用

像ICU4C一样映射文件。但是,.res的内容作为

通过Java类加载器的资源流-也就是说,它不是常规文件。

因此,我们不能使用java.nio。MappedByteBuffer。

我认为正确的做法是(如果我们继续使用.res)-

-.res的内容应该转换为Java对象(例如,文本

data->String,int vector->int[]…)创建捆绑包时。也,

同时创建/实例化键的字符串对象。

-释放保存原始.res数据的字节[]。(理想情况下,我们想阅读

.res作为流并将内容转换为本机Java对象

结构。但是.res格式不是为了

支持流解析,它需要将整个内容加载到

字节[]一次…)

-UResourceBundle/ICUResourceBundle的工作方式应该类似于

导航上面加载的Java资源对象。

-HashMap是在表资源中请求资源时创建的。

通过上述实现,它必须在加载时支付一次成本

时间。然而,它应该与Java标准没有太大区别

资源类型(加载ResourceBundle类也会创建Java对象

从字节码。)

7.ICU4J中可变资源类型的资源访问器方法

UResourceBundle(资源包)

UResourceBundle有一些返回可变Java类型的方法-

public ByteBuffer getBinary()

公共字节[]getBinary(字节[]ba)

public int[]getIntVector()

public String[]getStringArray()

从技术上讲,资源是只读的,不应修改。所以,

使用这些方法签名,实现必须创建一个新数组

每一次。即使我们想缓存资源对象,也应该克隆它

当通过这些方法访问资源时。这实际上是一个

Java语言的问题(没有常量数组类型)。我认为我们应该添加

只读数组包装器类型(第一个为只读ByteBuffer)

并使用这些类型。

除此之外,我认为“publicbyte[]getBinary(byte[]ba)”不会

说得通。即使您提供了fillin arg-byte[]ba,如果

字节大小与给定的字节[]不匹配。我想它是被移植的

从C开始,表示输入/输出参数的长度。

8.ICU4J UResourceBundle中的API错误

这实际上是我的错。就在拉姆离开项目之前,他

正在将一些内部API(ICUResourceBundle)移动到公共

(UResourceBundle)。他忘了分配一些API状态注释,我

添加它们时没有理解整个图片。现在我想我应该

没有公开以下API(我的意思是-public API,签名

实际上是“受保护的”)。这些API将HashMap作为参数

避免由无效ALIAS引起的循环引用,这实际上是我们的

内部实施。

受保护的UResourceBundle handleGet(String aKey,

HashMap表,

请求UResourceBundle)

受保护的UResourceBundle handleGet(int索引,

HashMap表,

请求UResourceBundle)