2 * model of lands for JTree view
4 * License : The MIT License
5 * Copyright(c) 2008 olyutorskii
8 package jp.sfjp.jindolf.data;
10 import java.text.MessageFormat;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.List;
16 import java.util.Objects;
17 import javax.swing.event.EventListenerList;
18 import javax.swing.event.TreeModelEvent;
19 import javax.swing.event.TreeModelListener;
20 import javax.swing.tree.TreeModel;
21 import javax.swing.tree.TreePath;
22 import jp.sourceforge.jindolf.corelib.LandDef;
25 * {@link javax.swing.JTree}のモデルとして国一覧と村一覧を管理。
27 * <p>ツリー階層は ROOT - 国 - 範囲セクション - 村 の4階層。
31 public class LandsTreeModel implements TreeModel{
33 private static final Object ROOT = new Object();
34 private static final int SECTION_INTERVAL = 100;
37 private final EventListenerList listeners;
39 private final List<Land> landList;
40 private final Map<Land, List<VillageSection> > sectionMap;
42 private boolean ascending = false;
48 public LandsTreeModel(){
51 this.listeners = new EventListenerList();
53 this.landList = buildLandList();
54 this.sectionMap = new HashMap<>();
61 * ツリーのルートオブジェクトか否か判定する。
66 private static boolean isRoot(Object obj){
67 boolean result = Objects.equals(ROOT, obj);
78 private static List<Land> buildLandList(){
79 List<LandDef> landDefList = CoreData.getLandDefList();
80 List<Land> newList = new ArrayList<>(landDefList.size());
82 landDefList.stream().map(landDef ->
84 ).forEachOrdered(land -> {
88 return Collections.unmodifiableList(newList);
92 * 与えられた国の全ての村を指定されたinterval間隔で格納するために、
96 * @param interval 範囲セクション間の村ID間隔
98 * @throws java.lang.IllegalArgumentException intervalが正でない
100 private static List<VillageSection> getSectionList(Land land,
102 throws IllegalArgumentException{
104 throw new IllegalArgumentException();
107 String pfx = land.getLandDef().getLandPrefix();
108 List<Village> span = new ArrayList<>(interval);
110 List<VillageSection> result = new ArrayList<>(2500 / interval);
112 boolean loop1st = true;
116 for(Village village : land.getVillageList()){
117 int vid = village.getVillageIDNum();
120 rangeStart = vid / interval * interval;
121 rangeEnd = rangeStart + interval - 1;
126 VillageSection section = new VillageSection(
127 pfx, rangeStart, rangeEnd, span);
131 rangeStart = vid / interval * interval;
132 rangeEnd = rangeStart + interval - 1;
138 if( ! span.isEmpty()){
139 VillageSection section = new VillageSection(
140 pfx, rangeStart, rangeEnd, span);
154 public List<Land> getLandList(){
155 return this.landList;
159 * 指定した国の村一覧でツリーリストを更新し、
162 * <p>2020-04現在、もはや村一覧が増減することはない。
166 public void updateVillageList(Land land){
167 List<VillageSection> sectionList =
168 getSectionList(land, SECTION_INTERVAL);
169 this.sectionMap.put(land, sectionList);
171 int[] childIndices = new int[sectionList.size()];
172 for(int ct = 0; ct < childIndices.length; ct++){
173 childIndices[ct] = ct;
175 Object[] children = sectionList.toArray();
177 TreePath treePath = new TreePath(ROOT);
178 treePath = treePath.pathByAddingChild(land);
180 TreeModelEvent event = new TreeModelEvent(this,
184 fireTreeStructureChanged(event);
192 * <p>場合によってはTreeModelEventが発生する。
194 * @param ascending trueなら昇順
196 public void setAscending(boolean ascending){
197 if(this.ascending == ascending) return;
199 this.ascending = ascending;
200 fireLandListChanged();
208 * @param lst {@inheritDoc}
211 public void addTreeModelListener(TreeModelListener lst){
212 this.listeners.add(TreeModelListener.class, lst);
219 * @param lst {@inheritDoc}
222 public void removeTreeModelListener(TreeModelListener lst){
223 this.listeners.remove(TreeModelListener.class, lst);
232 private TreeModelListener[] getTreeModelListeners(){
233 return this.listeners.getListeners(TreeModelListener.class);
239 * @param event ツリーイベント
241 protected void fireTreeStructureChanged(TreeModelEvent event){
242 for(TreeModelListener listener : getTreeModelListeners()){
243 listener.treeStructureChanged(event);
249 * ツリー内容の国一覧が更新された事をリスナーに通知する。
251 private void fireLandListChanged(){
252 int size = getLandList().size();
253 int[] childIndices = new int[size];
254 for(int ct = 0; ct < size; ct++){
256 childIndices[ct] = index;
259 Object[] children = getLandList().toArray();
261 TreePath treePath = new TreePath(ROOT);
262 TreeModelEvent event = new TreeModelEvent(this,
266 fireTreeStructureChanged(event);
274 * @param parent {@inheritDoc}
275 * @param index {@inheritDoc}
276 * @return {@inheritDoc}
279 public Object getChild(Object parent, int index){
280 if(index < 0) return null;
281 if(index >= getChildCount(parent)) return null;
283 Object result = null;
286 List<Land> list = getLandList();
287 int landIndex = index;
288 if( ! this.ascending) landIndex = list.size() - index - 1;
289 Land land = list.get(landIndex);
291 }else if(parent instanceof Land){
292 Land land = (Land) parent;
293 List<VillageSection> sectionList = this.sectionMap.get(land);
294 int sectIndex = index;
295 if( ! this.ascending) sectIndex = sectionList.size() - index - 1;
296 VillageSection section = sectionList.get(sectIndex);
298 }else if(parent instanceof VillageSection){
299 VillageSection section = (VillageSection) parent;
300 int vilIndex = index;
301 if( ! this.ascending){
302 vilIndex = section.getVillageCount() - index - 1;
304 Village village = section.getVillage(vilIndex);
314 * @param parent {@inheritDoc}
315 * @return {@inheritDoc}
318 public int getChildCount(Object parent){
322 result = getLandList().size();
323 }else if(parent instanceof Land){
324 Land land = (Land) parent;
325 List<VillageSection> sectionList = this.sectionMap.get(land);
326 if(sectionList != null){
327 result = sectionList.size();
329 }else if(parent instanceof VillageSection){
330 VillageSection section = (VillageSection) parent;
331 result = section.getVillageCount();
340 * @param parent {@inheritDoc}
341 * @param child {@inheritDoc}
342 * @return {@inheritDoc}
345 public int getIndexOfChild(Object parent, Object child){
346 if(child == null) return -1;
351 List<Land> list = getLandList();
352 int index = list.indexOf(child);
353 if( ! this.ascending) index = list.size() - index - 1;
355 }else if(parent instanceof Land){
356 Land land = (Land) parent;
357 List<VillageSection> sectionList = this.sectionMap.get(land);
358 int index = sectionList.indexOf(child);
359 if( ! this.ascending) index = sectionList.size() - index - 1;
361 }else if(parent instanceof VillageSection){
362 VillageSection section = (VillageSection) parent;
363 int index = section.getIndexOfVillage(child);
364 if( ! this.ascending){
365 index = section.getVillageCount() - index - 1;
376 * @return {@inheritDoc}
379 public Object getRoot(){
386 * @param node {@inheritDoc}
387 * @return {@inheritDoc}
390 public boolean isLeaf(Object node){
391 if(node instanceof Village) return true;
392 if(node instanceof VillageSection) return false;
393 if(node instanceof Land) return false;
394 if(isRoot(node)) return false;
401 * <p>※ たぶん使わないので必ず失敗させている。
403 * @param path {@inheritDoc}
404 * @param newValue {@inheritDoc}
407 public void valueForPathChanged(TreePath path, Object newValue){
408 throw new UnsupportedOperationException("Not supported yet.");
413 * 村IDで範囲指定した、村のセクション集合。国-村間の中間ツリー。
415 * @see javax.swing.tree.TreeModel
417 private static final class VillageSection{
419 private static final String FORM_NODE =
420 "{0}{1,number,#} ~ {0}{2,number,#}";
421 private static final String FORM_NODE_G =
422 "{0}{1,number,#000} ~ {0}{2,number,#000}";
425 private final int startId;
426 private final int endId;
428 private final String text;
430 private final List<Village> villageList;
436 * @param prefix 国名プレフィクス
437 * @param startId 区間開始村ID
438 * @param endId 区間終了村ID
439 * @param spanList 村の区間リスト
440 * @throws java.lang.IndexOutOfBoundsException IDの範囲指定が変
443 String prefix, int startId, int endId, List<Village> spanList)
444 throws IndexOutOfBoundsException{
447 if(startId < 0 || startId > endId){
448 throw new IndexOutOfBoundsException();
451 this.startId = startId;
455 if("G".equals(prefix)) format = FORM_NODE_G;
456 else format = FORM_NODE;
457 this.text = MessageFormat.format(
458 format, prefix, this.startId, this.endId);
460 List<Village> newList = new ArrayList<>(spanList);
461 this.villageList = Collections.unmodifiableList(newList);
463 assert this.endId - this.startId + 1 >= this.villageList.size();
470 * セクション内に含まれる村の総数を返す。
472 * <p>ほとんどの場合はintervalと同じ数。
476 int getVillageCount(){
477 return this.villageList.size();
481 * セクション内に含まれるindex番目の村を返す。
483 * @param index インデックス
486 Village getVillage(int index){
487 return this.villageList.get(index);
491 * セクション内における、指定された子(村)のインデックス位置を返す。
496 int getIndexOfVillage(Object child){
497 return this.villageList.indexOf(child);
510 public String toString(){