KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > jpivot > table > TableComponent


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.table;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Collections JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.NoSuchElementException JavaDoc;
22
23 import javax.servlet.http.HttpSession JavaDoc;
24
25 import org.apache.log4j.Logger;
26 import org.w3c.dom.CDATASection JavaDoc;
27 import org.w3c.dom.Document JavaDoc;
28 import org.w3c.dom.Element JavaDoc;
29 import org.w3c.dom.Node JavaDoc;
30
31 import com.tonbeller.jpivot.core.ModelChangeEvent;
32 import com.tonbeller.jpivot.core.ModelChangeListener;
33 import com.tonbeller.jpivot.olap.model.Axis;
34 import com.tonbeller.jpivot.olap.model.Cell;
35 import com.tonbeller.jpivot.olap.model.Member;
36 import com.tonbeller.jpivot.olap.model.OlapModel;
37 import com.tonbeller.jpivot.olap.model.Position;
38 import com.tonbeller.jpivot.olap.model.Result;
39 import com.tonbeller.jpivot.olap.navi.ClickableExtension;
40 import com.tonbeller.jpivot.table.span.PropertyConfig;
41 import com.tonbeller.tbutils.res.Resources;
42 import com.tonbeller.wcf.component.Component;
43 import com.tonbeller.wcf.component.ComponentSupport;
44 import com.tonbeller.wcf.controller.RequestContext;
45 import com.tonbeller.wcf.controller.RequestListener;
46 import com.tonbeller.wcf.utils.XmlUtils;
47
48 /**
49  * Contains a reference to the olap data plus additional gui settings.
50  * Renders the pivot table
51  */

52 public class TableComponent extends ComponentSupport implements ModelChangeListener {
53   private static Logger logger = Logger.getLogger(TableComponent.class);
54
55   // configurable options from config.xml
56
CellBuilder cellBuilder;
57   CornerBuilder cornerBuilder;
58   SlicerBuilder slicerBuilder;
59   RowAxisBuilder rowAxisBuilder;
60   ColumnAxisBuilder columnAxisBuilder;
61   
62   Resources resources;
63   
64   // extensions, map for scripting, list for ordered initializing
65
List JavaDoc extensionList = new ArrayList JavaDoc();
66   Map JavaDoc extensionMap = new HashMap JavaDoc();
67
68   // initialized from tag
69
OlapModel olapModel;
70
71   Document JavaDoc document;
72
73   /** valid between startBuild() and stopBuild() */
74   Result result;
75   /** valid between startBuild() and stopBuild() */
76   Iterator JavaDoc cellIterator;
77   /** valid between startBuild() and stopBuild() */
78   int dimCount;
79   /** valid between startBuild() and stopBuild() */
80   Element JavaDoc rootElement;
81
82   public TableComponent(String JavaDoc id, Component parent) {
83     super(id, parent);
84   }
85
86   public TableComponent(String JavaDoc id, Component parent, OlapModel newOlapModel) {
87     super(id, parent);
88     logger.info("TableComponent");
89     this.olapModel = newOlapModel;
90     olapModel.addModelChangeListener(this);
91   }
92
93   /**
94    * for instantiation via reflection api.
95    * @see #setOlapModel
96    */

97   public TableComponent() {
98     super(null, null);
99   }
100
101   public void setOlapModel(OlapModel newOlapModel) {
102     logger.info("setOlapModel");
103     if (olapModel != null)
104       olapModel.removeModelChangeListener(this);
105     olapModel = newOlapModel;
106     olapModel.addModelChangeListener(this);
107   }
108
109   /**
110    * deferred ctor called once by the creating tag
111    */

112   public void initialize(RequestContext context) throws Exception JavaDoc {
113     logger.info("initialize");
114     super.initialize(context);
115     resources = context.getResources(TableComponent.class);
116     for (Iterator JavaDoc it = extensionList.iterator(); it.hasNext();)
117        ((TableComponentExtension) it.next()).initialize(context, this);
118     columnAxisBuilder.initialize(context, this);
119     rowAxisBuilder.initialize(context, this);
120     cellBuilder.initialize(context, this);
121     cornerBuilder.initialize(context, this);
122     slicerBuilder.initialize(context, this);
123   }
124
125   /**
126    * deferred ctor called once by the creating tag
127    */

128   public void destroy(HttpSession JavaDoc session) throws Exception JavaDoc {
129     logger.info("destroy");
130     slicerBuilder.destroy(session);
131     cornerBuilder.destroy(session);
132     cellBuilder.destroy(session);
133     rowAxisBuilder.destroy(session);
134     columnAxisBuilder.destroy(session);
135     for (Iterator JavaDoc it = extensionList.iterator(); it.hasNext();)
136        ((TableComponentExtension) it.next()).destroy(session);
137     super.destroy(session);
138   }
139
140   public Object JavaDoc getBookmarkState(int levelOfDetail) {
141     logger.info("getBookmarkState");
142     Map JavaDoc map = (Map JavaDoc) super.getBookmarkState(levelOfDetail);
143     map.put("slicerBuilder", slicerBuilder.getBookmarkState(levelOfDetail));
144     map.put("cornerBuilder", cornerBuilder.getBookmarkState(levelOfDetail));
145     map.put("cellBuilder", cellBuilder.getBookmarkState(levelOfDetail));
146     map.put("rowAxisBuilder", rowAxisBuilder.getBookmarkState(levelOfDetail));
147     map.put("columnAxisBuilder", columnAxisBuilder.getBookmarkState(levelOfDetail));
148     for (Iterator JavaDoc it = extensionList.iterator(); it.hasNext();) {
149       TableComponentExtension tce = (TableComponentExtension) it.next();
150       map.put(tce.getId(), tce.getBookmarkState(levelOfDetail));
151     }
152     return map;
153   }
154
155   public void setBookmarkState(Object JavaDoc state) {
156     if (state == null)
157       return;
158     logger.info("setBookmarkState");
159     super.setBookmarkState(state);
160     Map JavaDoc map = (Map JavaDoc) state;
161     slicerBuilder.setBookmarkState(map.get("slicerBuilder"));
162     cornerBuilder.setBookmarkState(map.get("cornerBuilder"));
163     cellBuilder.setBookmarkState(map.get("cellBuilder"));
164     rowAxisBuilder.setBookmarkState(map.get("rowAxisBuilder"));
165     columnAxisBuilder.setBookmarkState(map.get("columnAxisBuilder"));
166     for (Iterator JavaDoc it = extensionList.iterator(); it.hasNext();) {
167       TableComponentExtension tce = (TableComponentExtension) it.next();
168       tce.setBookmarkState(map.get(tce.getId()));
169     }
170   }
171   
172   public Iterator JavaDoc clickableIterator() {
173     ClickableExtension ce = (ClickableExtension) olapModel.getExtension(ClickableExtension.ID);
174     if (ce == null)
175       return Collections.EMPTY_LIST.iterator();
176     return ce.getClickables().iterator();
177   }
178
179   public void request(RequestContext context) throws Exception JavaDoc {
180     super.request(context);
181     for (Iterator JavaDoc it = clickableIterator(); it.hasNext();)
182       ((RequestListener)it.next()).request(context);
183   }
184   
185   /**
186    * notifies PartBuilders that a new DOM will be created
187    */

188   private void startBuild(RequestContext context) {
189     logger.info("enter startBuild");
190     columnAxisBuilder.startBuild(context);
191     rowAxisBuilder.startBuild(context);
192     cellBuilder.startBuild(context);
193     cornerBuilder.startBuild(context);
194     slicerBuilder.startBuild(context);
195     for (Iterator JavaDoc it = extensionList.iterator(); it.hasNext();)
196        ((TableComponentExtension) it.next()).startBuild(context);
197     for (Iterator JavaDoc it = clickableIterator(); it.hasNext();)
198       ((ClickableMember) it.next()).startRendering(context, this);
199     logger.info("leave startBuild");
200   }
201
202   /**
203    * notifies PartBuilders that a new DOM has been created
204    */

205   private void stopBuild() {
206     logger.info("enter stopBuild");
207     for (Iterator JavaDoc it = extensionList.iterator(); it.hasNext();)
208        ((TableComponentExtension) it.next()).stopBuild();
209     slicerBuilder.stopBuild();
210     cornerBuilder.stopBuild();
211     cellBuilder.stopBuild();
212     rowAxisBuilder.stopBuild();
213     columnAxisBuilder.stopBuild();
214     for (Iterator JavaDoc it = clickableIterator(); it.hasNext();)
215       ((ClickableMember) it.next()).stopRendering();
216     
217     // reduce memory usage
218
result = null;
219     cellIterator = null;
220     
221     logger.info("leave stopBuild");
222     
223   }
224
225   /**
226    * main entry point
227    */

228   public Document JavaDoc render(RequestContext context) throws Exception JavaDoc {
229     logger.info("render");
230     if (document == null) {
231       logger.info("creating document");
232       long t1 = System.currentTimeMillis();
233       document = XmlUtils.createDocument();
234       Element JavaDoc elem = render2(context);
235       document.appendChild(elem);
236       if (logger.isInfoEnabled()) {
237         long t2 = System.currentTimeMillis();
238         logger.info("Rendering of Table took " + (t2 - t1) + " millisec");
239       }
240     }
241     return document;
242   }
243   
244   protected Result updateOlapModel() throws Exception JavaDoc {
245     return olapModel.getResult();
246   }
247
248   private Element JavaDoc render2(RequestContext context) throws Exception JavaDoc {
249     logger.info("render2");
250     this.result = updateOlapModel();
251     this.cellIterator = result.getCells().iterator();
252     this.dimCount = result.getAxes().length;
253
254     rootElement = document.createElement("mdxtable");
255     Element JavaDoc head = append("head", rootElement);
256     Element JavaDoc body = append("body", rootElement);
257     
258     startBuild(context);
259
260     switch (dimCount) {
261       case 0 :
262         logger.info("0-dim data");
263         buildRows0Dim(body);
264         break;
265       case 1 :
266         logger.info("1-dim data");
267         buildColumns1Dim(head);
268         buildRows1Dim(body);
269         break;
270       case 2 :
271         logger.info("2-dim data");
272         buildColumns2Dim(head);
273         buildRows2Dim(body);
274         break;
275       default :
276         logger.error("more than 2 dimensions");
277         throw new IllegalArgumentException JavaDoc("TableRenderer requires 0, 1 or 2 dimensional result");
278     }
279
280     rootElement.appendChild(buildSlicer());
281
282     stopBuild();
283
284     return rootElement;
285   }
286
287   private Element JavaDoc buildSlicer() {
288     logger.info("buildSlicer");
289     Element JavaDoc slicer = elem("slicer");
290     Iterator JavaDoc pi = getResult().getSlicer().getPositions().iterator();
291     while (pi.hasNext()) {
292       Position p = (Position) pi.next();
293       Member[] members = p.getMembers();
294       for (int i = 0; i < members.length; i++) {
295         Element JavaDoc e = slicerBuilder.build(members[i]);
296         slicer.appendChild(e);
297       }
298     }
299     return slicer;
300   }
301
302   /* ---------------------- 0 dim ------------------------------- */
303
304   private void buildRows0Dim(Element JavaDoc parent) {
305     logger.info("buildRows0Dim");
306     // if result is empty, dont show anything
307
if (!cellIterator.hasNext())
308       return;
309     
310     Element JavaDoc row = append("row", parent);
311     Cell cell = (Cell) cellIterator.next();
312     Element JavaDoc cellElem = cellBuilder.build(cell, true);
313     row.appendChild(cellElem);
314   }
315
316   /* ---------------------- 1 dim ------------------------------- */
317
318   private void buildRows1Dim(Element JavaDoc parent) {
319     logger.info("buildRows1Dim");
320     Element JavaDoc row = append("row", parent);
321     buildCells(row, false);
322   }
323
324   private void buildColumns1Dim(Element JavaDoc parent) {
325     logger.info("buildColumns1Dim");
326     final int N = columnAxisBuilder.getRowCount();
327     for (int i = 0; i < N; i++) {
328       Element JavaDoc row = append("row", parent);
329       columnAxisBuilder.buildRow(row, i);
330     }
331   }
332
333   /* ---------------------- 2 dim ------------------------------- */
334
335   private void buildCornerElement(Element JavaDoc parent, int colSpan, int rowSpan) {
336     Element JavaDoc corner = cornerBuilder.build(colSpan, rowSpan);
337     parent.appendChild(corner);
338   }
339
340   /**
341    * <pre>
342    * C = column axis
343    * R = row axis
344    * H = row axis heading
345    * X = corner element
346    *
347    * Case 1 (C &lt; H), corner element on top of column axis
348    *
349    * H H H X X X
350    * H H H C C C
351    * R R R 1 2 3
352    * R R R 3 4 5
353    *
354    * Case 2 (C &gt; H), corner element in the left upper corner
355    *
356    * X X X C C C
357    * H H H C C C
358    * R R R 1 2 3
359    * R R R 3 4 5
360    *
361    * Case 3 (C == H), no corner element
362    *
363    * H H H C C C
364    * H H H C C C
365    * R R R 1 2 3
366    * R R R 3 4 5
367    * </pre>
368    */

369
370   private void buildColumns2Dim(Element JavaDoc parent) {
371     logger.info("enter buildColumns2Dim");
372     int colAxisCount = columnAxisBuilder.getRowCount();
373     int rowAxisCount = rowAxisBuilder.getHeaderRowCount();
374     int colAxisIndex = 0;
375     int rowAxisIndex = 0;
376     
377     if (logger.isInfoEnabled())
378       logger.info("colAxisCount = " + colAxisCount + ", rowAxisCount = " + rowAxisCount);
379
380     if (rowAxisCount > colAxisCount) {
381       logger.info("rowAxisCount > colAxisCount");
382       // case 1
383
int N = rowAxisCount - colAxisCount;
384       Element JavaDoc row = append("row", parent);
385       rowAxisBuilder.buildHeaderRow(row, rowAxisIndex++);
386       buildCornerElement(row, columnAxisBuilder.getColumnCount(), N);
387       for (int i = 1; i < N; i++) {
388         row = append("row", parent);
389         rowAxisBuilder.buildHeaderRow(row, rowAxisIndex++);
390       }
391       // number of rows left to add
392
rowAxisCount -= N;
393     } else if (colAxisCount > rowAxisCount) {
394       logger.info("colAxisCount > rowAxisCount");
395       // case 2
396
int N = colAxisCount - rowAxisCount;
397       Element JavaDoc row = append("row", parent);
398       buildCornerElement(row, rowAxisBuilder.getColumnCount(), N);
399       columnAxisBuilder.buildRow(row, colAxisIndex++);
400       for (int i = 1; i < N; i++) {
401         row = append("row", parent);
402         columnAxisBuilder.buildRow(row, colAxisIndex++);
403       }
404       // number of rows left to add
405
colAxisCount -= N;
406     }
407     
408     logger.info("building cells");
409     // case 3
410
// assert(colAxisCount == rowAxisCount)
411
for (int i = 0; i < colAxisCount; i++) {
412       Element JavaDoc row = append("row", parent);
413       rowAxisBuilder.buildHeaderRow(row, rowAxisIndex++);
414       columnAxisBuilder.buildRow(row, colAxisIndex++);
415     }
416     logger.info("leave buildColumns2Dim");
417   }
418
419   private void buildRows2Dim(Element JavaDoc parent) {
420     logger.info("enter buildRows2Dim");
421     final int N = rowAxisBuilder.getRowCount();
422     for (int i = 0; i < N; i++) {
423       boolean even = (i % 2 == 0);
424       Element JavaDoc row = append("row", parent);
425       rowAxisBuilder.buildRow(row, i);
426       buildCells(row, even);
427     }
428     logger.info("leave buildRows2Dim");
429   }
430
431   /* ---------------------- common ------------------------------- */
432
433   private void buildCells(Element JavaDoc row, boolean even) {
434     final int N = columnAxisBuilder.getColumnCount();
435     for (int i = 0; i < N; i++) {
436       try {
437         Cell cell = (Cell) cellIterator.next();
438         Element JavaDoc cellElem = cellBuilder.build(cell, even);
439         row.appendChild(cellElem);
440       } catch (NoSuchElementException JavaDoc e) {
441         logger.error("not enough cells", e);
442         e.printStackTrace();
443       }
444
445     }
446   }
447
448   /* ----------------------- utilities -------------------------- */
449
450   /**
451    * utility - creates an element with the given name
452    */

453   public Element JavaDoc elem(String JavaDoc name) {
454     return document.createElement(name);
455   }
456
457   /**
458    * utility - creates an element and appends it
459    */

460   public Element JavaDoc append(String JavaDoc name, Element JavaDoc parent) {
461     Element JavaDoc elem = document.createElement(name);
462     parent.appendChild(elem);
463     return elem;
464   }
465
466   private void firstChild(Element JavaDoc child, Element JavaDoc parent) {
467     Node JavaDoc before = parent.getFirstChild();
468     if (before != null)
469       parent.insertBefore(child, before);
470     else
471       parent.appendChild(child);
472   }
473
474   /**
475    * utility - creates an element an inserts it before the first child
476    */

477   public Element JavaDoc insert(String JavaDoc name, Element JavaDoc parent) {
478     Element JavaDoc elem = document.createElement(name);
479     firstChild(elem, parent);
480     return elem;
481   }
482
483   /**
484    * utility - creates a CDATA section
485    */

486   public Object JavaDoc cdata(String JavaDoc content, Element JavaDoc parent) {
487     CDATASection JavaDoc section = document.createCDATASection(content);
488     parent.appendChild(section);
489     return section;
490   }
491
492   /* ----------------------- properties -------------------------------- */
493
494   public OlapModel getOlapModel() {
495     return olapModel;
496   }
497
498   /**
499    * registers an extension. Used at creation time before initialize() is
500    * called
501    */

502   public void addExtension(TableComponentExtension extension) {
503     extensionList.add(extension);
504     extensionMap.put(extension.getId(), extension);
505   }
506
507   /**
508    * provides access to the extensions thru JSP scripting
509    */

510   public Map JavaDoc getExtensions() {
511     return extensionMap;
512   }
513
514   /**
515    * true means that render() will create a new DOM
516    */

517   public boolean isDirty() {
518     return document == null;
519   }
520
521   public void setDirty(boolean dirty) {
522     document = null;
523     // avoid memory leak
524
result = null;
525     cellIterator = null;
526     rootElement = null;
527   }
528
529   public void modelChanged(ModelChangeEvent e) {
530     setDirty(true);
531   }
532
533   public void structureChanged(ModelChangeEvent e) {
534     setDirty(true);
535   }
536
537   /**
538    * Returns the cellBuilder.
539    * @return CellBuilder
540    */

541   public CellBuilder getCellBuilder() {
542     return cellBuilder;
543   }
544
545   /**
546    * Returns the columnAxisBuilder.
547    * @return ColumnAxisBuilder
548    */

549   public ColumnAxisBuilder getColumnAxisBuilder() {
550     return columnAxisBuilder;
551   }
552
553   /**
554    * Returns the cornerBuilder.
555    * @return CornerBuilder
556    */

557   public CornerBuilder getCornerBuilder() {
558     return cornerBuilder;
559   }
560
561   /**
562    * Returns the rowAxisBuilder.
563    * @return RowAxisBuilder
564    */

565   public RowAxisBuilder getRowAxisBuilder() {
566     return rowAxisBuilder;
567   }
568
569   /**
570    * Returns the slicerBuilder.
571    * @return SlicerBuilder
572    */

573   public SlicerBuilder getSlicerBuilder() {
574     return slicerBuilder;
575   }
576
577   /**
578    * Sets the cellBuilder.
579    * @param cellBuilder The cellBuilder to set
580    */

581   public void setCellBuilder(CellBuilder cellBuilder) {
582     this.cellBuilder = cellBuilder;
583   }
584
585   /**
586    * Sets the columnAxisBuilder.
587    * @param columnAxisBuilder The columnAxisBuilder to set
588    */

589   public void setColumnAxisBuilder(ColumnAxisBuilder columnAxisBuilder) {
590     this.columnAxisBuilder = columnAxisBuilder;
591   }
592
593   /**
594    * Sets the cornerBuilder.
595    * @param cornerBuilder The cornerBuilder to set
596    */

597   public void setCornerBuilder(CornerBuilder cornerBuilder) {
598     this.cornerBuilder = cornerBuilder;
599   }
600
601   /**
602    * Sets the rowAxisBuilder.
603    * @param rowAxisBuilder The rowAxisBuilder to set
604    */

605   public void setRowAxisBuilder(RowAxisBuilder rowAxisBuilder) {
606     this.rowAxisBuilder = rowAxisBuilder;
607   }
608
609   /**
610    * Sets the slicerBuilder.
611    * @param slicerBuilder The slicerBuilder to set
612    */

613   public void setSlicerBuilder(SlicerBuilder slicerBuilder) {
614     this.slicerBuilder = slicerBuilder;
615   }
616
617   /**
618    * returns the current result
619    */

620   public Result getResult() {
621     return result;
622   }
623
624   /**
625    * returns the dimension count of the current result
626    */

627   public int getDimCount() {
628     return dimCount;
629   }
630
631   /**
632    * returns the root DOM element that is rendered
633    */

634   public Element JavaDoc getRootElement() {
635     return rootElement;
636   }
637
638   /**
639    * returns the row axis or null
640    */

641   public Axis getRowAxis() {
642     if (dimCount < 2)
643       return null;
644     return result.getAxes()[1];
645   }
646
647   /**
648    * returns the column axis or null
649    */

650   public Axis getColumnAxis() {
651     if (dimCount < 1)
652       return null;
653     return result.getAxes()[0];
654   }
655
656   /**
657    * returns the property config object that allows
658    * to adjust visible properties
659    */

660   public PropertyConfig getPropertyConfig() {
661     return rowAxisBuilder.getAxisConfig().getPropertyConfig();
662   }
663
664 }
665
Popular Tags