代码之家  ›  专栏  ›  技术社区  ›  Jörg

带类加载器的资源包

  •  0
  • Jörg  · 技术社区  · 4 年前

    我想构建仅由键/值的一部分组成的ResourceBundles 属性文件中的配对,从而将大量文件保存为 与将这些部分或节中的每一个存储在单独的文件中相比。 这些部分由以“#”开头的标题行标记,并与 彼此用一条空行隔开。下面代码后面的示例属性文件包含两部分:

    • #文件选择器
    • #选项窗格

    我试图通过传递一个定制的ClassLoader来实现这一点,它只读取 在getBundle(…)方法中找到所需的部分。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();
        }
      }
    
    }
    //////////////////////////////////////////////////////////////////////////////
    

    /* //文件组件Bundle.properties

    #FileChooser
    acceptAllFileFilterText= All files (*.*)
    cancelButtonText= Cancel
    cancelButtonToolTipText= Cancel
    
    #OptionPane
    Cancel= Cancel
    Input= Input
    Message= Message
    No= No
    // End of ComponentBundle.properties
    

    */

    0 回复  |  直到 4 年前
        1
  •  1
  •   DuncG    4 年前

    您不会以这种方式获得受限资源包,因为您已经骑过了 findClass 将Properties文件的字节作为类返回。要了解发生了什么,请添加以下代码:

    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传递回子集文件。

    当你可以为所有应用程序定义一个资源包文件,或者为每个子组件定义一个资金包文件时,似乎要做很多工作。

        2
  •  1
  •   Jörg    4 年前

    如果一个人绝对想使用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)));