MSLocale.java

package com.github.mygreen.cellformatter.lang;

import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Microsoftで使用しているロケールを表現するクラス。
 * <ul>
 *  <li><a href="https://msdn.microsoft.com/ja-jp/library/cc392381.aspx" target="_blank">ロケール ID (LCID) の一覧</a></li>
 *  <li><a href="https://msdn.microsoft.com/ja-jp/goglobal/bb964664.aspx" target="_blank">Locale IDs Assigned by Microsoft</a></li>
 * </ul>
 * 
 * @version 0.5
 * @author T.TSUCHIE
 *
 */
public class MSLocale {
    
    private static final MessageResolver messageResolver = new MessageResolver("com.github.mygreen.cellformatter.locale");
    
    /**
     * 既知(プロパティファイル)に定義したのロケールはキャッシュする。
     * ・キー:id(10進数)
     * ・値:既知のロケール。
     */
    private static Map<Integer, MSLocale> KNOWN_LOCALES = new ConcurrentHashMap<>();
    
    /** 日本語 */
    public static final MSLocale JAPANESE = MSLocale.createKnownLocale(0x0411);
    
    /** 英語 (米国) */
    public static final MSLocale US = MSLocale.createKnownLocale(0x0409);
    
    /** 英語 (英国) */
    public static final MSLocale UK = MSLocale.createKnownLocale(0x0809);
    
    /** 英語 (カナダ) */
    public static final MSLocale CANADA = MSLocale.createKnownLocale(0x2809);
    
    /** ドイツ語 */
    public static final MSLocale GERMAN = MSLocale.createKnownLocale(0x0407);
    
    /** フランス語 */
    public static final MSLocale FRENCE = MSLocale.createKnownLocale(0x040C);
    
    /** フランス語(カナダ) */
    public static final MSLocale CANADA_FRENCH = MSLocale.createKnownLocale(0x0C0C);
    
    /** イタリア語 */
    public static final MSLocale ITALY = MSLocale.createKnownLocale(0x0410);
    
    /** 韓国語 */
    public static final MSLocale KOREA = MSLocale.createKnownLocale(0x0412);
    
    /** 中国語(中華人民共和国) */
    public static final MSLocale PRC = MSLocale.createKnownLocale(0x0804);
    
    /** 中国語(台湾) */
    public static final MSLocale TAIWAN = MSLocale.createKnownLocale(0x0404);
    
    /** ID - 10進値 */
    private final int id;
    
    /** ID - 16進値(10進数を元に設定する) */
    private final String hexId;
    
    /** 言語コード */
    private String code;
    
    /** 言語名 */
    private String language;
    
    /** 国 / 地域 */
    private String country;
    
    /** 名称 */
    private String name;
    
    /** Javaのロケール */
    private Locale locale;
    
    /**
     * 既知のIDかどうか。
     * <p>プロパティファイルに定義されているかで確認する。
     * @since 0.5
     * @param id ロケールID(10進数)。
     */
    public static boolean isKnownById(int id) {
        final String hexId = Utils.supplyZero(Integer.toHexString(id).toUpperCase(), 4);
        
        String code = messageResolver.getMessage(String.format("locale.%s.code", hexId));
        return Utils.isNotEmpty(code);
    }
    
    /**
     * IDを指定してインスタンスを作成する。
     * @param id ロケールID(10進数)
     */
    public MSLocale(final int id) {
        this.id = id;
        
        // IDを16進数に変換する
        final String hexId = Utils.supplyZero(Integer.toHexString(id).toUpperCase(), 4);
        this.hexId = hexId;
        
    }
    
    /**
     * 既知の言語を組み立てる。
     * <p>プロパティファイルに定義されている情報を元に作成する。
     * @since 0.5
     * @param id 10進数の言語ID
     * @return 不明なIDの場合は、nullを返す。
     */
    public static MSLocale createKnownLocale(final int id) {
        
        if(KNOWN_LOCALES.containsKey(id)) {
            return KNOWN_LOCALES.get(id);
        }
        
        final MSLocale locale = new MSLocale(id);
        
        // IDを16進数に変換する
        final String hexId = Utils.supplyZero(Integer.toHexString(id).toUpperCase(), 4);
        
        String code = messageResolver.getMessage(String.format("locale.%s.code", hexId));
        if(Utils.isEmpty(code)) {
            return null;
        }
        
        locale.code = code;
        locale.language = messageResolver.getMessage(String.format("locale.%s.language", hexId));
        locale.country = messageResolver.getMessage(String.format("locale.%s.country", hexId));
        locale.name = messageResolver.getMessage(String.format("locale.%s.name", hexId));
        
        // Javaのロケールの設定
        String jid = messageResolver.getMessage(String.format("locale.%s.jid", hexId));
        if(Utils.isNotEmpty(jid)) {
            locale.locale = parseLocale(jid);
        }
        
        // キャッシュに登録する。
        KNOWN_LOCALES.put(id, locale);
        
        return locale;
        
    }
    
    private static Locale parseLocale(final String jid) {
        
        String[] split = jid.split("_");
        if(split.length == 1) {
            return new Locale(split[0]);
            
        } if(split.length == 2) {
            return new Locale(split[0], split[1]);
            
        } else if(split.length == 3) {
            return new Locale(split[0], split[2]);
            
        }
        
        return null;
        
    }
    
    /**
     * システムの日付用のロケールの場合。
     * <p>16進数の値が'F800'のとき。
     * @return
     */
    public boolean isSystemDate() {
        return getHexId().equalsIgnoreCase("F800");
    }
    
    /**
     * システムの時刻用のロケールの場合。
     * <p>16進数の値が'F400'のとき。
     * @return
     */
    public boolean isSystemTime() {
        return getHexId().equalsIgnoreCase("F400");
    }
    
    /**
     * IDを取得する。10進数の数値
     * @return
     */
    public int getId() {
        return id;
    }
    
    /**
     * 16進数のIDを取得する。
     * ・大文字、4桁に整形されている。
     * @return
     */
    public String getHexId() {
        return hexId;
    }
    
    /**
     * コードの取得
     * @return
     */
    public String getCode() {
        return code;
    }
    
    /**
     * 言語コードを取得する
     * @return
     */
    public String getLanguage() {
        return language;
    }
    
    /**
     * 国コードを取得する
     * @return
     */
    public String getCountry() {
        return country;
    }
    
    /**
     * 名称を取得する。
     * @return
     */
    public String getName() {
        return name;
    }
    
    /**
     * 指定したロケールの名称を取得する。
     * @param locale
     * @return
     */
    public String getName(final Locale locale) {
        return messageResolver.getMessage(locale, String.format("locale.%s.name", hexId));
    }
    
    /**
     * 対応するJavaのロケールを取得する。
     * <p>存在しない場合は、nullを返す。
     * @return
     */
    public Locale getLocale() {
        return locale;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }
    
    @Override
    public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        MSLocale other = (MSLocale) obj;
        if(id != other.id)
            return false;
        return true;
    }
    
}