KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > jpivot > navigator > member > MemberNavigator


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.jpivot.navigator.member;
14
15 import java.util.Collection JavaDoc;
16 import java.util.Comparator JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.Locale JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import org.apache.log4j.Logger;
24 import org.w3c.dom.Document JavaDoc;
25 import org.w3c.dom.Element JavaDoc;
26
27 import com.tonbeller.jpivot.core.ModelChangeEvent;
28 import com.tonbeller.jpivot.core.ModelChangeListener;
29 import com.tonbeller.jpivot.olap.model.Displayable;
30 import com.tonbeller.jpivot.olap.model.Hierarchy;
31 import com.tonbeller.jpivot.olap.model.Level;
32 import com.tonbeller.jpivot.olap.model.Member;
33 import com.tonbeller.jpivot.olap.model.OlapModel;
34 import com.tonbeller.jpivot.olap.model.OlapUtils;
35 import com.tonbeller.jpivot.olap.navi.MemberDeleter;
36 import com.tonbeller.jpivot.olap.navi.MemberTree;
37 import com.tonbeller.jpivot.ui.Available;
38 import com.tonbeller.tbutils.res.Resources;
39 import com.tonbeller.wcf.component.Component;
40 import com.tonbeller.wcf.controller.Dispatcher;
41 import com.tonbeller.wcf.controller.RequestContext;
42 import com.tonbeller.wcf.controller.RequestListener;
43 import com.tonbeller.wcf.scroller.Scroller;
44 import com.tonbeller.wcf.selection.SelectionModel;
45 import com.tonbeller.wcf.tree.CachingTreeModelDecorator;
46 import com.tonbeller.wcf.tree.DecoratedTreeModel;
47 import com.tonbeller.wcf.tree.DefaultDeleteNodeModel;
48 import com.tonbeller.wcf.tree.DefaultLabelProvider;
49 import com.tonbeller.wcf.tree.DefaultNodeRenderer;
50 import com.tonbeller.wcf.tree.DeleteNodeModel;
51 import com.tonbeller.wcf.tree.EnumBoundedTreeModelDecorator;
52 import com.tonbeller.wcf.tree.GroupingTreeModelDecorator;
53 import com.tonbeller.wcf.tree.LabelProvider;
54 import com.tonbeller.wcf.tree.MutableTreeModelDecorator;
55 import com.tonbeller.wcf.tree.TreeComponent;
56 import com.tonbeller.wcf.tree.TreeModel;
57 import com.tonbeller.wcf.ui.Button;
58 import com.tonbeller.wcf.utils.DomUtils;
59
60 /**
61  * GUI for choosing members. User of this class must call setHierarchy or setHierarchies before
62  * rendering.
63  *
64  * @author av
65  */

66 public class MemberNavigator extends TreeComponent implements ModelChangeListener, Available {
67
68   public static final String JavaDoc MEMBER_NAVIGATOR_LAZY_FETCH_CHILDREN = "MemberNavigator.lazyFetchChildren";
69   public static final String JavaDoc MEMBER_NAVIGATOR_EXPAND_SELECTED = "MemberNavigator.expandSelected";
70   public static final String JavaDoc MEMBER_NAVIGATOR_INITIAL_GROUPING = "MemberNavigator.initialGrouping";
71   public static final String JavaDoc MEMBER_NAVIGATOR_GROUPING_MEMBER_COUNT = "MemberNavigator.groupingMemberCount";
72   
73   private OlapModel olapModel;
74   private String JavaDoc title;
75   private RequestListener okHandler;
76   private RequestListener cancelHandler;
77   private boolean showSelectNoneButton;
78   private String JavaDoc okButtonId;
79   private String JavaDoc cancelButtonId;
80   private String JavaDoc selectVisibleButtonId;
81   private String JavaDoc selectNoneButtonId;
82   private String JavaDoc enableGroupingButtonId;
83   private String JavaDoc disableGroupingButtonId;
84   // contains a Tree (value) for a HierarchyArray (key)
85
private Map JavaDoc models = new HashMap JavaDoc();
86   private Resources resources;
87
88   private int groupingMemberCount = 12;
89   private boolean initialGrouping = true;
90   private boolean expandSelected = true;
91   private boolean lazyFetchChildren = false;
92
93   /**
94    * defines equals/hashCode for an array of hierarchies. Two arrays are equal if and only if all
95    * their hierarchies are equal.
96    *
97    * @author av
98    */

99   static class HierarchyArray {
100
101     private Hierarchy[] hiers;
102
103     public HierarchyArray(Hierarchy[] hiers) {
104       this.hiers = hiers;
105     }
106
107     public boolean equals(Object JavaDoc obj) {
108       if (!(obj instanceof HierarchyArray))
109         return false;
110       HierarchyArray that = (HierarchyArray) obj;
111       if (this.hiers.length != that.hiers.length)
112         return false;
113       for (int i = 0; i < hiers.length; i++)
114         if (!this.hiers[i].equals(that.hiers[i]))
115           return false;
116       return true;
117     }
118
119     public Hierarchy[] getHierarchies() {
120       return hiers;
121     }
122
123     public String JavaDoc getLabel() {
124       if (hiers.length == 0)
125         return ""; //$NON-NLS-1$
126
if (hiers.length == 1)
127         return hiers[0].getLabel();
128       StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
129       for (int i = 0; i < hiers.length; i++) {
130         if (i > 0)
131           sb.append(" / "); //$NON-NLS-1$
132
sb.append(hiers[i].getLabel());
133       }
134       return sb.toString();
135     }
136
137     public int hashCode() {
138       int code = 27;
139       for (int i = 0; i < hiers.length; i++) {
140         code ^= hiers[i].hashCode();
141       }
142       return code;
143     }
144
145     public void setHierarchies(Hierarchy[] hiers) {
146       this.hiers = hiers;
147     }
148
149   }
150
151   class SelectVisibleHandler implements RequestListener {
152     public void request(RequestContext context) throws Exception JavaDoc {
153       Scroller.enableScroller(context);
154       validate(context);
155       selectVisible();
156     }
157   }
158
159   class SelectNoneHandler implements RequestListener {
160     public void request(RequestContext context) throws Exception JavaDoc {
161       Scroller.enableScroller(context);
162       validate(context);
163       getSelectionModel().clear();
164     }
165   }
166
167   class SetGroupingHandler implements RequestListener {
168     boolean grouping;
169
170     SetGroupingHandler(boolean grouping) {
171       this.grouping = grouping;
172     }
173
174     public void request(RequestContext context) throws Exception JavaDoc {
175       validate(context);
176       setGrouping(grouping);
177     }
178   }
179
180   class MutableMemberTreeModelDecorator extends MutableTreeModelDecorator {
181     public MutableMemberTreeModelDecorator(TreeModel decoree) {
182       super(decoree);
183     }
184
185     public MutableMemberTreeModelDecorator(TreeModel decoree, Comparator JavaDoc comp) {
186       super(decoree, comp);
187     }
188
189     public boolean mayMove(Object JavaDoc scope, Object JavaDoc node) {
190       // non-members are virtual groups
191
if (!(node instanceof Member)) { return false; }
192       return super.mayMove(scope, node);
193     }
194   }
195
196   private static Logger logger = Logger.getLogger(MemberNavigator.class);
197
198   private DeleteNodeModel deleteModel = new DefaultDeleteNodeModel() {
199     public boolean isDeletable(Object JavaDoc node) {
200       if (olapModel == null)
201         return false;
202       MemberDeleter md = (MemberDeleter) olapModel.getExtension(MemberDeleter.ID);
203       if (md == null)
204         return false;
205       if (node instanceof Member)
206         return md.isDeletable((Member) node);
207       return false;
208     }
209   };
210
211   public MemberNavigator(String JavaDoc id, Component parent, OlapModel olapModel,
212       RequestListener okHandler, RequestListener cancelHandler) {
213     super(id, parent);
214     logger.info("creating instance: " + this);
215
216     this.olapModel = olapModel;
217     if (olapModel != null)
218       olapModel.addModelChangeListener(this);
219
220     this.okHandler = okHandler;
221     this.cancelHandler = cancelHandler;
222     setNodeRenderer(new DefaultNodeRenderer(labelProvider));
223     if (id == null)
224       id = DomUtils.randomId();
225     okButtonId = id + ".ok";
226     cancelButtonId = id + ".cancel";
227     selectVisibleButtonId = id + ".selectVisible";
228     selectNoneButtonId = id + ".selectNone";
229     enableGroupingButtonId = id + ".enableGrouping";
230     disableGroupingButtonId = id + ".disableGrouping";
231     setSelectionModel(new MemberSelectionModel());
232     setDeleteNodeModel(deleteModel);
233     // move nodes via Cut/Paste GUI
234
setCutPasteMode(true);
235   }
236
237   /**
238    * crerates a MemberNavigator that is not connected to a query. Before callin setHierarchies() the
239    * client must call setOlapModel().
240    */

241   public MemberNavigator(String JavaDoc id, Component parent, RequestListener okHandler,
242       RequestListener cancelHandler) {
243     this(id, parent, null, okHandler, cancelHandler);
244   }
245
246   public void setOlapModel(OlapModel newOlapModel) {
247     if (olapModel != null)
248       olapModel.removeModelChangeListener(this);
249     olapModel = newOlapModel;
250     if (olapModel != null)
251       olapModel.addModelChangeListener(this);
252     models.clear();
253   }
254
255   /**
256    * Returns the title.
257    *
258    * @return String
259    */

260   public String JavaDoc getTitle() {
261     return title;
262   }
263
264   public void initialize(RequestContext context) throws Exception JavaDoc {
265     super.initialize(context);
266
267     Dispatcher disp = super.getDispatcher();
268     disp.addRequestListener(selectVisibleButtonId, null, new SelectVisibleHandler());
269     disp.addRequestListener(selectNoneButtonId, null, new SelectNoneHandler());
270     disp.addRequestListener(enableGroupingButtonId, null, new SetGroupingHandler(true));
271     disp.addRequestListener(disableGroupingButtonId, null, new SetGroupingHandler(false));
272     disp.addRequestListener(okButtonId, null, okHandler);
273     disp.addRequestListener(cancelButtonId, null, cancelHandler);
274     resources = context.getResources(MemberNavigator.class);
275     
276     groupingMemberCount = resources.getOptionalInteger(MEMBER_NAVIGATOR_GROUPING_MEMBER_COUNT, groupingMemberCount);
277     initialGrouping = resources.getOptionalBoolean(MEMBER_NAVIGATOR_INITIAL_GROUPING, initialGrouping);
278     expandSelected = resources.getOptionalBoolean(MEMBER_NAVIGATOR_EXPAND_SELECTED, expandSelected);
279     lazyFetchChildren = resources.getOptionalBoolean(MEMBER_NAVIGATOR_LAZY_FETCH_CHILDREN, lazyFetchChildren);
280     
281     // test environment?
282
String JavaDoc s = context.getRequest().getParameter(MEMBER_NAVIGATOR_LAZY_FETCH_CHILDREN);
283     if (s != null)
284       lazyFetchChildren = Boolean.valueOf(s).booleanValue();
285   }
286
287   public Element JavaDoc render(RequestContext context, Document JavaDoc factory) throws Exception JavaDoc {
288     if (getModel() == null)
289       throw new IllegalStateException JavaDoc("must call MemberNavigator.setHierarchy() before rendering");
290     Element JavaDoc elem = super.render(context, factory);
291     elem.setAttribute("title", title);
292     elem.setAttribute("closeId", cancelButtonId);
293     renderButtons(elem, factory);
294     setError(null);
295     return elem;
296   }
297
298   private void renderButtons(Element JavaDoc parent, Document JavaDoc factory) {
299     Element JavaDoc buttons = factory.createElement("buttons");
300     parent.appendChild(buttons);
301     appendSelectButton(buttons);
302     appendGroupingButton(buttons);
303     Button.addButton(buttons, okButtonId, resources.getString("MemberNavigator.ok.title"));
304     Button.addButton(buttons, cancelButtonId, resources.getString("MemberNavigator.cancel.title"));
305   }
306
307   private void appendGroupingButton(Element JavaDoc buttons) {
308     if (isGrouping()) {
309       String JavaDoc label = resources.getString("MemberNavigator.disableGrouping.title");
310       Button.addButton(buttons, disableGroupingButtonId, label);
311     } else {
312       String JavaDoc label = resources.getString("MemberNavigator.enableGrouping.title");
313       Button.addButton(buttons, enableGroupingButtonId, label);
314     }
315   }
316
317   private void appendSelectButton(Element JavaDoc buttons) {
318     // in multiple selection mode append toggle-button "select all" / "select none"
319
if (getSelectionModel().getMode() == SelectionModel.MULTIPLE_SELECTION) {
320       if (getSelectionModel().isEmpty()) {
321         String JavaDoc label = resources.getString("MemberNavigator.selectVisible.title");
322         Button.addButton(buttons, selectVisibleButtonId, label);
323       } else {
324         String JavaDoc label = resources.getString("MemberNavigator.selectNone.title");
325         Button.addButton(buttons, selectNoneButtonId, label);
326       }
327     }
328     // in single selection mode optionally append "select none"
329
else if (showSelectNoneButton) {
330       String JavaDoc label = resources.getString("MemberNavigator.selectNone.title");
331       Button.addButton(buttons, selectNoneButtonId, label);
332     }
333   }
334
335   public static LabelProvider labelProvider = new DefaultLabelProvider() {
336     public String JavaDoc getLabel(Object JavaDoc node) {
337       if (node instanceof Displayable)
338         return ((Displayable) node).getLabel();
339       return super.getLabel(node);
340     }
341   };
342
343   private TreeModelAdapter.OverflowListener overflowListener = new TreeModelAdapter.OverflowListener() {
344     public void overflowOccured() {
345       setError(resources.getString("MemberNavigator.overflowOccurred"));
346     }
347   };
348
349   /**
350    * sets the hierarchies for the members to choose from. Sets a default title.
351    *
352    * @param hierarchies
353    * @param allowChangeOrder
354    */

355   public void setHierarchies(Hierarchy[] hierarchies, boolean allowChangeOrder) {
356     if (!isAvailable())
357       return;
358     HierarchyArray hierarchyArray = new HierarchyArray(hierarchies);
359     TreeModel model = (TreeModel) models.get(hierarchyArray);
360     if (model == null) {
361       // build the decorator chain
362
Locale JavaDoc locale = resources.getLocale();
363       MemberTree memberTree = (MemberTree) olapModel.getExtension(MemberTree.ID);
364       TreeModelAdapter modelAdapter = new TreeModelAdapter(hierarchies, memberTree, locale);
365       modelAdapter.setOverflowListener(overflowListener);
366
367       // the singleRecord level can not be opened because it contains
368
// too many children
369
for (int i = 0; i < hierarchies.length; i++) {
370         if (OlapUtils.isSingleRecord(hierarchies[i])) {
371           Level[] levels = hierarchies[i].getLevels();
372           modelAdapter.setNoChildrenLevel(levels[0]);
373         }
374       }
375
376       model = new CachingTreeModelDecorator(modelAdapter);
377       if (lazyFetchChildren)
378         model = new EnumBoundedTreeModelDecorator(model);
379
380       // insert virtual groups for Non-Measures hierarchies
381
if (hierarchies.length > 0 && !hierarchies[0].getDimension().isMeasure()) {
382         model = new GroupingTreeModelDecorator(labelProvider, model, initialGrouping ? groupingMemberCount : 0);
383       }
384
385       // let the user change node position
386
model = new MutableMemberTreeModelDecorator(model);
387
388       // create and store the tree
389
models.put(hierarchyArray, model);
390     }
391
392     super.setModel(model);
393     MutableTreeModelDecorator mutableModel = (MutableTreeModelDecorator) findModel(MutableTreeModelDecorator.class);
394     if (mutableModel != null) {
395       mutableModel.setEnableChangeOrder(allowChangeOrder);
396       super.setChangeOrderModel(mutableModel);
397     }
398     this.title = hierarchyArray.getLabel();
399   }
400
401   public void setHierarchies(Hierarchy[] hiers, boolean allowChangeOrder, MemberSelectionModel selection, Collection JavaDoc deleted) {
402     setHierarchies(hiers, allowChangeOrder);
403     if (lazyFetchChildren) {
404       EnumBoundedTreeModelDecorator boundedModel = (EnumBoundedTreeModelDecorator) findModel(EnumBoundedTreeModelDecorator.class);
405       boundedModel.setVisible(selection.getOrderedSelection());
406       super.setBounding(boundedModel);
407     }
408     setSelectionModel(selection);
409     if (expandSelected)
410       expandSelected(false);
411     Set JavaDoc set = getDeleteNodeModel().getDeleted();
412     set.clear();
413     set.addAll(deleted);
414   }
415   
416   /**
417    * sets the hierarchy of members to choose from. Sets a default title.
418    */

419   public void setHierarchy(Hierarchy hierarchy, boolean allowChangeOrder) {
420     setHierarchies(new Hierarchy[] { hierarchy}, allowChangeOrder);
421   }
422
423   /**
424    * Sets the title.
425    *
426    * @param title
427    * The title to set
428    */

429   public void setTitle(String JavaDoc title) {
430     this.title = title;
431   }
432
433   public void modelChanged(ModelChangeEvent e) {
434     for (Iterator JavaDoc it = models.values().iterator(); it.hasNext();) {
435       TreeModel model = (TreeModel) it.next();
436       TreeModelAdapter tma = (TreeModelAdapter) findModel(model, TreeModelAdapter.class);
437       tma.modelChanged();
438     }
439   }
440
441   public void structureChanged(ModelChangeEvent e) {
442     // clear all references
443
logger.info("cleaning up");
444     models.clear();
445     super.setModel(null);
446     super.getSelectionModel().clear();
447   }
448
449   /**
450    * shows "Select None" button in single selection mode
451    */

452   public boolean isShowSelectNoneButton() {
453     return showSelectNoneButton;
454   }
455
456   /**
457    * shows "Select None" button in single selection mode
458    */

459   public void setShowSelectNoneButton(boolean b) {
460     showSelectNoneButton = b;
461   }
462
463   /**
464    * Number of members of a non-selectable pseudo level
465    * @return Returns the groupingMemberCount.
466    */

467   public int getGroupingMemberCount() {
468     return groupingMemberCount;
469   }
470
471   /**
472    * Number of members of a non-selectable pseudo level
473    * @param groupingMemberCount The groupingMemberCount to set.
474    */

475   public void setGroupingMemberCount(int groupingMemberCount) {
476     this.groupingMemberCount = groupingMemberCount;
477   }
478
479   public boolean isGrouping() {
480     GroupingTreeModelDecorator groupingTreeModel = (GroupingTreeModelDecorator) findModel(GroupingTreeModelDecorator.class);
481     if (groupingTreeModel != null)
482       return groupingTreeModel.getLimit() > 0;
483     return false;
484   }
485
486   public void setGrouping(boolean b) {
487     GroupingTreeModelDecorator groupingTreeModel = (GroupingTreeModelDecorator) findModel(GroupingTreeModelDecorator.class);
488     if (groupingTreeModel != null)
489       groupingTreeModel.setLimit(b ? groupingMemberCount : 0);
490   }
491
492   private TreeModel findModel(Class JavaDoc clazz) {
493     return findModel(getModel(), clazz);
494   }
495
496   private TreeModel findModel(TreeModel tm, Class JavaDoc clazz) {
497     while (true) {
498       if (tm == null)
499         return null;
500       if (clazz.isAssignableFrom(tm.getClass()))
501         return tm;
502       if (!(tm instanceof DecoratedTreeModel))
503         return null;
504       tm = ((DecoratedTreeModel) tm).getDecoree();
505     }
506   }
507
508   /**
509    * returns true if the OlapModel supports all extensions that are required
510    * to use the MemberNavigator
511    */

512   public boolean isAvailable() {
513     return olapModel.getExtension(MemberTree.ID) != null;
514   }
515
516   public void setVisible(boolean b) {
517     if (!isAvailable())
518       b = false;
519     super.setVisible(b);
520   }
521
522   public boolean isExpandSelected() {
523     return expandSelected;
524   }
525
526   public void setExpandSelected(boolean expandSelected) {
527     this.expandSelected = expandSelected;
528   }
529
530   public boolean isLazyFetchChildren() {
531     return lazyFetchChildren;
532   }
533
534   public void setLazyFetchChildren(boolean lazyFetchChildren) {
535     this.lazyFetchChildren = lazyFetchChildren;
536   }
537
538
539 }
Popular Tags