KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > wcf > tree > GroupingTreeModelDecorator


1 /*
2  * ====================================================================
3  * This software is subject to the terms of the Common Public License
4  * Agreement, available at the following URL:
5  * http://www.opensource.org/licenses/cpl.html .
6  * Copyright (C) 2003-2004 TONBELLER AG.
7  * All Rights Reserved.
8  * You must accept the terms of that agreement to use this software.
9  * ====================================================================
10  *
11  *
12  */

13 package com.tonbeller.wcf.tree;
14
15 import java.text.MessageFormat JavaDoc;
16 import java.util.Comparator JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Locale JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.TreeMap JavaDoc;
21
22 import com.tonbeller.tbutils.res.Resources;
23 import com.tonbeller.wcf.controller.RequestContext;
24 import com.tonbeller.wcf.selection.Unselectable;
25
26 /**
27  * groups large amounts of children into groups by inserting
28  * intermediate "artificial" nodes into the tree model. This is
29  * to prevent users to open a large amount of children.
30  *
31  * <p />
32  * The implementation uses HashMap's so the tree nodes must properly support this.
33  * TreeMaps will not work, because the result tree contains nodes of different
34  * types that are not really comparable.
35  *
36  * @author av
37  */

38 public class GroupingTreeModelDecorator extends TreeModelDecorator {
39
40   private int limit;
41   private LabelProvider labelProvider;
42   private Map JavaDoc mapChild2Parent;
43   private Map JavaDoc mapParent2Children;
44   private MessageFormat JavaDoc format;
45   private static final Object JavaDoc NULL = new Object JavaDoc();
46
47   /**
48    * creates a GroupingTreeModel using HashMap's
49    *
50    * @param labelProvider provides labels for the nodes in the tree model
51    * @param decoree the tree model
52    * @param limit number of children that will not divided into groups
53    */

54   public GroupingTreeModelDecorator(LabelProvider labelProvider, TreeModel decoree, int limit) {
55     super(decoree);
56     this.mapChild2Parent = new HashMap JavaDoc();
57     this.mapParent2Children = new HashMap JavaDoc();
58     initialize(labelProvider, limit);
59   }
60
61   /**
62    * creates a GroupingTreeModel using HashMap's
63    *
64    * @param nodeComparator compares nodes from underlying (decoree) tree model
65    * @param labelProvider provides labels for the nodes in the tree model
66    * @param decoree the tree model
67    * @param limit number of children that will not divided into groups
68    */

69   public GroupingTreeModelDecorator(Comparator JavaDoc nodeComparator, LabelProvider labelProvider,
70       TreeModel decoree, int limit) {
71     super(decoree);
72     Comparator JavaDoc comp = new GroupComparator(nodeComparator);
73     this.mapChild2Parent = new TreeMap JavaDoc(comp);
74     this.mapParent2Children = new TreeMap JavaDoc(comp);
75     initialize(labelProvider, limit);
76   }
77
78   class GroupComparator implements Comparator JavaDoc {
79     Comparator JavaDoc nodeComparator;
80
81     public GroupComparator(Comparator JavaDoc nodeComparator) {
82       this.nodeComparator = nodeComparator;
83     }
84
85     public int compare(Object JavaDoc o1, Object JavaDoc o2) {
86
87       // compare NULL placeholder object
88
if (o1 == NULL && o2 == NULL)
89         return 0;
90       else if (o1 == NULL)
91         return -1;
92       else if (o2 == NULL)
93         return 1;
94
95       // Group node involved?
96
if (o1 instanceof Group) {
97         if (o2 instanceof Group)
98           return ((Group) o1).compareTo(o2);
99         return 1;
100       } else if (o2 instanceof Group)
101         return -1;
102
103       // other nodes
104
return nodeComparator.compare(o1, o2);
105     }
106   };
107
108   TreeModelChangeListener listener = new TreeModelChangeListener() {
109     public void treeModelChanged(TreeModelChangeEvent event) {
110       if (event.getSubtree() != null) {
111         invalidateSubtree(event.getSubtree());
112       } else {
113         mapChild2Parent.clear();
114         mapParent2Children.clear();
115       }
116     }
117
118   };
119   
120   private void invalidateSubtree(Object JavaDoc parent) {
121     Object JavaDoc[] children = (Object JavaDoc[]) mapParent2Children.remove(parent);
122     if (children != null) {
123       for (int i = 0; i < children.length; i++) {
124         mapChild2Parent.remove(children[i]);
125         invalidateSubtree(children[i]);
126       }
127     }
128   }
129
130   private void initialize(LabelProvider labelProvider, int limit) {
131     this.limit = limit;
132     this.labelProvider = labelProvider;
133     super.getDecoree().addTreeModelChangeListener(listener);
134
135     // label format
136
RequestContext ctx = RequestContext.instance(false);
137     if (ctx != null) {
138       Resources res = ctx.getResources(GroupingTreeModelDecorator.class);
139       String JavaDoc fmt = res.getString("tree.group.label");
140       Locale JavaDoc loc = res.getLocale();
141       this.format = new MessageFormat JavaDoc(fmt, loc);
142     } else {
143       /* test environment */
144       this.format = new MessageFormat JavaDoc("{0} ...");
145     }
146   }
147
148   public class Group implements Unselectable, Comparable JavaDoc {
149
150     String JavaDoc label;
151
152     public Group(String JavaDoc label) {
153       this.label = label;
154     }
155
156     public Group(Object JavaDoc[] children) {
157       Object JavaDoc[] args = new Object JavaDoc[2];
158       args[0] = labelProvider.getLabel(children[0]);
159       args[1] = labelProvider.getLabel(children[children.length - 1]);
160       label = format.format(args, new StringBuffer JavaDoc(), null).toString();
161     }
162
163     public String JavaDoc toString() {
164       return label;
165     }
166
167     public int compareTo(Object JavaDoc o) {
168       Group g = (Group) o;
169       return label.compareTo(g.label);
170     }
171   }
172
173   private void updateCache(Object JavaDoc parent, Object JavaDoc[] children) {
174     if (parent == null)
175       parent = NULL;
176     mapParent2Children.put(parent, children);
177     for (int i = 0; i < children.length; i++)
178       mapChild2Parent.put(children[i], parent);
179   }
180
181   Object JavaDoc[] group(Object JavaDoc parent, Object JavaDoc[] children) {
182     // no need for grouping
183
if (limit <= 0 || children.length <= limit) {
184       updateCache(parent, children);
185       return children;
186     }
187     // number of groups
188
int groupCount = (children.length + limit - 1) / limit;
189     Group[] groups = new Group[groupCount];
190     for (int i = 0; i < groupCount; i++) {
191       // compute which children belong to this group
192
int fromIndex = i * limit;
193       int toIndex = (i + 1) * limit;
194       if (toIndex > children.length)
195         toIndex = children.length;
196       int count = toIndex - fromIndex;
197       // create group node
198
Object JavaDoc[] arr = new Object JavaDoc[count];
199       System.arraycopy(children, fromIndex, arr, 0, count);
200       groups[i] = new Group(arr);
201       updateCache(groups[i], arr);
202     }
203     updateCache(parent, groups);
204     return group(parent, groups);
205   }
206
207   public Object JavaDoc[] getRoots() {
208     Object JavaDoc[] roots = (Object JavaDoc[]) mapParent2Children.get(NULL);
209     if (roots != null)
210       return roots;
211     return group(NULL, super.getRoots());
212   }
213
214   public boolean hasChildren(Object JavaDoc node) {
215     return node instanceof Group || super.hasChildren(node);
216   }
217
218   public Object JavaDoc[] getChildren(Object JavaDoc node) {
219     Object JavaDoc[] children = (Object JavaDoc[]) mapParent2Children.get(node);
220     if (children != null)
221       return children;
222     return group(node, super.getChildren(node));
223   }
224
225   public Object JavaDoc getParent(Object JavaDoc node) {
226     Object JavaDoc parent = mapChild2Parent.get(node);
227     if (parent == null) {
228       parent = super.getParent(node);
229       Object JavaDoc[] children;
230       if (parent == null)
231         children = super.getRoots();
232       else
233         children = super.getChildren(parent);
234       group(parent, children);
235     }
236     // now its in the map!
237
parent = mapChild2Parent.get(node);
238     return parent == NULL ? null : parent;
239   }
240
241   public int getLimit() {
242     return limit;
243   }
244
245   public void setLimit(int limit) {
246     this.limit = limit;
247     fireModelChanged(false);
248   }
249 }
Popular Tags