OSDN Git Service

慣例情報更新
authorOlyutorskii <olyutorskii@users.osdn.me>
Wed, 24 Apr 2013 12:13:10 +0000 (21:13 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Wed, 24 Apr 2013 12:13:10 +0000 (21:13 +0900)
13 files changed:
src/main/java/jp/sfjp/mikutoga/typical/AliasMap.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/typical/I18nAlias.java [moved from src/main/java/jp/sourceforge/mikutoga/typical/I18nAlias.java with 63% similarity]
src/main/java/jp/sfjp/mikutoga/typical/TypicalBone.java [moved from src/main/java/jp/sourceforge/mikutoga/typical/TypicalBone.java with 56% similarity]
src/main/java/jp/sfjp/mikutoga/typical/TypicalMorph.java [moved from src/main/java/jp/sourceforge/mikutoga/typical/TypicalMorph.java with 63% similarity]
src/main/java/jp/sfjp/mikutoga/typical/UniqBone.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/typical/package-info.java [moved from src/main/java/jp/sourceforge/mikutoga/typical/package-info.java with 72% similarity]
src/main/resources/jp/sfjp/mikutoga/typical/resources/typicalBone.xml [moved from src/main/resources/jp/sourceforge/mikutoga/typical/resources/typicalBone.xml with 96% similarity]
src/main/resources/jp/sfjp/mikutoga/typical/resources/typicalMorph.xml [moved from src/main/resources/jp/sourceforge/mikutoga/typical/resources/typicalMorph.xml with 100% similarity]
src/test/java/jp/sfjp/mikutoga/typical/AliasMapTest.java [new file with mode: 0644]
src/test/java/jp/sfjp/mikutoga/typical/I18nAliasTest.java [new file with mode: 0644]
src/test/java/jp/sfjp/mikutoga/typical/TypicalBoneTest.java [moved from src/test/java/jp/sourceforge/mikutoga/typical/TypicalBoneTest.java with 74% similarity]
src/test/java/jp/sfjp/mikutoga/typical/TypicalMorphTest.java [moved from src/test/java/jp/sourceforge/mikutoga/typical/TypicalMorphTest.java with 77% similarity]
src/test/java/jp/sfjp/mikutoga/typical/UniqBoneTest.java [new file with mode: 0644]

diff --git a/src/main/java/jp/sfjp/mikutoga/typical/AliasMap.java b/src/main/java/jp/sfjp/mikutoga/typical/AliasMap.java
new file mode 100644 (file)
index 0000000..bd33ba7
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * alias map with primary & global
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.typical;
+
+import java.text.Normalizer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * プライマリ名 - グローバル名間の対応、
+ * およびUnicode正規化によるゆらぎ表記吸収処理を行う。
+ * @param <T> 別名管理クラス
+ * @see <a href="http://ja.wikipedia.org/wiki/Unicode正規化">
+ * Unicode正規化 </a>
+ * @see <a href="http://unicode.org/reports/tr15/">
+ * UNICODE NORMALIZATION FORMS </a>
+ * @see java.text.Normalizer
+ */
+class AliasMap<T extends I18nAlias> {
+
+    private final Map<String, T> primaryAliasMap;
+    private final Map<String, T> globalAliasMap;
+
+    /**
+     * コンストラクタ。
+     */
+    AliasMap(){
+        super();
+
+        this.primaryAliasMap = new HashMap<String, T>();
+        this.globalAliasMap  = new HashMap<String, T>();
+
+        return;
+    }
+
+
+    /**
+     * NFKC正規化されたUnicode文字列を返す。
+     * <p>等価な全半角、濁点、丸付き数字などの表現の正規化を目的とする。
+     * <ul>
+     * <li>「ボーン」は「ボーン」になる
+     * <li>「ホ゛ーン9」は「ボーン9」になる
+     * </ul>
+     * @param name 正規化対象文字列
+     * @return 正規化済み文字列
+     */
+    static String normalize(CharSequence name){
+        String result;
+        result = Normalizer.normalize(name, Normalizer.Form.NFKC);
+        return result;
+    }
+
+
+    /**
+     * 別名管理オブジェクトを登録。
+     * <p>キーとなる名前は、事前にNFKC正規化で
+     * 揺らぎ表記が吸収されたプライマリ名およびグローバル名。
+     * <p>登録キーが衝突した時は後の方が有効となる。
+     * @param alias 別名管理オブジェクト
+     */
+    void addAlias(T alias){
+        addPrimary(alias);
+        addGlobal(alias);
+        return;
+    }
+
+    /**
+     * 別名管理オブジェクトと正規化プライマリ名を対応づける。
+     * <p>事前にNFKC正規化されたプライマリ名が登録キーとなる。
+     * <p>登録キーが衝突した時は後の方が有効となる。
+     * @param alias 別名管理オブジェクト
+     */
+    private void addPrimary(T alias){
+        for(String primaryName : alias.getPrimaryNameList()){
+            String normalized = normalize(primaryName);
+            normalized = normalized.intern();
+            this.primaryAliasMap.put(normalized, alias);
+        }
+        return;
+    }
+
+    /**
+     * 別名管理オブジェクトと正規化グローバル名を対応づける。
+     * <p>事前にNFKC正規化されたグローバル名が登録キーとなる。
+     * <p>登録キーが衝突した時は後の方が有効となる。
+     * @param alias 別名管理オブジェクト
+     */
+    private void addGlobal(T alias){
+        for(String globalName : alias.getGlobalNameList()){
+            String normalized = normalize(globalName);
+            normalized = normalized.intern();
+            this.globalAliasMap.put(normalized, alias);
+        }
+        return;
+    }
+
+    /**
+     * 名前から別名管理オブジェクトを得る。
+     * <p>プライマリ名、グローバル名の順で検索される。
+     * <p>名前は事前にNFKC正規化された後、検索キーとなる。
+     * @param name 名前
+     * @return 別名管理オブジェクト。見つからなければnull
+     */
+    T getAlias(String name){
+        T result;
+        result = getAliasByPrimary(name);
+        if(result == null){
+            result = getAliasByGlobal(name);
+        }
+        return result;
+    }
+
+    /**
+     * プライマリ名から別名管理オブジェクトを得る。
+     * <p>プライマリ名は事前にNFKC正規化された後、検索キーとなる。
+     * @param primaryName プライマリ名
+     * @return 別名管理オブジェクト。見つからなければnull
+     */
+    T getAliasByPrimary(String primaryName){
+        String normalized = normalize(primaryName);
+        T result = this.primaryAliasMap.get(normalized);
+        return result;
+    }
+
+    /**
+     * グローバル名から別名管理オブジェクトを得る。
+     * <p>グローバル名は事前にNFKC正規化された後、検索キーとなる。
+     * @param globalName グローバル名
+     * @return 別名管理オブジェクト。見つからなければnull
+     */
+    T getAliasByGlobal(String globalName){
+        String normalized = normalize(globalName);
+        T result = this.globalAliasMap.get(normalized);
+        return result;
+    }
+
+    /**
+     * プライマリ名から代表グローバル名を得る。
+     * <p>プライマリ名は事前にNFKC正規化された後、検索キーとなる。
+     * @param primaryName プライマリ名
+     * @return 代表グローバル名。見つからなければnull
+     */
+    String primary2global(String primaryName){
+        T alias = getAliasByPrimary(primaryName);
+        if(alias == null) return null;
+        String globalName = alias.getTopGlobalName();
+        return globalName;
+    }
+
+    /**
+     * グローバル名から代表プライマリ名を得る。
+     * <p>グローバル名は事前にNFKC正規化された後、検索キーとなる。
+     * @param globalName グローバル名
+     * @return 代表プライマリ名。見つからなければnull
+     */
+     String global2primary(String globalName){
+        T alias = getAliasByGlobal(globalName);
+        if(alias == null) return null;
+        String primary = alias.getTopPrimaryName();
+        return primary;
+    }
+
+}
@@ -5,11 +5,10 @@
  * Copyright(c) 2011 MikuToga Partners
  */
 
-package jp.sourceforge.mikutoga.typical;
+package jp.sfjp.mikutoga.typical;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.text.Normalizer;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -24,10 +23,15 @@ import org.xml.sax.SAXException;
 
 /**
  * 国際化&別名管理オブジェクトの実装基板。
+ * <p>別名管理オブジェクトは、
+ * 各々のリストの先頭が代表名となる、
+ * プライマリ名の不変リストとグローバル名の不変リストを持つ。
  * <p>国産モデルではプライマリ名に日本語名が収められることが多い。
  * プライマリ名は必ず一つ以上なければならない。
  * <p>国産モデルではグローバル名に英語名が収められることが多いが、
  * プライマリ名と同一の日本語名が収められている場合も多い。
+ * <p>別名管理オブジェクトは、
+ * インスタンス間での順序を定義するためのオーダー番号を持つ。
  */
 class I18nAlias {
 
@@ -38,39 +42,39 @@ class I18nAlias {
 
     private int orderNo;
 
-    private final List<String> primaryList;
-    private final List<String> globalList;
+    private final List<String> primaryNameList;
+    private final List<String> globalNameList;
 
-    private final List<String> umodPrimaryList;
-    private final List<String> umodGlobalList;
+    private final List<String> umodPrimaryNameList;
+    private final List<String> umodGlobalNameList;
 
 
     /**
      * コンストラクタ。
      * <p>各初期数が0以下の場合は、
      * 状況に応じて伸長する連結リストが用意される。
-     * @param primaryNo プライマリ名初期数。
-     * @param globalNo グローバル名初期数。
+     * @param primaryNum プライマリ名初期数。
+     * @param globalNum グローバル名初期数。
      */
-    protected I18nAlias(int primaryNo, int globalNo){
+    protected I18nAlias(int primaryNum, int globalNum){
         super();
 
-        if(primaryNo <= 0){
-            this.primaryList = new LinkedList<String>();
+        if(primaryNum <= 0){
+            this.primaryNameList = new LinkedList<String>();
         }else{
-            this.primaryList = new ArrayList<String>(primaryNo);
+            this.primaryNameList = new ArrayList<String>(primaryNum);
         }
 
-        if(globalNo <= 0){
-            this.globalList  = new LinkedList<String>();
+        if(globalNum <= 0){
+            this.globalNameList  = new LinkedList<String>();
         }else{
-            this.globalList  = new ArrayList<String>(globalNo);
+            this.globalNameList  = new ArrayList<String>(globalNum);
         }
 
-        this.umodPrimaryList =
-                Collections.unmodifiableList(this.primaryList);
-        this.umodGlobalList =
-                Collections.unmodifiableList(this.globalList);
+        this.umodPrimaryNameList =
+                Collections.unmodifiableList(this.primaryNameList);
+        this.umodGlobalNameList =
+                Collections.unmodifiableList(this.globalNameList);
 
         return;
     }
@@ -98,7 +102,6 @@ class I18nAlias {
             throws ParserConfigurationException, SAXException, IOException {
         DocumentBuilderFactory factory;
         factory = DocumentBuilderFactory.newInstance();
-        factory.setNamespaceAware(true);
 
         DocumentBuilder builder = factory.newDocumentBuilder();
         Document doc = builder.parse(is);
@@ -108,21 +111,6 @@ class I18nAlias {
         return top;
     }
 
-    /**
-     * NFKC正規化された文字列を返す。
-     * <ul>
-     * <li>「ボーン」は「ボーン」になる
-     * <li>「ホ゛ーン9」は「ボーン9」になる
-     * </ul>
-     * @param name 正規化対象文字列
-     * @return 正規化済み文字列
-     */
-    protected static String normalize(CharSequence name){
-        String result;
-        result = Normalizer.normalize(name, Normalizer.Form.NFKC);
-        return result;
-    }
-
 
     /**
      * オーダー番号を返す。
@@ -143,10 +131,11 @@ class I18nAlias {
 
     /**
      * プライマリ名の代表をひとつ返す。
+     * <p>必ず存在しなければならない。
      * @return 最初のプライマリ名
      */
     public String getTopPrimaryName(){
-        String result = this.primaryList.get(0);
+        String result = this.primaryNameList.get(0);
         return result;
     }
 
@@ -155,43 +144,44 @@ class I18nAlias {
      * @return 最初のグローバル名。ひとつもなければnull
      */
     public String getTopGlobalName(){
-        String result;
-        if(this.globalList.isEmpty()) result = null;
-        else                          result = this.globalList.get(0);
+        if(this.globalNameList.isEmpty()) return null;
+
+        String result = this.globalNameList.get(0);
+
         return result;
     }
 
     /**
-     * プライマリ名の全エイリアス文字列リストを返す。
-     * @return 全プライマリ名リスト。(不可変)
+     * プライマリ名の全別名リストを返す。
+     * @return 全名リスト。(不可変)
      */
-    public List<String> getPrimaryList(){
-        return this.umodPrimaryList;
+    public List<String> getPrimaryNameList(){
+        return this.umodPrimaryNameList;
     }
 
     /**
      * プライマリ名を追加。
-     * @param name プライマリ名
+     * @param primaryName プライマリ名
      */
-    protected void addPrimaryName(String name){
-        this.primaryList.add(name);
+    protected void addPrimaryName(String primaryName){
+        this.primaryNameList.add(primaryName);
         return;
     }
 
     /**
-     * グローバル名の全エイリアス文字列リストを返す。
-     * @return 全グローバル名リスト。(不可変)
+     * グローバル名の全別名リストを返す。
+     * @return 全名リスト。(不可変)
      */
-    public List<String> getGlobalList(){
-        return this.umodGlobalList;
+    public List<String> getGlobalNameList(){
+        return this.umodGlobalNameList;
     }
 
     /**
      * グローバル名を追加。
-     * @param name グローバル名
+     * @param globalName グローバル名
      */
-    protected void addGlobalName(String name){
-        this.globalList.add(name);
+    protected void addGlobalName(String globalName){
+        this.globalNameList.add(globalName);
         return;
     }
 
@@ -5,15 +5,13 @@
  * Copyright(c) 2011 MikuToga Partners
  */
 
-package jp.sourceforge.mikutoga.typical;
+package jp.sfjp.mikutoga.typical;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import javax.xml.parsers.ParserConfigurationException;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
@@ -31,18 +29,23 @@ public final class TypicalBone extends I18nAlias {
     private static final Class<?> THISCLASS = TypicalBone.class;
     private static final String BONE_XML = "resources/typicalBone.xml";
 
-    private static final List<TypicalBone> TYP_BONE_LIST =
+    private static final String ELEM_BONE    = "bone";
+    private static final String ELEM_ROOT    = "root";
+    private static final String ELEM_PRIMARY = "primary";
+    private static final String ELEM_GLOBAL  = "global";
+    private static final String ATTR_NAME    = "name";
+
+    private static final List<TypicalBone> BONE_LIST =
             new LinkedList<TypicalBone>();
-    private static final Map<String, TypicalBone> PRIMARY_MAP =
-            new HashMap<String, TypicalBone>();
-    private static final Map<String, TypicalBone> GLOBAL_MAP =
-            new HashMap<String, TypicalBone>();
+    private static final AliasMap<TypicalBone> BONE_ALIAS_MAP =
+            new AliasMap<TypicalBone>();
 
-    private static final List<TypicalBone> TYP_BONE_UNMODLIST =
-            Collections.unmodifiableList(TYP_BONE_LIST);
+    private static final List<TypicalBone> BONE_UNMODLIST =
+            Collections.unmodifiableList(BONE_LIST);
 
     static{
         InputStream is = THISCLASS.getResourceAsStream(BONE_XML);
+
         Element top;
         try{
             top = I18nAlias.loadXml(is);
@@ -60,15 +63,19 @@ public final class TypicalBone extends I18nAlias {
     }
 
 
+    private boolean isRoot;
+
+
     /**
      * コンストラクタ。
      * <p>各初期数が0以下の場合は、
      * 状況に応じて伸長する連結リストが用意される。
-     * @param primaryNo プライマリ名初期数。
-     * @param globalNo グローバル名初期数。
+     * @param primaryNum プライマリ名初期数。
+     * @param globalNum グローバル名初期数。
      */
-    private TypicalBone(int primaryNo, int globalNo){
-        super(primaryNo, globalNo);
+    private TypicalBone(int primaryNum, int globalNum){
+        super(primaryNum, globalNum);
+        assert this.getClass() == THISCLASS;
         return;
     }
 
@@ -78,12 +85,13 @@ public final class TypicalBone extends I18nAlias {
      * @param top 最上位要素
      */
     private static void parse(Element top) {
-        NodeList boneList = top.getElementsByTagName("bone");
+        NodeList boneList = top.getElementsByTagName(ELEM_BONE);
         int boneNo = boneList.getLength();
         for(int idx = 0; idx < boneNo; idx++){
-            Element bone = (Element) boneList.item(idx);
-            TypicalBone typBone = parseBone(bone);
-            TYP_BONE_LIST.add(typBone);
+            Element boneElem = (Element) boneList.item(idx);
+            TypicalBone typBone = parseBone(boneElem);
+            BONE_LIST.add(typBone);
+            BONE_ALIAS_MAP.addAlias(typBone);
         }
 
         return;
@@ -91,49 +99,48 @@ public final class TypicalBone extends I18nAlias {
 
     /**
      * bone要素を解読する。
-     * @param bone bone要素
+     * @param boneElem bone要素
      * @return ボーン情報
      */
-    private static TypicalBone parseBone(Element bone){
-        NodeList primaryNodes = bone.getElementsByTagName("primary");
-        NodeList globalNodes  = bone.getElementsByTagName("global");
+    private static TypicalBone parseBone(Element boneElem){
+        NodeList primaryNodes = boneElem.getElementsByTagName(ELEM_PRIMARY);
+        NodeList globalNodes  = boneElem.getElementsByTagName(ELEM_GLOBAL);
         int primaryNo = primaryNodes.getLength();
         int globalNo  = globalNodes.getLength();
 
-        TypicalBone typBone = new TypicalBone(primaryNo, globalNo);
+        assert primaryNo > 0;
+
+        TypicalBone bone = new TypicalBone(primaryNo, globalNo);
 
         for(int idx = 0; idx < primaryNo; idx++){
-            Element primary = (Element) primaryNodes.item(idx);
-            String name = primary.getAttribute("name");
-            typBone.addPrimaryName(name);
+            Element primaryElem = (Element) primaryNodes.item(idx);
+            String name = primaryElem.getAttribute(ATTR_NAME);
+            bone.addPrimaryName(name);
         }
 
         for(int idx = 0; idx < globalNo; idx++){
-            Element global = (Element) globalNodes.item(idx);
-            String name = global.getAttribute("name");
-            typBone.addGlobalName(name);
-        }
-
-        for(String primaryName : typBone.getPrimaryList()){
-            String key = normalize(primaryName).intern();
-            PRIMARY_MAP.put(key, typBone);
+            Element globalElem = (Element) globalNodes.item(idx);
+            String name = globalElem.getAttribute(ATTR_NAME);
+            bone.addGlobalName(name);
         }
 
-        for(String globalName : typBone.getGlobalList()){
-            String key = normalize(globalName).intern();
-            GLOBAL_MAP.put(key, typBone);
+        NodeList rootNodes = boneElem.getElementsByTagName(ELEM_ROOT);
+        if(rootNodes.getLength() > 0){
+            bone.isRoot = true;
+        }else{
+            bone.isRoot = false;
         }
 
-        return typBone;
+        return bone;
     }
 
     /**
-     * å\85¨ã\83\9cã\83¼ã\83³æ\83\85å ±ã\82\92ä¸\80æ\84\8fã\81«é \86åº\8fä»\98ã\81\91ã\82\8b設å®\9aã\82\92è¡\8cã\81\86
+     * å\85¨ã\83\9cã\83¼ã\83³æ\83\85å ±ã\81«é\80\9aã\81\97ç\95ªå\8f·ã\82\92ä»\98ã\81\91ã\82\8b
      * <p>XMLでの定義順が反映される。
      */
     private static void numbering(){
         int order = 0;
-        for(TypicalBone bone : TYP_BONE_LIST){
+        for(TypicalBone bone : BONE_LIST){
             bone.setOrderNo(order++);
         }
 
@@ -144,8 +151,8 @@ public final class TypicalBone extends I18nAlias {
      * 全ボーンの不変リストを返す。
      * @return 全ボーンのリスト
      */
-    public static List<TypicalBone> getBoneList(){
-        return TYP_BONE_UNMODLIST;
+    public static List<TypicalBone> getTypicalBoneList(){
+        return BONE_UNMODLIST;
     }
 
     /**
@@ -155,8 +162,7 @@ public final class TypicalBone extends I18nAlias {
      * @return モーフ情報。見つからなければnull
      */
     public static TypicalBone findWithPrimary(String primaryName){
-        String key = normalize(primaryName);
-        TypicalBone result = PRIMARY_MAP.get(key);
+        TypicalBone result = BONE_ALIAS_MAP.getAliasByPrimary(primaryName);
         return result;
     }
 
@@ -167,8 +173,7 @@ public final class TypicalBone extends I18nAlias {
      * @return モーフ情報。見つからなければnull
      */
     public static TypicalBone findWithGlobal(String globalName){
-        String key = normalize(globalName);
-        TypicalBone result = GLOBAL_MAP.get(key);
+        TypicalBone result = BONE_ALIAS_MAP.getAliasByGlobal(globalName);
         return result;
     }
 
@@ -178,10 +183,8 @@ public final class TypicalBone extends I18nAlias {
      * @return グローバル名。見つからなければnull
      */
     public static String primary2global(String primaryName){
-        TypicalBone bone = findWithPrimary(primaryName);
-        if(bone == null) return null;
-        String global = bone.getTopGlobalName();
-        return global;
+        String globalName = BONE_ALIAS_MAP.primary2global(primaryName);
+        return globalName;
     }
 
     /**
@@ -190,10 +193,19 @@ public final class TypicalBone extends I18nAlias {
      * @return プライマリ名。見つからなければnull
      */
     public static String global2primary(String globalName){
-        TypicalBone bone = findWithGlobal(globalName);
-        if(bone == null) return null;
-        String primary = bone.getTopPrimaryName();
-        return primary;
+        String primaryName = BONE_ALIAS_MAP.global2primary(globalName);
+        return primaryName;
+    }
+
+
+    /**
+     * このボーンが親を持たないルートボーンとして扱われる慣習なのか
+     * 判定する。
+     * <p>※「全親」ボーンに関する慣習は無視される。
+     * @return 親を持たなければtrue
+     */
+    public boolean isRoot(){
+        return this.isRoot;
     }
 
 }
@@ -5,14 +5,13 @@
  * Copyright(c) 2011 MikuToga Partners
  */
 
-package jp.sourceforge.mikutoga.typical;
+package jp.sfjp.mikutoga.typical;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumMap;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import javax.xml.parsers.ParserConfigurationException;
@@ -34,19 +33,25 @@ public final class TypicalMorph extends I18nAlias {
     private static final Class<?> THISCLASS = TypicalMorph.class;
     private static final String MORPH_XML = "resources/typicalMorph.xml";
 
+    private static final String ELEM_MORPHGROUP = "morphGroup";
+    private static final String ELEM_MORPH      = "morph";
+    private static final String ATTR_TYPE       = "type";
+    private static final String ELEM_PRIMARY    = "primary";
+    private static final String ELEM_GLOBAL     = "global";
+    private static final String ATTR_NAME       = "name";
+
     private static final List<TypicalMorph> EMPTY = Collections.emptyList();
 
     private static final Map<MorphType, List<TypicalMorph>> TYPED_MAP =
             new EnumMap<MorphType, List<TypicalMorph>>(MorphType.class);
 
-    private static final Map<String, TypicalMorph> PRIMARY_MAP =
-            new HashMap<String, TypicalMorph>();
-    private static final Map<String, TypicalMorph> GLOBAL_MAP =
-            new HashMap<String, TypicalMorph>();
+    private static final AliasMap<TypicalMorph> MORPH_ALIAS_MAP =
+            new AliasMap<TypicalMorph>();
 
 
     static{
         InputStream is = THISCLASS.getResourceAsStream(MORPH_XML);
+
         Element top;
         try{
             top = I18nAlias.loadXml(is);
@@ -72,12 +77,16 @@ public final class TypicalMorph extends I18nAlias {
      * <p>各初期数が0以下の場合は、
      * 状況に応じて伸長する連結リストが用意される。
      * @param type モーフ種別
-     * @param primaryNo プライマリ名初期数。
-     * @param globalNo グローバル名初期数。
+     * @param primaryNum プライマリ名初期数。
+     * @param globalNum グローバル名初期数。
      */
-    private TypicalMorph(MorphType type, int primaryNo, int globalNo){
-        super(primaryNo, globalNo);
+    private TypicalMorph(MorphType type, int primaryNum, int globalNum){
+        super(primaryNum, globalNum);
+
         this.type = type;
+
+        assert this.getClass() == THISCLASS;
+
         return;
     }
 
@@ -87,11 +96,11 @@ public final class TypicalMorph extends I18nAlias {
      * @param top 最上位要素
      */
     private static void parse(Element top) {
-        NodeList groupList = top.getElementsByTagName("morphGroup");
+        NodeList groupList = top.getElementsByTagName(ELEM_MORPHGROUP);
         int groupNo = groupList.getLength();
         for(int idx = 0; idx < groupNo; idx++){
-            Element group = (Element) groupList.item(idx);
-            parseGroup(group);
+            Element groupElem = (Element) groupList.item(idx);
+            parseGroup(groupElem);
         }
 
         // 必要に応じモーフ枠に不変空リスト登録
@@ -106,21 +115,22 @@ public final class TypicalMorph extends I18nAlias {
 
     /**
      * モーフグループ構造を解読する。
-     * @param group morphGroup要素
+     * @param groupElem morphGroup要素
      */
-    private static void parseGroup(Element group){
-        String typeAttr = group.getAttribute("type");
+    private static void parseGroup(Element groupElem){
+        String typeAttr = groupElem.getAttribute(ATTR_TYPE);
         MorphType morphType = MorphType.valueOf(typeAttr);
 
-        NodeList morphList = group.getElementsByTagName("morph");
+        NodeList morphList = groupElem.getElementsByTagName(ELEM_MORPH);
         int morphNo = morphList.getLength();
         List<TypicalMorph> groupedList =
                 new ArrayList<TypicalMorph>(morphNo);
 
         for(int idx = 0; idx < morphNo; idx++){
-            Element morph = (Element) morphList.item(idx);
-            TypicalMorph common = parseMorph(morph, morphType);
-            groupedList.add(common);
+            Element morphElem = (Element) morphList.item(idx);
+            TypicalMorph morph = parseMorph(morphElem, morphType);
+            groupedList.add(morph);
+            MORPH_ALIAS_MAP.addAlias(morph);
         }
 
         groupedList = Collections.unmodifiableList(groupedList);
@@ -131,65 +141,58 @@ public final class TypicalMorph extends I18nAlias {
 
     /**
      * morph要素を解読する。
-     * @param morph morph要素
+     * @param morphElem morph要素
      * @param mtype モーフ種別
      * @return モーフ情報
      */
-    private static TypicalMorph parseMorph(Element morph, MorphType mtype){
-        NodeList primaryNodes = morph.getElementsByTagName("primary");
-        NodeList globalNodes  = morph.getElementsByTagName("global");
+    private static TypicalMorph parseMorph(Element morphElem,
+                                             MorphType mtype ){
+        NodeList primaryNodes = morphElem.getElementsByTagName(ELEM_PRIMARY);
+        NodeList globalNodes  = morphElem.getElementsByTagName(ELEM_GLOBAL);
         int primaryNo = primaryNodes.getLength();
         int globalNo  = globalNodes.getLength();
 
-        TypicalMorph typMorph = new TypicalMorph(mtype, primaryNo, globalNo);
+        assert primaryNo > 0;
+
+        TypicalMorph morph = new TypicalMorph(mtype, primaryNo, globalNo);
 
         for(int idx = 0; idx < primaryNo; idx++){
-            Element primary = (Element) primaryNodes.item(idx);
-            String name = primary.getAttribute("name");
-            typMorph.addPrimaryName(name);
+            Element primaryElem = (Element) primaryNodes.item(idx);
+            String primaryName = primaryElem.getAttribute(ATTR_NAME);
+            morph.addPrimaryName(primaryName);
         }
 
         for(int idx = 0; idx < globalNo; idx++){
-            Element global = (Element) globalNodes.item(idx);
-            String name = global.getAttribute("name");
-            typMorph.addGlobalName(name);
+            Element globalElem = (Element) globalNodes.item(idx);
+            String globalName = globalElem.getAttribute(ATTR_NAME);
+            morph.addGlobalName(globalName);
         }
 
-        for(String primaryName : typMorph.getPrimaryList()){
-            String key = normalize(primaryName).intern();
-            PRIMARY_MAP.put(key, typMorph);
-        }
-
-        for(String globalName : typMorph.getGlobalList()){
-            String key = normalize(globalName).intern();
-            GLOBAL_MAP.put(key, typMorph);
-        }
-
-        return typMorph;
+        return morph;
     }
 
     /**
-     * å\85¨ã\83¢ã\83¼ã\83\95æ\83\85å ±ã\82\92ä¸\80æ\84\8fã\81«é \86åº\8fä»\98ã\81\91ã\82\8b設å®\9aã\82\92è¡\8cã\81\86
+     * å\85¨ã\83¢ã\83¼ã\83\95æ\83\85å ±ã\81«é\80\9aã\81\97ç\95ªå\8f·ã\82\92ä»\98ã\81\91ã\82\8b
      * <p>同一グループ内ではXMLでの定義順が反映される。
      */
     private static void numbering(){
         int order = 0;
         for(MorphType morphType : MorphType.values()){
-            for(TypicalMorph common : TYPED_MAP.get(morphType)){
-                common.setOrderNo(order++);
+            for(TypicalMorph morph : TYPED_MAP.get(morphType)){
+                morph.setOrderNo(order++);
             }
         }
 
         return;
     }
 
-
     /**
-     * 種別ごとのモーフ情報リストを取得する。
+     * 種別ごとのモーフ情報不変リストを取得する。
      * @param morphType モーフ種別
-     * @return モーフ情報リスト
+     * @return モーフ情報不変リスト
      */
-    public static List<TypicalMorph> getTypedMorphList(MorphType morphType){
+    public static List<TypicalMorph> getTypicalMorphList(
+            MorphType morphType ){
         List<TypicalMorph> result = TYPED_MAP.get(morphType);
         return result;
     }
@@ -201,8 +204,7 @@ public final class TypicalMorph extends I18nAlias {
      * @return モーフ情報。見つからなければnull
      */
     public static TypicalMorph findWithPrimary(String primaryName){
-        String key = normalize(primaryName);
-        TypicalMorph result = PRIMARY_MAP.get(key);
+        TypicalMorph result = MORPH_ALIAS_MAP.getAliasByPrimary(primaryName);
         return result;
     }
 
@@ -213,8 +215,7 @@ public final class TypicalMorph extends I18nAlias {
      * @return モーフ情報。見つからなければnull
      */
     public static TypicalMorph findWithGlobal(String globalName){
-        String key = normalize(globalName);
-        TypicalMorph result = GLOBAL_MAP.get(key);
+        TypicalMorph result = MORPH_ALIAS_MAP.getAliasByGlobal(globalName);
         return result;
     }
 
@@ -224,10 +225,8 @@ public final class TypicalMorph extends I18nAlias {
      * @return グローバル名。見つからなければnull
      */
     public static String primary2global(String primaryName){
-        TypicalMorph morph = findWithPrimary(primaryName);
-        if(morph == null) return null;
-        String global = morph.getTopGlobalName();
-        return global;
+        String globalName = MORPH_ALIAS_MAP.primary2global(primaryName);
+        return globalName;
     }
 
     /**
@@ -236,10 +235,8 @@ public final class TypicalMorph extends I18nAlias {
      * @return プライマリ名。見つからなければnull
      */
     public static String global2primary(String globalName){
-        TypicalMorph morph = findWithGlobal(globalName);
-        if(morph == null) return null;
-        String primary = morph.getTopPrimaryName();
-        return primary;
+        String primaryName = MORPH_ALIAS_MAP.global2primary(globalName);
+        return primaryName;
     }
 
 
diff --git a/src/main/java/jp/sfjp/mikutoga/typical/UniqBone.java b/src/main/java/jp/sfjp/mikutoga/typical/UniqBone.java
new file mode 100644 (file)
index 0000000..af171c5
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * unique bone information
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.typical;
+
+/**
+ * 特別扱いされるボーンに関する諸々。
+ */
+public final class UniqBone {
+
+    private static final String HIDARI_LEFT = "\u5de6";        // 「左」
+    private static final String MIGI_RIGHT  = "\u53f3";        // 「右」
+    private static final String HIZA_KNEE   = "\u3072\u3056"; // 「ひざ」
+
+
+    private static final String KNEE_L_PFX =
+            HIDARI_LEFT + HIZA_KNEE;          // 左ひざ
+    private static final String KNEE_R_PFX =
+            MIGI_RIGHT + HIZA_KNEE;           // 右ひざ
+
+    static{
+        assert "左ひざ".equals(KNEE_L_PFX);
+        assert "右ひざ".equals(KNEE_R_PFX);
+    }
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private UniqBone(){
+        assert false;
+        throw new AssertionError();
+    }
+
+    /**
+     * IK演算時の回転方向に制限を受ける「ひざボーン」か否か、
+     * ボーン名で判定する。
+     * <p>ボーンのプライマリ名が「左ひざ」もしくは「右ひざ」で始まれば
+     * ひざボーンとする。
+     * <p>ひざボーン名の例
+     * <ul>
+     * <li>「左ひざ」
+     * <li>「左ひざげり」
+     * </ul>
+     * <p>ひざボーン名ではない例
+     * <ul>
+     * <li>「左ひ」
+     * <li>「ひざ」
+     * <li>「前ひざ」
+     * <li>「左ひさ゛」
+     * <li>「左ヒザ」
+     * <li>「左ヒザ」
+     * <li>「左膝」
+     * <li>「Knee_L」
+     * </ul>
+     * @param boneNameJp プライマリボーン名
+     * @return ひざボーンならtrue
+     */
+    public static boolean isPrimaryKneeName(String boneNameJp){
+        if(boneNameJp.startsWith(KNEE_L_PFX)) return true;
+        if(boneNameJp.startsWith(KNEE_R_PFX)) return true;
+
+        return false;
+    }
+
+}
@@ -7,10 +7,10 @@
 
 /**
  * MMDコミュニティにおける一般的な慣例に関する情報を提供する。
- * <p>例)ボーン名やモーフ名の一般的な名前など
+ * <p>ä¾\8b\83\9cã\83¼ã\83³å\90\8dã\82\84ã\83¢ã\83¼ã\83\95å\90\8dã\81®ä¸\80è\88¬ç\9a\84ã\81ªå\90\8då\89\8dã\80\81対訳ã\81ªã\81©
  * <p>MikuMikuDance Ver.7.39同梱のモデルなどが主な情報源。
  */
 
-package jp.sourceforge.mikutoga.typical;
+package jp.sfjp.mikutoga.typical;
 
 /* EOF */
@@ -9,7 +9,8 @@
 
 <!DOCTYPE typicalBone [
     <!ELEMENT typicalBone (bone*) >
-    <!ELEMENT bone (primary+, global*) >
+    <!ELEMENT bone (root?, primary+, global*) >
+    <!ELEMENT root EMPTY >
     <!ELEMENT primary EMPTY >
     <!ELEMENT global EMPTY >
 
@@ -22,6 +23,7 @@
 
 
     <bone>
+        <root />
         <primary name="&#x30BB;&#x30F3;&#x30BF;&#x30FC;" />  <!-- センター -->
         <global name="center" />
     </bone>
     </bone>
 
     <bone>
+        <root />
         <primary name="&#x5DE6;&#x8DB3;&#xFF29;&#xFF2B;" />  <!-- 左足IK -->
         <global name="leg IK_L" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#x53F3;&#x8DB3;&#xFF29;&#xFF2B;" />  <!-- 右足IK -->
         <global name="leg IK_R" />
     </bone>
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;01" />  <!-- ボーン01 -->
         <global name="bone01" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;02" />  <!-- ボーン02 -->
         <global name="bone02" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;03" />  <!-- ボーン03 -->
         <global name="bone03" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;04" />  <!-- ボーン04 -->
         <global name="bone04" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;05" />  <!-- ボーン05 -->
         <global name="bone05" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;06" />  <!-- ボーン06 -->
         <global name="bone06" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;07" />  <!-- ボーン07 -->
         <global name="bone07" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;08" />  <!-- ボーン08 -->
         <global name="bone08" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;09" />  <!-- ボーン09 -->
         <global name="bone09" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;10" />  <!-- ボーン10 -->
         <global name="bone10" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;11" />  <!-- ボーン11 -->
         <global name="bone11" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;12" />  <!-- ボーン12 -->
         <global name="bone12" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;13" />  <!-- ボーン13 -->
         <global name="bone13" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;14" />  <!-- ボーン14 -->
         <global name="bone14" />
     </bone>
 
     <bone>
+        <root />
         <primary name="&#xFF8E;&#xFF9E;&#xFF70;&#xFF9D;15" />  <!-- ボーン15 -->
         <global name="bone15" />
     </bone>
diff --git a/src/test/java/jp/sfjp/mikutoga/typical/AliasMapTest.java b/src/test/java/jp/sfjp/mikutoga/typical/AliasMapTest.java
new file mode 100644 (file)
index 0000000..bd6e637
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ */
+
+package jp.sfjp.mikutoga.typical;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class AliasMapTest {
+
+    public AliasMapTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    static class TestAlias extends I18nAlias{
+    }
+
+    /**
+     * Test of normalize method, of class AliasMap.
+     */
+    @Test
+    public void testNormalize() {
+        System.out.println("normalize");
+
+        assertEquals("", AliasMap.normalize(""));
+
+        assertEquals("azAZ", AliasMap.normalize("azAZ"));
+        assertEquals("azAZ", AliasMap.normalize("azAZ"));
+
+        assertEquals("5678", AliasMap.normalize("5678"));
+        assertEquals("56VII八", AliasMap.normalize("5⑥Ⅶ八"));
+
+        assertEquals("ガ", AliasMap.normalize("ガ"));
+        assertEquals("ガ", AliasMap.normalize("ガ"));
+        assertEquals("ガ", AliasMap.normalize("ガ"));
+        assertEquals("ガ", AliasMap.normalize("カ"+"\u3099"));
+        assertEquals("カ\u0020\u3099", AliasMap.normalize("カ"+"\u309b"));
+
+        assertEquals("パ", AliasMap.normalize("パ"));
+        assertEquals("パ", AliasMap.normalize("パ"));
+        assertEquals("パ", AliasMap.normalize("パ"));
+        assertEquals("パ", AliasMap.normalize("ハ"+"\u309a"));
+        assertEquals("ハ\u0020\u309a", AliasMap.normalize("ハ"+"\u309c"));
+
+        assertEquals("リットル", AliasMap.normalize("㍑"));
+
+        return;
+    }
+
+    /**
+     * Test of addAlias method, of class AliasMap.
+     */
+    @Test
+    public void testAddAlias() {
+        System.out.println("addAlias");
+
+        AliasMap<TestAlias> map;
+        TestAlias alias1;
+
+        alias1 = new TestAlias();
+        alias1.addPrimaryName("p1");
+        alias1.addPrimaryName("p2");
+        alias1.addGlobalName("g1");
+        alias1.addGlobalName("g2");
+
+        map = new AliasMap<TestAlias>();
+        map.addAlias(alias1);
+
+        assertSame(alias1, map.getAlias("p1"));
+        assertSame(alias1, map.getAlias("p2"));
+        assertSame(alias1, map.getAlias("g1"));
+        assertSame(alias1, map.getAlias("g2"));
+
+        assertNull(map.getAlias("ZZZ"));
+
+        assertEquals("g1", map.primary2global("p2"));
+        assertEquals("p1", map.global2primary("g2"));
+
+        assertNull(map.primary2global("ZZZ"));
+        assertNull(map.global2primary("ZZZ"));
+
+
+        TestAlias aliasHand;
+        TestAlias aliasFoot;
+
+        aliasHand = new TestAlias();
+        aliasHand.addPrimaryName("手1");
+        aliasHand.addPrimaryName("おてて2");
+        aliasHand.addPrimaryName("h");
+        aliasHand.addPrimaryName("bone");
+        aliasHand.addPrimaryName("cross1");
+        aliasHand.addGlobalName("hand1");
+        aliasHand.addGlobalName("paw2");
+        aliasHand.addGlobalName("h");
+        aliasHand.addGlobalName("bone");
+        aliasHand.addGlobalName("cross2");
+
+        aliasFoot = new TestAlias();
+        aliasFoot.addPrimaryName("足1");
+        aliasFoot.addPrimaryName("あんよ2");
+        aliasFoot.addPrimaryName("f");
+        aliasFoot.addPrimaryName("bone");
+        aliasFoot.addPrimaryName("cross2");
+        aliasFoot.addGlobalName("foot1");
+        aliasFoot.addGlobalName("hoof2");
+        aliasFoot.addGlobalName("f");
+        aliasFoot.addGlobalName("bone");
+        aliasFoot.addGlobalName("cross1");
+
+        map = new AliasMap<TestAlias>();
+        map.addAlias(aliasHand);
+        map.addAlias(aliasFoot);
+
+        assertSame(aliasHand, map.getAlias("h"));
+        assertSame(aliasFoot, map.getAlias("f"));
+        assertSame(aliasFoot, map.getAlias("bone"));
+        assertSame(aliasHand, map.getAlias("cross1"));
+        assertSame(aliasFoot, map.getAlias("cross2"));
+
+        assertSame(aliasHand, map.getAliasByPrimary("h"));
+        assertSame(aliasFoot, map.getAliasByPrimary("f"));
+        assertSame(aliasFoot, map.getAliasByPrimary("bone"));
+        assertSame(aliasHand, map.getAliasByPrimary("cross1"));
+        assertSame(aliasFoot, map.getAliasByPrimary("cross2"));
+
+        assertSame(aliasHand, map.getAliasByGlobal("h"));
+        assertSame(aliasFoot, map.getAliasByGlobal("f"));
+        assertSame(aliasFoot, map.getAliasByGlobal("bone"));
+        assertSame(aliasFoot, map.getAliasByGlobal("cross1"));
+        assertSame(aliasHand, map.getAliasByGlobal("cross2"));
+
+        return;
+    }
+
+}
diff --git a/src/test/java/jp/sfjp/mikutoga/typical/I18nAliasTest.java b/src/test/java/jp/sfjp/mikutoga/typical/I18nAliasTest.java
new file mode 100644 (file)
index 0000000..ac37bb1
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ */
+
+package jp.sfjp.mikutoga.typical;
+
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class I18nAliasTest {
+
+    public I18nAliasTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    /**
+     * Test of orderNo method, of class I18nAlias.
+     */
+    @Test
+    public void testOrderNo() {
+        System.out.println("OrderNo");
+
+        I18nAlias alias = new I18nAlias();
+
+        assertEquals(0, alias.getOrderNo());
+        alias.setOrderNo(99);
+        assertEquals(99, alias.getOrderNo());
+
+        I18nAlias alias2 = new I18nAlias();
+
+        alias2.setOrderNo(999);
+        assertTrue(0 > I18nAlias.ORDER_COMPARATOR.compare(alias, alias2));
+
+        alias2.setOrderNo(99);
+        assertTrue(0 == I18nAlias.ORDER_COMPARATOR.compare(alias, alias2));
+
+        alias2.setOrderNo(9);
+        assertTrue(0 < I18nAlias.ORDER_COMPARATOR.compare(alias, alias2));
+
+        return;
+    }
+
+    /**
+     * Test of method, of class I18nAlias.
+     */
+    @Test
+    public void testName() {
+        System.out.println("Name");
+
+        I18nAlias alias;
+
+        alias = new I18nAlias();
+
+        alias.addPrimaryName("p1");
+
+        assertEquals("p1", alias.getTopPrimaryName());
+        assertNull(alias.getTopGlobalName());
+
+        List<String> primaryList;
+        List<String> globalList;
+
+        primaryList = alias.getPrimaryNameList();
+        globalList = alias.getGlobalNameList();
+
+        assertEquals(1, primaryList.size());
+        assertEquals(0, globalList.size());
+        assertEquals("p1", primaryList.get(0));
+
+        alias.addGlobalName("g1");
+
+        assertEquals("g1", alias.getTopGlobalName());
+        globalList = alias.getGlobalNameList();
+        assertEquals(1, globalList.size());
+        assertEquals("g1", globalList.get(0));
+
+        return;
+    }
+
+}
@@ -1,7 +1,7 @@
 /*
  */
 
-package jp.sourceforge.mikutoga.typical;
+package jp.sfjp.mikutoga.typical;
 
 import java.util.List;
 import org.junit.After;
@@ -47,10 +47,10 @@ public class TypicalBoneTest {
 
         assertEquals("頭", result.getTopPrimaryName());
         assertEquals("head", result.getTopGlobalName());
-        assertEquals(1, result.getPrimaryList().size());
-        assertEquals("頭", result.getPrimaryList().get(0));
-        assertEquals(1, result.getGlobalList().size());
-        assertEquals("head", result.getGlobalList().get(0));
+        assertEquals(1, result.getPrimaryNameList().size());
+        assertEquals("頭", result.getPrimaryNameList().get(0));
+        assertEquals(1, result.getGlobalNameList().size());
+        assertEquals("head", result.getGlobalNameList().get(0));
 
         return;
     }
@@ -122,7 +122,7 @@ public class TypicalBoneTest {
     }
 
     /**
-     * Test of getBoneList method, of class TypicalBone.
+     * Test of getTypicalBoneList method, of class TypicalBone.
      */
     @Test
     public void testGetBoneList() {
@@ -130,7 +130,7 @@ public class TypicalBoneTest {
 
         List<TypicalBone> boneList;
 
-        boneList = TypicalBone.getBoneList();
+        boneList = TypicalBone.getTypicalBoneList();
 
         assertNotNull(boneList);
         assertEquals(77, boneList.size());
@@ -147,4 +147,31 @@ public class TypicalBoneTest {
         return;
     }
 
+    /**
+     * Test of isRoot method, of class TypicalBone.
+     */
+    @Test
+    public void testIsRoot() {
+        System.out.println("isRoot");
+
+        TypicalBone bone;
+
+        bone = TypicalBone.findWithPrimary("センター");
+        assertTrue(bone.isRoot());
+
+        bone = TypicalBone.findWithPrimary("頭");
+        assertFalse(bone.isRoot());
+
+        bone = TypicalBone.findWithPrimary("右足IK");
+        assertTrue(bone.isRoot());
+
+        bone = TypicalBone.findWithPrimary("左足IK");
+        assertTrue(bone.isRoot());
+
+        bone = TypicalBone.findWithPrimary("ボーン01");
+        assertTrue(bone.isRoot());
+
+        return;
+    }
+
 }
@@ -1,7 +1,7 @@
 /*
  */
 
-package jp.sourceforge.mikutoga.typical;
+package jp.sfjp.mikutoga.typical;
 
 import java.util.List;
 import jp.sfjp.mikutoga.pmd.MorphType;
@@ -37,7 +37,7 @@ public class TypicalMorphTest {
     }
 
     /**
-     * Test of getTypedMorphList method, of class TypicalMorph.
+     * Test of getTypicalMorphList method, of class TypicalMorph.
      */
     @Test
     public void testGetTypedMorphList() {
@@ -45,16 +45,16 @@ public class TypicalMorphTest {
 
         List<TypicalMorph> morphList;
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.EYEBROW);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.EYEBROW);
         assertEquals(6, morphList.size());
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.EYE);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.EYE);
         assertEquals(7, morphList.size());
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.LIP);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.LIP);
         assertEquals(12, morphList.size());
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.EXTRA);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.EXTRA);
         assertEquals(2, morphList.size());
 
         return;
@@ -74,10 +74,10 @@ public class TypicalMorphTest {
         assertEquals(MorphType.LIP, result.getMorphType());
         assertEquals("あ", result.getTopPrimaryName());
         assertEquals("a", result.getTopGlobalName());
-        assertEquals(1, result.getPrimaryList().size());
-        assertEquals("あ", result.getPrimaryList().get(0));
-        assertEquals(1, result.getGlobalList().size());
-        assertEquals("a", result.getGlobalList().get(0));
+        assertEquals(1, result.getPrimaryNameList().size());
+        assertEquals("あ", result.getPrimaryNameList().get(0));
+        assertEquals(1, result.getGlobalNameList().size());
+        assertEquals("a", result.getGlobalNameList().get(0));
 
         TypicalMorph result1;
         TypicalMorph result2;
@@ -88,11 +88,11 @@ public class TypicalMorphTest {
         assertEquals(MorphType.EXTRA, result1.getMorphType());
         assertEquals("べー", result1.getTopPrimaryName());
         assertEquals("tongue", result1.getTopGlobalName());
-        assertEquals(2, result1.getPrimaryList().size());
-        assertEquals("べー", result1.getPrimaryList().get(0));
-        assertEquals("ぺろっ", result1.getPrimaryList().get(1));
-        assertEquals(1, result1.getGlobalList().size());
-        assertEquals("tongue", result1.getGlobalList().get(0));
+        assertEquals(2, result1.getPrimaryNameList().size());
+        assertEquals("べー", result1.getPrimaryNameList().get(0));
+        assertEquals("ぺろっ", result1.getPrimaryNameList().get(1));
+        assertEquals(1, result1.getGlobalNameList().size());
+        assertEquals("tongue", result1.getGlobalNameList().get(0));
 
         return;
     }
@@ -176,28 +176,28 @@ public class TypicalMorphTest {
 
         List<TypicalMorph> morphList;
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.EYEBROW);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.EYEBROW);
 
         for(TypicalMorph morph : morphList){
             MorphType type = morph.getMorphType();
             assertEquals(MorphType.EYEBROW, type);
         }
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.EYE);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.EYE);
 
         for(TypicalMorph morph : morphList){
             MorphType type = morph.getMorphType();
             assertEquals(MorphType.EYE, type);
         }
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.LIP);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.LIP);
 
         for(TypicalMorph morph : morphList){
             MorphType type = morph.getMorphType();
             assertEquals(MorphType.LIP, type);
         }
 
-        morphList = TypicalMorph.getTypedMorphList(MorphType.EXTRA);
+        morphList = TypicalMorph.getTypicalMorphList(MorphType.EXTRA);
 
         for(TypicalMorph morph : morphList){
             MorphType type = morph.getMorphType();
diff --git a/src/test/java/jp/sfjp/mikutoga/typical/UniqBoneTest.java b/src/test/java/jp/sfjp/mikutoga/typical/UniqBoneTest.java
new file mode 100644 (file)
index 0000000..821114f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ */
+
+package jp.sfjp.mikutoga.typical;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class UniqBoneTest {
+
+    public UniqBoneTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    /**
+     * Test of isPrimaryKneeName method, of class UniqBone.
+     */
+    @Test
+    public void testIsPrimaryKneeName() {
+        System.out.println("isPrimaryKneeName");
+
+        assertTrue(UniqBone.isPrimaryKneeName("左ひざ"));
+        assertTrue(UniqBone.isPrimaryKneeName("右ひざ"));
+        assertTrue(UniqBone.isPrimaryKneeName("左ひざ蹴り"));
+
+        assertFalse(UniqBone.isPrimaryKneeName(""));
+        assertFalse(UniqBone.isPrimaryKneeName("左ひ"));
+        assertFalse(UniqBone.isPrimaryKneeName("ひざ"));
+        assertFalse(UniqBone.isPrimaryKneeName("前ひざ"));
+        assertFalse(UniqBone.isPrimaryKneeName("左ひさ゛"));
+        assertFalse(UniqBone.isPrimaryKneeName("左ヒザ"));
+        assertFalse(UniqBone.isPrimaryKneeName("左ヒザ"));
+        assertFalse(UniqBone.isPrimaryKneeName("左膝"));
+        assertFalse(UniqBone.isPrimaryKneeName("Knee_L"));
+
+        return;
+    }
+
+}