OSDN Git Service

PmdLimitsパッケージ移動
[mikutoga/TogaGem.git] / src / main / java / jp / sfjp / mikutoga / typical / TypicalBone.java
1 /*
2  * typical bone information
3  *
4  * License : The MIT License
5  * Copyright(c) 2011 MikuToga Partners
6  */
7
8 package jp.sfjp.mikutoga.typical;
9
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.util.Collections;
13 import java.util.LinkedList;
14 import java.util.List;
15 import javax.xml.parsers.ParserConfigurationException;
16 import org.w3c.dom.Element;
17 import org.w3c.dom.NodeList;
18 import org.xml.sax.SAXException;
19
20 /**
21  * 一般的な標準ボーン構成に関する情報。
22  * <p>各ボーン情報はひとつ以上のプライマリ名(≒日本語名)と
23  * ゼロ個以上のグローバル名(≒英語名)を持つ。
24  * <p>選択基準は独断。
25  * <p>和英対訳はMMD Ver7.39の同梱モデルにほぼ準拠。
26  */
27 public final class TypicalBone extends I18nAlias {
28
29     private static final Class<?> THISCLASS = TypicalBone.class;
30     private static final String BONE_XML = "resources/typicalBone.xml";
31
32     private static final String ELEM_BONE    = "bone";
33     private static final String ELEM_ROOT    = "root";
34     private static final String ELEM_PRIMARY = "primary";
35     private static final String ELEM_GLOBAL  = "global";
36     private static final String ATTR_NAME    = "name";
37
38     private static final List<TypicalBone> BONE_LIST =
39             new LinkedList<TypicalBone>();
40     private static final AliasMap<TypicalBone> BONE_ALIAS_MAP =
41             new AliasMap<TypicalBone>();
42
43     private static final List<TypicalBone> BONE_UNMODLIST =
44             Collections.unmodifiableList(BONE_LIST);
45
46     static{
47         InputStream is = THISCLASS.getResourceAsStream(BONE_XML);
48
49         Element top;
50         try{
51             top = I18nAlias.loadXml(is);
52         }catch(ParserConfigurationException e){
53             throw new ExceptionInInitializerError(e);
54         }catch(SAXException e){
55             throw new ExceptionInInitializerError(e);
56         }catch(IOException e){
57             throw new ExceptionInInitializerError(e);
58         }
59
60         parse(top);
61
62         numbering();
63     }
64
65
66     private boolean isRoot;
67
68
69     /**
70      * コンストラクタ。
71      * <p>各初期数が0以下の場合は、
72      * 状況に応じて伸長する連結リストが用意される。
73      * @param primaryNum プライマリ名初期数。
74      * @param globalNum グローバル名初期数。
75      */
76     private TypicalBone(int primaryNum, int globalNum){
77         super(primaryNum, globalNum);
78         assert this.getClass() == THISCLASS;
79         return;
80     }
81
82
83     /**
84      * XML文書の最上位構造を解読する。
85      * @param top 最上位要素
86      */
87     private static void parse(Element top) {
88         NodeList boneList = top.getElementsByTagName(ELEM_BONE);
89         int boneNo = boneList.getLength();
90         for(int idx = 0; idx < boneNo; idx++){
91             Element boneElem = (Element) boneList.item(idx);
92             TypicalBone typBone = parseBone(boneElem);
93             BONE_LIST.add(typBone);
94             BONE_ALIAS_MAP.addAlias(typBone);
95         }
96
97         return;
98     }
99
100     /**
101      * bone要素を解読する。
102      * @param boneElem bone要素
103      * @return ボーン情報
104      */
105     private static TypicalBone parseBone(Element boneElem){
106         NodeList primaryNodes = boneElem.getElementsByTagName(ELEM_PRIMARY);
107         NodeList globalNodes  = boneElem.getElementsByTagName(ELEM_GLOBAL);
108         int primaryNo = primaryNodes.getLength();
109         int globalNo  = globalNodes.getLength();
110
111         assert primaryNo > 0;
112
113         TypicalBone bone = new TypicalBone(primaryNo, globalNo);
114
115         for(int idx = 0; idx < primaryNo; idx++){
116             Element primaryElem = (Element) primaryNodes.item(idx);
117             String name = primaryElem.getAttribute(ATTR_NAME);
118             bone.addPrimaryName(name);
119         }
120
121         for(int idx = 0; idx < globalNo; idx++){
122             Element globalElem = (Element) globalNodes.item(idx);
123             String name = globalElem.getAttribute(ATTR_NAME);
124             bone.addGlobalName(name);
125         }
126
127         NodeList rootNodes = boneElem.getElementsByTagName(ELEM_ROOT);
128         if(rootNodes.getLength() > 0){
129             bone.isRoot = true;
130         }else{
131             bone.isRoot = false;
132         }
133
134         return bone;
135     }
136
137     /**
138      * 全ボーン情報に通し番号を付ける。
139      * <p>XMLでの定義順が反映される。
140      */
141     private static void numbering(){
142         int order = 0;
143         for(TypicalBone bone : BONE_LIST){
144             bone.setOrderNo(order++);
145         }
146
147         return;
148     }
149
150     /**
151      * 全ボーンの不変リストを返す。
152      * @return 全ボーンのリスト
153      */
154     public static List<TypicalBone> getTypicalBoneList(){
155         return BONE_UNMODLIST;
156     }
157
158     /**
159      * プライマリ名の合致するボーン情報を返す。
160      * NFKCで正規化されたプライマリ名で検索される。
161      * @param primaryName プライマリ名
162      * @return モーフ情報。見つからなければnull
163      */
164     public static TypicalBone findWithPrimary(String primaryName){
165         TypicalBone result = BONE_ALIAS_MAP.getAliasByPrimary(primaryName);
166         return result;
167     }
168
169     /**
170      * グローバル名の合致するボーン情報を返す。
171      * NFKCで正規化されたグローバル名で検索される。
172      * @param globalName グローバル名
173      * @return モーフ情報。見つからなければnull
174      */
175     public static TypicalBone findWithGlobal(String globalName){
176         TypicalBone result = BONE_ALIAS_MAP.getAliasByGlobal(globalName);
177         return result;
178     }
179
180     /**
181      * プライマリ名をグローバル名に変換する。
182      * @param primaryName プライマリ名
183      * @return グローバル名。見つからなければnull
184      */
185     public static String primary2global(String primaryName){
186         String globalName = BONE_ALIAS_MAP.primary2global(primaryName);
187         return globalName;
188     }
189
190     /**
191      * グローバル名をプライマリ名へ変換する。
192      * @param globalName グローバル名
193      * @return プライマリ名。見つからなければnull
194      */
195     public static String global2primary(String globalName){
196         String primaryName = BONE_ALIAS_MAP.global2primary(globalName);
197         return primaryName;
198     }
199
200
201     /**
202      * このボーンが親を持たないルートボーンとして扱われる慣習なのか
203      * 判定する。
204      * <p>※「全親」ボーンに関する慣習は無視される。
205      * @return 親を持たなければtrue
206      */
207     public boolean isRoot(){
208         return this.isRoot;
209     }
210
211 }