KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > jpivot > table > span > SpanCalc


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.span;
14
15 import java.util.Iterator JavaDoc;
16
17 import org.apache.log4j.Logger;
18
19 import com.tonbeller.jpivot.olap.model.Axis;
20 import com.tonbeller.jpivot.olap.model.Member;
21 import com.tonbeller.jpivot.olap.model.Position;
22
23 /**
24  * Calculates table spans for an axis. An axis is seen as a matrix of
25  * positions and hierarchies. For a row-axis, the positions are the rows, the hierarchies
26  * are the columns, for a column-axis its vice versa.
27  * <p>
28  * The cells of the matrix are <code>Span</code> instances.
29  *
30  * @author av
31  */

32 public class SpanCalc {
33   private static final Logger logger = Logger.getLogger(SpanCalc.class);
34   
35   int positionCount, hierarchyCount;
36   // index order is spans[positionIndex][hierarchyIndex]
37
Span[][] spans;
38
39   // true when spans have been calculated
40
boolean initialized = false;
41
42   SpanConfig config = new NoSpanConfig();
43
44   /**
45    * creates an instance
46    */

47   public SpanCalc(Span[][] spans) {
48     this.spans = spans;
49   }
50
51   /**
52    * creates an instance from an axis
53    */

54   public SpanCalc(Axis axis) {
55     positionCount = axis.getPositions().size();
56     if (positionCount > 0)
57       hierarchyCount = ((Position) axis.getPositions().get(0)).getMembers().length;
58     else
59       hierarchyCount = 0;
60     if (logger.isInfoEnabled())
61       logger.info("creating SpanCalc, positionCount = " + positionCount + ", hierarchyCount = " + hierarchyCount);
62     spans = new Span[positionCount][];
63     for (int i = 0; i < positionCount; i++)
64       spans[i] = new Span[hierarchyCount];
65
66     Iterator JavaDoc it = axis.getPositions().iterator();
67     for (int posIndex = 0; posIndex < positionCount; posIndex++) {
68       Position p = (Position) it.next();
69       createSpansFromAxis(axis, p, posIndex, spans[posIndex]);
70     }
71   }
72
73   void createSpansFromAxis(Axis axis, Position position, int posIndex, Span[] spans) {
74     if (logger.isDebugEnabled())
75       logger.debug("creating Span for position " + posIndex);
76     Member[] members = position.getMembers();
77     for (int hierIndex = 0; hierIndex < hierarchyCount; hierIndex++) {
78       Member member = members[hierIndex];
79       spans[hierIndex] = new Span(axis, position, member);
80     }
81   }
82
83   void initialize() {
84     if (!initialized) {
85       positionCount = spans.length;
86       if (positionCount > 0)
87         hierarchyCount = spans[0].length;
88       else
89         hierarchyCount = 0;
90       initSpans();
91       calcSpans();
92       calcIndent();
93       initialized = true;
94     }
95   }
96
97   /**
98    * initializes the spans to 1 column and 1 row.
99    */

100   void initSpans() {
101     for (int posIndex = 0; posIndex < positionCount; posIndex++) {
102       for (int hierIndex = 0; hierIndex < hierarchyCount; hierIndex++) {
103         spans[posIndex][hierIndex].initialize(posIndex, hierIndex);
104       }
105     }
106   }
107
108   /**
109    * flag indicating that a break is forced. Example row-axis:
110    * <pre>
111    * Product 1 | Revenue
112    * Product 2 | Revenue
113    * </pre>
114    * The revenues shall not be combined to a single span because a break occured
115    * at higher level.
116    */

117   boolean[][] forcePositionBreak;
118
119   void calcSpans() {
120     logger.info("calcSpans");
121     forcePositionBreak = new boolean[positionCount][hierarchyCount];
122
123     for (int hierIndex = 0; hierIndex < hierarchyCount; hierIndex++) {
124       for (int posIndex = 0; posIndex < positionCount; posIndex++) {
125         Span span = spans[posIndex][hierIndex];
126
127         // if the span is already part of another, continue
128
if (!span.isSignificant())
129           continue;
130
131         int dir = config.chooseSpanDirection(span);
132
133         if (dir == SpanConfig.HIERARCHY_SPAN) {
134           makeHierSpan(span, 1);
135           addForcePositionBreak(span);
136         } else if (dir == SpanConfig.POSITION_SPAN) {
137           makePosSpan(span, 1);
138           addForcePositionBreak(span);
139         } else if (dir == SpanConfig.HIERARCHY_THEN_POSITION_SPAN) {
140           int count = makeHierSpan(span, 1);
141           makePosSpan(span, count);
142           addForcePositionBreak(span);
143         } else if (dir == SpanConfig.POSITION_THEN_HIERARCHY_SPAN) {
144           int count = makePosSpan(span, 1);
145           makeHierSpan(span, count);
146           addForcePositionBreak(span);
147         }
148
149         // else do nothing because spans are initialized to 1/1 row/col
150
}
151     }
152   }
153
154   /** returns the number of hierarchy spans created */
155   int makeHierSpan(Span span, int posSpans) {
156     logger.debug("makeHierSpan");
157     int pi = span.positionIndex;
158     int spanCount = 1;
159     loop : for (int hi = span.hierarchyIndex + 1; hi < hierarchyCount; hi++) {
160       // check if all positions at hierarchy hi level are equal
161
boolean equal = true;
162       for (int i = 0; i < posSpans; i++) {
163         Span s = spans[pi + i][hi];
164         equal = equal && config.equals(span, s);
165       }
166
167       // add another row of spans
168
if (equal) {
169         span.hierarchySpan += 1;
170         spanCount += 1;
171         for (int i = 0; i < posSpans; i++) {
172           Span s = spans[pi + i][hi];
173           s.significant = false;
174           s.hierarchySpan = s.positionSpan = 0;
175         }
176       } else
177         break loop;
178     }
179     return spanCount;
180   }
181
182   /** returns the number of position spans created */
183   int makePosSpan(Span span, int hierSpans) {
184     logger.debug("makePosSpan");
185     int hi = span.hierarchyIndex;
186     int spanCount = 1;
187     loop : for (int pi = span.positionIndex + 1; pi < positionCount; pi++) {
188       // artificial break?
189
if (forcePositionBreak[pi][hi])
190         break loop;
191
192       // check if all hierarchies at position pi are equal
193
boolean equal = true;
194       for (int i = 0; i < hierSpans; i++) {
195         Span s = spans[pi][hi + i];
196         equal = equal && config.equals(span, s);
197       }
198
199       // add another row of spans
200
if (equal) {
201         span.positionSpan += 1;
202         spanCount += 1;
203         for (int i = 0; i < hierSpans; i++) {
204           Span s = spans[pi][hi + i];
205           s.significant = false;
206           s.hierarchySpan = s.positionSpan = 0;
207         }
208       } else
209         break loop;
210     }
211     return spanCount;
212   }
213
214   void addForcePositionBreak(Span span) {
215     // spans[pi][hi] == last element of span
216
int pi = span.positionIndex + span.positionSpan;
217     int hi = span.hierarchyIndex + span.hierarchySpan;
218     for (; pi < positionCount && hi < hierarchyCount; hi++)
219       forcePositionBreak[pi][hi] = true;
220   }
221
222   /* --------------------------------------------------------------------- */
223
224   /**
225    * creates a SpanCalc for row axis headers. Searches all positions
226    * for a "significant" span that is used to create the header via
227    * <code>shf</code>. Example: the row axis
228    * <pre>
229    * a a
230    * a b
231    * </pre>
232    * will get the header (upper case letters)
233    * <pre>
234    * A B
235    * ---
236    * a a
237    * a b
238    * </pre>
239    * because the second <code>a</code> is not significant. Here <code>A</code>
240    * is created by <code>shf.create(a)</code>, and <code>B = shf.create(b)</code>.
241    */

242   public SpanCalc createPositionHeader(SpanHeaderFactory shf) {
243     logger.info("createPositionHeader");
244     if (!initialized)
245       initialize();
246
247     if (hierarchyCount == 0 || positionCount == 0)
248       return null;
249
250     Span[][] header = new Span[1][hierarchyCount];
251
252     header[0][0] = shf.create(spans[0][0]);
253
254     for (int hi = 1; hi < hierarchyCount; hi++) {
255       int pi;
256       inner : for (pi = 0; pi < positionCount; pi++) {
257         Span curSpan = spans[pi][hi];
258         Span prevSpan = spans[pi][hi - 1];
259         if (!config.equals(prevSpan, curSpan)) {
260           prevSpan = curSpan;
261           header[0][hi] = shf.create(curSpan);
262           break inner;
263         }
264       }
265       if (pi == positionCount) {
266         // throw new IllegalArgumentException("no header found");
267
// create a non-significant header
268
header[0][hi] = shf.create(spans[0][hi]);
269       }
270     }
271
272     return new SpanCalc(header);
273   }
274
275   /* --------------------------------------------------------------------- */
276
277   public void addHierarchyHeader(SpanHeaderFactory shf, boolean removeDuplicates) {
278     logger.info("addHierarchyHeader");
279     boolean[] keep = new boolean[hierarchyCount * 2];
280     createHeaderSpans(shf, keep);
281     int newHierarchyCount = 0;
282     for (int i = 0; i < keep.length; i++)
283       if (keep[i])
284         newHierarchyCount += 1;
285     removeDuplicateHeaders(keep, newHierarchyCount);
286     initialized = false;
287   }
288
289   void removeDuplicateHeaders(boolean[] keep, int newHierarchyCount) {
290     logger.info("removeDuplicateHeaders");
291     for (int posIndex = 0; posIndex < positionCount; posIndex++) {
292       Span[] oldSpans = spans[posIndex];
293       Span[] newSpans = new Span[newHierarchyCount];
294       int newHierIndex = 0;
295       for (int oldHierIndex = 0; oldHierIndex < oldSpans.length; oldHierIndex++) {
296         if (keep[oldHierIndex]) {
297           Span span = oldSpans[oldHierIndex];
298           newSpans[newHierIndex++] = span;
299         }
300       }
301       spans[posIndex] = newSpans;
302     }
303   }
304
305   /**
306    * doubles the number of spans per hierarchy (i.e. hierarchyCount).
307    * Adds either a header span or the original span to every hierarchyIndex.
308    * A header is added if its different from the previous one.
309    */

310   void createHeaderSpans(SpanHeaderFactory shf, boolean[] keep) {
311     logger.info("createHeaderSpans");
312     for (int posIndex = 0; posIndex < positionCount; posIndex++) {
313       Span[] newSpans = new Span[hierarchyCount * 2];
314       int newIndex = 0;
315       Span prevHeaderSpan = null;
316       for (int hierIndex = 0; hierIndex < hierarchyCount; hierIndex++) {
317         Span span = spans[posIndex][hierIndex];
318         // create a header span
319
Span curHeaderSpan = shf.create(span);
320         if (prevHeaderSpan == null || !config.equals(prevHeaderSpan, curHeaderSpan)) {
321           keep[newIndex] = true;
322           newSpans[newIndex++] = curHeaderSpan;
323           prevHeaderSpan = curHeaderSpan;
324         } else {
325           // we dont have to keep this one
326
newSpans[newIndex++] = (Span) span.clone();
327         }
328         // copy the original span
329
keep[newIndex] = true;
330         newSpans[newIndex++] = span;
331       }
332       spans[posIndex] = newSpans;
333     }
334   }
335
336   /* --------------------------------------------------------------------- */
337
338   /**
339    * return span info for the element at positionIndex, hierarchyIndex.
340    * @param positionIndex - index for axis.getPositions()
341    * @param hierarchyIndex - index for axis.getPositions().getMembers()
342    * @return the Span info if for (positionIndex, hierarchyIndex) needs
343    * a &lt;td&gt; to be generated, returns null otherwise. The return value
344    * is non-null, if (positionIndex, hierarchyIndex) are minimal for this
345    * cell.
346    */

347   public Span getSpan(int positionIndex, int hierarchyIndex) {
348     if (!initialized)
349       initialize();
350     return spans[positionIndex][hierarchyIndex];
351   }
352
353   /**
354    * Returns the hierarchyCount.
355    * @return int
356    */

357   public int getHierarchyCount() {
358     if (!initialized)
359       initialize();
360     return hierarchyCount;
361   }
362
363   /**
364    * Returns the positionCount.
365    * @return int
366    */

367   public int getPositionCount() {
368     if (!initialized)
369       initialize();
370     return positionCount;
371   }
372
373   /**
374    * Returns the config.
375    * @return SpanConfig
376    */

377   public SpanConfig getConfig() {
378     return config;
379   }
380
381   /**
382    * Sets the config.
383    * @param config The config to set
384    */

385   public void setConfig(SpanConfig config) {
386     initialized = false;
387     this.config = config;
388   }
389
390   /**
391    * returns a matrix of spans[positionIndex][hierarchyIndex] for faster access.
392    */

393   public Span[][] getSpans() {
394     return spans;
395   }
396
397   /**
398    * set the matrix of spans[positionIndex][hierarchyIndex] for faster access.
399    */

400   public void setSpans(Span[][] spans) {
401     this.spans = spans;
402     initialized = false;
403   }
404
405   /**
406    * sets the indent attribute of all spans
407    */

408   void calcIndent() {
409     logger.info("calcIndent");
410     for (int hi = 0; hi < hierarchyCount; hi++) {
411
412       // find minimal root distance for this hierIndex
413
int minRootDistance = Integer.MAX_VALUE;
414       for (int pi = 0; pi < positionCount; pi++) {
415         Span s = spans[pi][hi];
416         if (s.isMember()) {
417           Member m = s.getMember();
418           if (m.getRootDistance() < minRootDistance)
419             minRootDistance = m.getRootDistance();
420         }
421       }
422
423       // set the indent for this hierIndex
424
for (int pi = 0; pi < positionCount; pi++) {
425         Span s = spans[pi][hi];
426         if (s.isMember()) {
427           Member m = s.getMember();
428           s.setIndent(m.getRootDistance() - minRootDistance);
429         } else
430           s.setIndent(0);
431       }
432     }
433   }
434
435   /* -------------------------------------------------------------------- */
436
437   public static SpanCalc appendBelow(SpanCalc above, SpanCalc below) {
438     logger.info("appendBelow");
439     if (above == null)
440       return below;
441     if (below == null)
442       return above;
443     if (above.getHierarchyCount() != below.getHierarchyCount())
444       throw new IllegalArgumentException JavaDoc("sizes dont match");
445     final int HI = above.getHierarchyCount();
446     Span[][] a = above.spans;
447     Span[][] b = below.spans;
448     Span[][] s = new Span[a.length + b.length][];
449     for (int pi = 0; pi < a.length; pi++) {
450       s[pi] = new Span[HI];
451       for (int hi = 0; hi < HI; hi++)
452         s[pi][hi] = a[pi][hi];
453     }
454     for (int pi = 0; pi < b.length; pi++) {
455       s[pi + a.length] = new Span[HI];
456       for (int hi = 0; hi < HI; hi++)
457         s[pi + a.length][hi] = b[pi][hi];
458     }
459     return new SpanCalc(s);
460   }
461
462 }
463
Popular Tags