CLOVER🍀

That was when it all began.

java.util.Propertiesについて

ちょっと、こちらの資料を見て

from old Java to modern Java
http://www.slideshare.net/shintanimoto/from-old-java-to-modern-java

ひとつ衝撃を受けた点が。

APIの変更で、native2asciiを使わなくて済むようになった

えぇぇ、マジですか?知らんかった…。

まあ、これも何かの機会です。どこかに作成したプロパティファイルから、java.util.Propertiesにその定義内容をロードするサンプルを。

The native2ascii

基本形。たとえばこんなプロパティファイルを用意して
*無意味にキーに日本語入れました…まあ、オススメしませんね…
config-multibyte.properties

# コメント
language1=英語
language2=日本語
language3=中国語
言語4=アラビア語

ファイルの内容に、意味はまったくありません。

で、これをnative2asciiでUnicodeエスケープします。

$ native2ascii -encoding UTF-8 config-multibyte.properties config-simple.properties 

config-simple.properties

# \u30b3\u30e1\u30f3\u30c8
language1=\u82f1\u8a9e
language2=\u65e5\u672c\u8a9e
language3=\u4e2d\u56fd\u8a9e
\u8a00\u8a9e4=\u30a2\u30e9\u30d3\u30a2\u8a9e

あとは、これをJavaで読み込みます。

public static void loadBasicProperties() throws IOException {
    Properties properties = new Properties();

    try (InputStream is = findStream("config-simple.properties")) {
        properties.load(is);
    }

    System.out.println("Properties Loaded, Simple Properties File");
    System.out.printf("%s = %s%n", "language1", properties.getProperty("language1"));
    System.out.printf("%s = %s%n", "language2", properties.getProperty("language2"));
    System.out.printf("%s = %s%n", "language3", properties.getProperty("language3"));
    System.out.printf("%s = %s%n", "言語4", properties.getProperty("言語4"));
}

findStreamは指定のパスから、InputStreamを構築して返却するメソッドだと思っててください。

結果。

Properties Loaded, Simple Properties File
language1 = 英語
language2 = 日本語
language3 = 中国語
言語4 = アラビア語

まあ、なんのことはありません。

Properties#loadに、Readerが引数に取れますよ

これを知りませんでした…。Java SE 6から、そんなの追加されてたんですねぇ…。

モダンなJavaを知らず、申し訳ございません。

プロパティファイルは、先ほどのファイル。
config-multibyte.properties

# コメント
language1=英語
language2=日本語
language3=中国語
言語4=アラビア語

つまり、native2asciiをかける前のファイルです。

    public static void loadMultiByteProperties() throws IOException {
    Properties properties = new Properties();

    try (InputStream is = findStream("config-multibyte.properties");
         InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) {
        properties.load(isr);
    }

    System.out.println("Properties Loaded, Multi Byte Properties File");
    System.out.printf("%s = %s%n", "language1", properties.getProperty("language1"));
    System.out.printf("%s = %s%n", "language2", properties.getProperty("language2"));
    System.out.printf("%s = %s%n", "language3", properties.getProperty("language3"));
    System.out.printf("%s = %s%n", "言語4", properties.getProperty("言語4"));
}

実行。

Properties Loaded, Multi Byte Properties File
language1 = 英語
language2 = 日本語
language3 = 中国語
言語4 = アラビア語

java.io.Readerを渡せるので、マルチバイトを含めたプロパティファイルが読めるというわけですね。

XML

プロパティファイルは、XML形式で定義することもできます。こちらはJ2SE 5での追加で、一応使ったことがあります。

プロパティファイルの形式は、こんな感じです。定義の意味自体は、先に出したプロパティファイルとまったく同じです。
config-xml.properties

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>
  <comment>コメント</comment>
  <entry key="language1">英語</entry>
  <entry key="language2">日本語</entry>
  <entry key="language3">中国語</entry>
  <entry key="言語4">アラビア語</entry>
</properties>

XMLの場合は、コメントはcommentタグで記述します。マルチバイトはもちろんOKですが、XML宣言のエンコーディングと合わせましょう。普通UTF-8でしょうね。なお、DOCTYPE宣言は必須です。

読み込む時は、Properties#loadFromXMLを使います。

public static void loadXmlProperties() throws IOException {
    Properties properties = new Properties();

    try (InputStream is = findStream("config-xml.properties")) {
        properties.loadFromXML(is);
    }

    System.out.println("Properties Loaded, XML Properties File");
    System.out.printf("%s = %s%n", "language1", properties.getProperty("language1"));
    System.out.printf("%s = %s%n", "language2", properties.getProperty("language2"));
    System.out.printf("%s = %s%n", "language3", properties.getProperty("language3"));
    System.out.printf("%s = %s%n", "言語4", properties.getProperty("言語4"));
}

あんまり意味ありませんが、実行結果。

Properties Loaded, XML Properties File
language1 = 英語
language2 = 日本語
language3 = 中国語
言語4 = アラビア語

ところでですね

これらのメソッドに指定されたリソースをどうするかですが、

指定されたストリームは、このメソッドが復帰したあとも開いたままです。

load InputStream

指定されたストリームは、このメソッドが復帰したあとも開いたままです。

load Reader

このメソッドが戻ると、指定されたストリームが閉じられます。

loadFromXML InputStream

ねえねえ、どうしてキミたちは、足並みが揃ってないの?

まあ、外で閉じるコードを書いていることがほとんだと思いますけどね。

あと、マルチバイトで書いたプロパティファイルが利用できるということですが、引用元のスライドにもありますように開発の現場で最も使われていると思われるEclipse(特にPleiades All in One)にはプロパティエディタが搭載されていますし、実際には裏でnative2asciiをかけたまま使うんでしょうね。

このAPIを使うがために、わざわざ設定をいじくるなんて、ちょっと考えにくいです(笑)。

とりあえず、知らなかったことを知る、よい機会になりました。
PropertiesReader.java

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;

public class PropertiesReader {
    public static void main(String[] args) {
        try {
            loadBasicProperties();
            loadMultiByteProperties();
            loadXmlProperties();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void loadBasicProperties() throws IOException {
        Properties properties = new Properties();

        try (InputStream is = findStream("config-simple.properties")) {
            properties.load(is);
        }

        System.out.println("Properties Loaded, Simple Properties File");
        System.out.printf("%s = %s%n", "language1", properties.getProperty("language1"));
        System.out.printf("%s = %s%n", "language2", properties.getProperty("language2"));
        System.out.printf("%s = %s%n", "language3", properties.getProperty("language3"));
        System.out.printf("%s = %s%n", "言語4", properties.getProperty("言語4"));
    }

    public static void loadMultiByteProperties() throws IOException {
        Properties properties = new Properties();

        try (InputStream is = findStream("config-multibyte.properties");
             InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) {
            properties.load(isr);
        }

        System.out.println("Properties Loaded, Multi Byte Properties File");
        System.out.printf("%s = %s%n", "language1", properties.getProperty("language1"));
        System.out.printf("%s = %s%n", "language2", properties.getProperty("language2"));
        System.out.printf("%s = %s%n", "language3", properties.getProperty("language3"));
        System.out.printf("%s = %s%n", "言語4", properties.getProperty("言語4"));
    }

    public static void loadXmlProperties() throws IOException {
        Properties properties = new Properties();

        try (InputStream is = findStream("config-xml.properties")) {
            properties.loadFromXML(is);
        }

        System.out.println("Properties Loaded, XML Properties File");
        System.out.printf("%s = %s%n", "language1", properties.getProperty("language1"));
        System.out.printf("%s = %s%n", "language2", properties.getProperty("language2"));
        System.out.printf("%s = %s%n", "language3", properties.getProperty("language3"));
        System.out.printf("%s = %s%n", "言語4", properties.getProperty("言語4"));
    }

    private static InputStream findStream(String path) throws FileNotFoundException {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        InputStream is = cl.getResourceAsStream(path);
        if (is == null) {
            throw new FileNotFoundException("No Such File: " + path);
        }

        return is;
    }
}