OSDN Git Service

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