OSDN Git Service

from subversion repository
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / LandsModel.java
1 /*\r
2  * model of lands for JTree view\r
3  *\r
4  * Copyright(c) 2008 olyutorskii\r
5  * $Id: LandsModel.java 953 2009-12-06 16:42:14Z olyutorskii $\r
6  */\r
7 \r
8 package jp.sourceforge.jindolf;\r
9 \r
10 import java.io.IOException;\r
11 import java.net.URISyntaxException;\r
12 import java.util.Collections;\r
13 import java.util.HashMap;\r
14 import java.util.LinkedList;\r
15 import java.util.List;\r
16 import java.util.Map;\r
17 import javax.swing.event.EventListenerList;\r
18 import javax.swing.event.TreeModelEvent;\r
19 import javax.swing.event.TreeModelListener;\r
20 import javax.swing.tree.TreeModel;\r
21 import javax.swing.tree.TreePath;\r
22 import javax.xml.parsers.DocumentBuilder;\r
23 import javax.xml.parsers.ParserConfigurationException;\r
24 import jp.sourceforge.jindolf.corelib.LandDef;\r
25 import org.xml.sax.SAXException;\r
26 \r
27 /**\r
28  * 国の集合。あらゆるデータモデルの大元。\r
29  * 国一覧と村一覧を管理。\r
30  * JTreeのモデルも兼用。\r
31  */\r
32 public class LandsModel implements TreeModel{ // ComboBoxModelも付けるか?\r
33 \r
34     /**\r
35      * 村IDで範囲指定した、村のセクション集合。国-村間の中間ツリー。\r
36      * @see javax.swing.tree.TreeModel\r
37      */\r
38     private static final class VillageSection{\r
39 \r
40         /**\r
41          * 与えられた国の全ての村を、指定されたinterval間隔でセクション化する。\r
42          * @param land 国\r
43          * @param interval セクションの間隔\r
44          * @return セクションのリスト\r
45          * @throws java.lang.IllegalArgumentException intervalが正でない\r
46          */\r
47         private static List<VillageSection> getSectionList(Land land,\r
48                                                              int interval )\r
49                 throws IllegalArgumentException{\r
50             if(interval <= 0){\r
51                 throw new IllegalArgumentException();\r
52             }\r
53 \r
54             List<Village> villageList = land.getVillageList();\r
55             Village village1st = villageList.get(0);\r
56             Village villageLast = villageList.get(villageList.size() - 1);\r
57 \r
58             int startID = village1st.getVillageIDNum();\r
59             int endID = villageLast.getVillageIDNum();\r
60 \r
61             List<VillageSection> result = new LinkedList<VillageSection>();\r
62 \r
63             int fixedStart = startID / interval * interval;\r
64             for(int ct = fixedStart; ct <= endID; ct += interval){\r
65                 VillageSection section =\r
66                         new VillageSection(land, ct, ct + interval - 1);\r
67                 result.add(section);\r
68             }\r
69 \r
70             return Collections.unmodifiableList(result);\r
71         }\r
72 \r
73         private final int startID;\r
74         private final int endID;\r
75         private final String prefix;\r
76 \r
77         private final List<Village> villageList = new LinkedList<Village>();\r
78 \r
79         /**\r
80          * セクション集合を生成する。\r
81          * @param land 国\r
82          * @param startID 開始村ID\r
83          * @param endID 終了村ID\r
84          * @throws java.lang.IndexOutOfBoundsException IDの範囲指定が変\r
85          */\r
86         private VillageSection(Land land, int startID, int endID)\r
87                 throws IndexOutOfBoundsException{\r
88             super();\r
89 \r
90             if(startID < 0 || startID > endID){\r
91                 throw new IndexOutOfBoundsException();\r
92             }\r
93 \r
94             this.startID = startID;\r
95             this.endID = endID;\r
96             this.prefix = land.getLandDef().getLandPrefix();\r
97 \r
98             for(Village village : land.getVillageList()){\r
99                 int id = village.getVillageIDNum();\r
100                 if(startID <= id && id <= endID){\r
101                     this.villageList.add(village);\r
102                 }\r
103             }\r
104 \r
105             return;\r
106         }\r
107 \r
108         /**\r
109          * セクションに含まれる村の総数を返す。\r
110          * @return 村の総数\r
111          */\r
112         private int getVillageCount(){\r
113             return this.villageList.size();\r
114         }\r
115 \r
116         /**\r
117          * セクションに含まれるindex番目の村を返す。\r
118          * @param index インデックス\r
119          * @return index番目の村\r
120          */\r
121         private Village getVillage(int index){\r
122             return this.villageList.get(index);\r
123         }\r
124 \r
125         /**\r
126          * セクションにおける、指定された子(村)のインデックス位置を返す。\r
127          * @param child 子\r
128          * @return インデックス位置\r
129          */\r
130         private int getIndexOfVillage(Object child){\r
131             return this.villageList.indexOf(child);\r
132         }\r
133 \r
134         /**\r
135          * セクションの文字列表記。\r
136          * JTree描画に反映される。\r
137          * @return 文字列表記\r
138          */\r
139         @Override\r
140         public String toString(){\r
141             StringBuilder result = new StringBuilder();\r
142             result.append(this.prefix).append(this.startID);\r
143             result.append(" ~ ");\r
144             result.append(this.prefix).append(this.endID);\r
145             return result.toString();\r
146         }\r
147     }\r
148 \r
149     private static final String ROOT = "ROOT";\r
150     private static final int SECTION_INTERVAL = 100;\r
151 \r
152     private final List<Land> landList = new LinkedList<Land>();\r
153     private final List<Land> unmodList =\r
154             Collections.unmodifiableList(this.landList);\r
155     private final Map<Land, List<VillageSection>> sectionMap =\r
156             new HashMap<Land, List<VillageSection>>();\r
157     private boolean isLandListLoaded = false;\r
158 \r
159     private final EventListenerList listeners = new EventListenerList();\r
160 \r
161     private boolean ascending = false;\r
162 \r
163     /**\r
164      * コンストラクタ。\r
165      * この時点ではまだ国一覧が読み込まれない。\r
166      */\r
167     public LandsModel(){\r
168         super();\r
169         return;\r
170     }\r
171 \r
172     /**\r
173      * 指定した国の村一覧を読み込む。\r
174      * @param land 国\r
175      * @throws java.io.IOException ネットワーク入出力の異常\r
176      */\r
177     public void loadVillageList(Land land) throws IOException{\r
178         land.updateVillageList();\r
179 \r
180         List<VillageSection> sectionList =\r
181                 VillageSection.getSectionList(land, SECTION_INTERVAL);\r
182         this.sectionMap.put(land, sectionList);\r
183 \r
184         int[] childIndices = new int[sectionList.size()];\r
185         for(int ct = 0; ct < childIndices.length; ct++){\r
186             childIndices[ct] = ct;\r
187         }\r
188         Object[] children = sectionList.toArray();\r
189 \r
190         Object[] path = {ROOT, land};\r
191         TreePath treePath = new TreePath(path);\r
192         TreeModelEvent event = new TreeModelEvent(this,\r
193                                                   treePath,\r
194                                                   childIndices,\r
195                                                   children     );\r
196         fireTreeStructureChanged(event);\r
197 \r
198         return;\r
199     }\r
200 \r
201     /**\r
202      * 国一覧を読み込む。\r
203      */\r
204     // TODO static にできない?\r
205     public void loadLandList(){\r
206         if(this.isLandListLoaded) return;\r
207 \r
208         this.landList.clear();\r
209 \r
210         List<LandDef> landDefList;\r
211         try{\r
212             DocumentBuilder builder = XmlUtils.createDocumentBuilder();\r
213             landDefList = LandDef.buildLandDefList(builder);\r
214         }catch(IOException e){\r
215             Jindolf.logger().fatal("failed to load land list", e);\r
216             return;\r
217         }catch(SAXException e){\r
218             Jindolf.logger().fatal("failed to load land list", e);\r
219             return;\r
220         }catch(URISyntaxException e){\r
221             Jindolf.logger().fatal("failed to load land list", e);\r
222             return;\r
223         }catch(ParserConfigurationException e){\r
224             Jindolf.logger().fatal("failed to load land list", e);\r
225             return;\r
226         }\r
227 \r
228         for(LandDef landDef : landDefList){\r
229             Land land = new Land(landDef);\r
230             this.landList.add(land);\r
231         }\r
232 \r
233         this.isLandListLoaded = true;\r
234 \r
235         fireLandListChanged();\r
236 \r
237         return;\r
238     }\r
239 \r
240     /**\r
241      * ツリー内容が更新された事をリスナーに通知する。\r
242      */\r
243     private void fireLandListChanged(){\r
244         int size = this.landList.size();\r
245         int[] childIndices = new int[size];\r
246         for(int ct = 0; ct < size; ct++){\r
247             int index = ct;\r
248             childIndices[ct] = index;\r
249         }\r
250 \r
251         Object[] children = this.landList.toArray();\r
252 \r
253         TreePath treePath = new TreePath(ROOT);\r
254         TreeModelEvent event = new TreeModelEvent(this,\r
255                                                   treePath,\r
256                                                   childIndices,\r
257                                                   children     );\r
258         fireTreeStructureChanged(event);\r
259 \r
260         return;\r
261     }\r
262 \r
263     /**\r
264      * ツリーの並び順を設定する。\r
265      * 場合によってはTreeModelEventが発生する。\r
266      * @param ascending trueなら昇順\r
267      */\r
268     public void setAscending(boolean ascending){\r
269         if(this.ascending == ascending) return;\r
270 \r
271         this.ascending = ascending;\r
272         fireLandListChanged();\r
273 \r
274         return;\r
275     }\r
276 \r
277     /**\r
278      * {@inheritDoc}\r
279      * @param l {@inheritDoc}\r
280      */\r
281     public void addTreeModelListener(TreeModelListener l){\r
282         this.listeners.add(TreeModelListener.class, l);\r
283         return;\r
284     }\r
285 \r
286     /**\r
287      * {@inheritDoc}\r
288      * @param l {@inheritDoc}\r
289      */\r
290     public void removeTreeModelListener(TreeModelListener l){\r
291         this.listeners.remove(TreeModelListener.class, l);\r
292         return;\r
293     }\r
294 \r
295     /**\r
296      * 登録中のリスナーのリストを得る。\r
297      * @return リスナーのリスト\r
298      */\r
299     private TreeModelListener[] getTreeModelListeners(){\r
300         return this.listeners.getListeners(TreeModelListener.class);\r
301     }\r
302 \r
303     /**\r
304      * 全リスナーにイベントを送出する。\r
305      * @param event ツリーイベント\r
306      */\r
307     protected void fireTreeStructureChanged(TreeModelEvent event){\r
308         for(TreeModelListener listener : getTreeModelListeners()){\r
309             listener.treeStructureChanged(event);\r
310         }\r
311         return;\r
312     }\r
313 \r
314     /**\r
315      * 国リストを得る。\r
316      * @return 国のリスト\r
317      */\r
318     public List<Land> getLandList(){\r
319         return this.unmodList;\r
320     }\r
321 \r
322     /**\r
323      * {@inheritDoc}\r
324      * @param parent {@inheritDoc}\r
325      * @param index {@inheritDoc}\r
326      * @return {@inheritDoc}\r
327      */\r
328     public Object getChild(Object parent, int index){\r
329         if(index < 0)                      return null;\r
330         if(index >= getChildCount(parent)) return null;\r
331 \r
332         if(parent == ROOT){\r
333             List<Land> list = getLandList();\r
334             int landIndex = index;\r
335             if( ! this.ascending) landIndex = list.size() - index - 1;\r
336             Land land = list.get(landIndex);\r
337             return land;\r
338         }\r
339         if(parent instanceof Land){\r
340             Land land = (Land)parent;\r
341             List<VillageSection> sectionList = this.sectionMap.get(land);\r
342             int sectIndex = index;\r
343             if( ! this.ascending) sectIndex = sectionList.size() - index - 1;\r
344             VillageSection section = sectionList.get(sectIndex);\r
345             return section;\r
346         }\r
347         if(parent instanceof VillageSection){\r
348             VillageSection section = (VillageSection)parent;\r
349             int vilIndex = index;\r
350             if( ! this.ascending){\r
351                 vilIndex = section.getVillageCount() - index - 1;\r
352             }\r
353             Village village = section.getVillage(vilIndex);\r
354             return village;\r
355         }\r
356         return null;\r
357     }\r
358 \r
359     /**\r
360      * {@inheritDoc}\r
361      * @param parent {@inheritDoc}\r
362      * @return {@inheritDoc}\r
363      */\r
364     public int getChildCount(Object parent){\r
365         if(parent == ROOT){\r
366             return getLandList().size();\r
367         }\r
368         if(parent instanceof Land){\r
369             Land land = (Land)parent;\r
370             List<VillageSection> sectionList = this.sectionMap.get(land);\r
371             if(sectionList == null) return 0;\r
372             return sectionList.size();\r
373         }\r
374         if(parent instanceof VillageSection){\r
375             VillageSection section = (VillageSection)parent;\r
376             return section.getVillageCount();\r
377         }\r
378         return 0;\r
379     }\r
380 \r
381     /**\r
382      * {@inheritDoc}\r
383      * @param parent {@inheritDoc}\r
384      * @param child {@inheritDoc}\r
385      * @return {@inheritDoc}\r
386      */\r
387     public int getIndexOfChild(Object parent, Object child){\r
388         if(child == null) return -1;\r
389         if(parent == ROOT){\r
390             List<Land> list = getLandList();\r
391             int index = list.indexOf(child);\r
392             if( ! this.ascending) index = list.size() - index - 1;\r
393             return index;\r
394         }\r
395         if(parent instanceof Land){\r
396             Land land = (Land)parent;\r
397             List<VillageSection> sectionList = this.sectionMap.get(land);\r
398             int index = sectionList.indexOf(child);\r
399             if( ! this.ascending) index = sectionList.size() - index - 1;\r
400             return index;\r
401         }\r
402         if(parent instanceof VillageSection){\r
403             VillageSection section = (VillageSection)parent;\r
404             int index = section.getIndexOfVillage(child);\r
405             if( ! this.ascending){\r
406                 index = section.getVillageCount() - index - 1;\r
407             }\r
408             return index;\r
409         }\r
410         return -1;\r
411     }\r
412 \r
413     /**\r
414      * {@inheritDoc}\r
415      * @return {@inheritDoc}\r
416      */\r
417     public Object getRoot(){\r
418         return ROOT;\r
419     }\r
420 \r
421     /**\r
422      * {@inheritDoc}\r
423      * @param node {@inheritDoc}\r
424      * @return {@inheritDoc}\r
425      */\r
426     public boolean isLeaf(Object node){\r
427         if(node == ROOT)                   return false;\r
428         if(node instanceof Land)           return false;\r
429         if(node instanceof VillageSection) return false;\r
430         if(node instanceof Village)        return true;\r
431         return true;\r
432     }\r
433 \r
434     /**\r
435      * {@inheritDoc}\r
436      * ※ たぶん使わないので必ず失敗させている。\r
437      * @param path {@inheritDoc}\r
438      * @param newValue {@inheritDoc}\r
439      */\r
440     public void valueForPathChanged(TreePath path, Object newValue){\r
441         throw new UnsupportedOperationException("Not supported yet.");\r
442     }\r
443 \r
444 }\r