我希望构建由属性文件中的键/值对的一部分组成的ResourceBundles,从而与将这些部分或部分存储在单个文件中相比,节省了相当数量的文件。这些部分由以"#“开头的标题行标记,并以空行分隔。下面代码之后的示例属性文件包含两个部分:
我试图通过在ClassLoader (.)中传递一个只读取所需部分的自定义ClassLoader来实现这一点。方法。CustomClassLoader可以很好地减少键定义,但是ResourceBundle仍然包含属性文件的所有键。
import java.io.*;
import java.util.*;
public class ResourceReader {
public ResourceReader() {
Locale locale= Locale.getDefault();
ResourceBundle i18n= ResourceBundle.getBundle("ComponentBundle", locale,
new CustomClassLoader("#FileChooser"));
Enumeration<String> enu= i18n.getKeys();
System.out.println("Keys of ResourceBundle");
printEnumeration(enu);
}
public static void main(String args[]) {
new ResourceReader();
}
public void printEnumeration(Enumeration<String> enu) {
int i= 1;
while (enu.hasMoreElements()) {
System.out.println(i+".: "+enu.nextElement());
i++;
}
}
//////////////////////////////////////////////////////////////////////////////
public class CustomClassLoader extends ClassLoader {
String section;
public CustomClassLoader(String section) {
this.section= section;
}
@Override
public Class findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassFromFile(name);
//System.out.writeBytes(b); // OK.
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassFromFile(String fileName) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
fileName.replace('.', File.separatorChar) + ".properties");
byte[] buffer;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
int nextValue = 0;
try {
while ( (nextValue = inputStream.read()) != -1 ) {
byteStream.write(nextValue);
}
} catch (IOException e) {
e.printStackTrace();
}
buffer = extractSection(byteStream.toString(), section);
return buffer;
}
private byte[] extractSection(String stream, String caption) {
final String LINE_SEP= System.getProperty("line.separator", "\n");
String[] lines= stream.split(LINE_SEP);
// Detect first and last line (exclusive) of section.
int iEnd= 0, iStart= -1;
for (int i=0; i<lines.length; i++) {
lines[i]= lines[i].trim();
if (iStart==-1) {
if (!lines[i].equals(caption)) continue;
iStart= i+1;
i++;
}
else if (lines[i].isEmpty()) {
iEnd= i;
break;
}
}
if (iEnd==0) iEnd= lines.length+1;
StringBuilder sb= new StringBuilder();
for (int i=iStart; i<iEnd; i++)
sb.append(lines[i]+LINE_SEP);
return sb.toString().getBytes();
}
}
}
///////////////////////////////////////////////////////////////////////////////* // File ComponentBundle.properties
#FileChooser
acceptAllFileFilterText= All files (*.*)
cancelButtonText= Cancel
cancelButtonToolTipText= Cancel
#OptionPane
Cancel= Cancel
Input= Input
Message= Message
No= No
// End of ComponentBundle.properties*/
发布于 2020-12-01 13:16:03
您不会以这种方式获得受限的资源包,因为您已经重写了findClass,可以将属性文件的字节作为一个类返回。要了解正在发生的事情,请添加以下代码:
public URL getResource(String name)
{
var url = super.getResource(name);
System.out.println("getResource "+name+" -> "+url);
return url;
}
public Class findClass(String name) throws ClassNotFoundException {
System.out.println("findClass "+name);
...然后,您可以看到ResourceBundle的内部工作方式,它查找所有键,因为它正在加载文件url的内容--您的代码是而不是使用的:
findClass ComponentBundle
getResource ComponentBundle.properties -> file:/C:/some/path/to/ComponentBundle.properties
findClass ComponentBundle_en
getResource ComponentBundle_en.properties -> null
findClass ComponentBundle_en_GB
getResource ComponentBundle_en_GB.properties -> null如果覆盖getResource(String name)并使生成的文件适合键的子集,并将URL传回子集文件,那么就有可能让您的绑定器工作。
当您只为所有应用程序定义一个资源包文件,或者为每个子组件定义一个资源包文件时,这似乎需要做很多工作。
发布于 2020-12-02 23:46:34
如果一个人绝对想使用ResourceBundle的一个子集,这可能是一种方法--尽管不是很优雅,因为完整的包需要被读取和过滤。
int componentFlag= ...;
final int FILE_CHOOSER= 1, OPTION_PANE= 2;
ResourceBundle i18n= ResourceBundle.getBundle("ComponentBundle", locale);
String prefix;
Set<String> set= i18n.keySet();
if ((componentFlag&FILE_CHOOSER)>0)
prefix= "FileChooser.";
else if ((componentFlag&OPTION_PANE)>0)
prefix= "OptionPane.";
set.stream().filter(s -> s.startsWith(prefix))
.forEach(s -> UIManager.put(s, i18n.getString(s)));https://stackoverflow.com/questions/65075437
复制相似问题