1 13 package com.tonbeller.wcf.tree; 14 15 import java.text.MessageFormat ; 16 import java.util.Comparator ; 17 import java.util.HashMap ; 18 import java.util.Locale ; 19 import java.util.Map ; 20 import java.util.TreeMap ; 21 22 import com.tonbeller.tbutils.res.Resources; 23 import com.tonbeller.wcf.controller.RequestContext; 24 import com.tonbeller.wcf.selection.Unselectable; 25 26 38 public class GroupingTreeModelDecorator extends TreeModelDecorator { 39 40 private int limit; 41 private LabelProvider labelProvider; 42 private Map mapChild2Parent; 43 private Map mapParent2Children; 44 private MessageFormat format; 45 private static final Object NULL = new Object (); 46 47 54 public GroupingTreeModelDecorator(LabelProvider labelProvider, TreeModel decoree, int limit) { 55 super(decoree); 56 this.mapChild2Parent = new HashMap (); 57 this.mapParent2Children = new HashMap (); 58 initialize(labelProvider, limit); 59 } 60 61 69 public GroupingTreeModelDecorator(Comparator nodeComparator, LabelProvider labelProvider, 70 TreeModel decoree, int limit) { 71 super(decoree); 72 Comparator comp = new GroupComparator(nodeComparator); 73 this.mapChild2Parent = new TreeMap (comp); 74 this.mapParent2Children = new TreeMap (comp); 75 initialize(labelProvider, limit); 76 } 77 78 class GroupComparator implements Comparator { 79 Comparator nodeComparator; 80 81 public GroupComparator(Comparator nodeComparator) { 82 this.nodeComparator = nodeComparator; 83 } 84 85 public int compare(Object o1, Object o2) { 86 87 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 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 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 parent) { 121 Object [] children = (Object []) 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 RequestContext ctx = RequestContext.instance(false); 137 if (ctx != null) { 138 Resources res = ctx.getResources(GroupingTreeModelDecorator.class); 139 String fmt = res.getString("tree.group.label"); 140 Locale loc = res.getLocale(); 141 this.format = new MessageFormat (fmt, loc); 142 } else { 143 144 this.format = new MessageFormat ("{0} ..."); 145 } 146 } 147 148 public class Group implements Unselectable, Comparable { 149 150 String label; 151 152 public Group(String label) { 153 this.label = label; 154 } 155 156 public Group(Object [] children) { 157 Object [] args = new Object [2]; 158 args[0] = labelProvider.getLabel(children[0]); 159 args[1] = labelProvider.getLabel(children[children.length - 1]); 160 label = format.format(args, new StringBuffer (), null).toString(); 161 } 162 163 public String toString() { 164 return label; 165 } 166 167 public int compareTo(Object o) { 168 Group g = (Group) o; 169 return label.compareTo(g.label); 170 } 171 } 172 173 private void updateCache(Object parent, Object [] 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 [] group(Object parent, Object [] children) { 182 if (limit <= 0 || children.length <= limit) { 184 updateCache(parent, children); 185 return children; 186 } 187 int groupCount = (children.length + limit - 1) / limit; 189 Group[] groups = new Group[groupCount]; 190 for (int i = 0; i < groupCount; i++) { 191 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 Object [] arr = new Object [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 [] getRoots() { 208 Object [] roots = (Object []) mapParent2Children.get(NULL); 209 if (roots != null) 210 return roots; 211 return group(NULL, super.getRoots()); 212 } 213 214 public boolean hasChildren(Object node) { 215 return node instanceof Group || super.hasChildren(node); 216 } 217 218 public Object [] getChildren(Object node) { 219 Object [] children = (Object []) mapParent2Children.get(node); 220 if (children != null) 221 return children; 222 return group(node, super.getChildren(node)); 223 } 224 225 public Object getParent(Object node) { 226 Object parent = mapChild2Parent.get(node); 227 if (parent == null) { 228 parent = super.getParent(node); 229 Object [] children; 230 if (parent == null) 231 children = super.getRoots(); 232 else 233 children = super.getChildren(parent); 234 group(parent, children); 235 } 236 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 |