KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openi > xmla > DatasetAdapter


1 /*********************************************************************************
2  * The contents of this file are subject to the OpenI Public License Version 1.0
3  * ("License"); You may not use this file except in compliance with the
4  * License. You may obtain a copy of the License at
5  * http://www.openi.org/docs/LICENSE.txt
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is: OpenI Open Source
12  *
13  * The Initial Developer of the Original Code is Loyalty Matrix, Inc.
14  * Portions created by Loyalty Matrix, Inc. are
15  * Copyright (C) 2005 Loyalty Matrix, Inc.; All Rights Reserved.
16  *
17  * Contributor(s): ______________________________________.
18  *
19  ********************************************************************************/

20 package org.openi.xmla;
21
22 import com.tonbeller.jpivot.olap.model.Cell;
23 import com.tonbeller.jpivot.olap.model.Member;
24 import com.tonbeller.jpivot.olap.model.OlapException;
25 import com.tonbeller.jpivot.olap.model.OlapModel;
26 import com.tonbeller.jpivot.olap.model.Position;
27 import com.tonbeller.jpivot.olap.navi.MemberTree;
28
29 import org.apache.log4j.Logger;
30
31 import org.jfree.data.category.DefaultCategoryDataset;
32 import org.jfree.data.time.Day;
33 import org.jfree.data.time.Month;
34 import org.jfree.data.time.Quarter;
35 import org.jfree.data.time.RegularTimePeriod;
36 import org.jfree.data.time.TimeSeries;
37 import org.jfree.data.time.TimeSeriesCollection;
38 import org.jfree.data.xy.XYDataset;
39
40 import java.text.DecimalFormat JavaDoc;
41 import java.text.DecimalFormatSymbols JavaDoc;
42 import java.text.NumberFormat JavaDoc;
43 import java.text.ParseException JavaDoc;
44 import java.text.SimpleDateFormat JavaDoc;
45
46 import java.util.Calendar JavaDoc;
47 import java.util.Date JavaDoc;
48 import java.util.HashMap JavaDoc;
49 import java.util.List JavaDoc;
50 import java.util.Locale JavaDoc;
51
52
53 /**
54  * Takes an olap model, adapts it to a jfreechart dataset
55  */

56 public class DatasetAdapter {
57     private static Logger logger = Logger.getLogger(DatasetAdapter.class);
58     private Locale JavaDoc locale;
59     private NumberFormat JavaDoc numberFormatter;
60
61     /**
62      * @param locale
63      */

64     public DatasetAdapter(Locale JavaDoc locale) {
65         this.locale = locale;
66     }
67
68     /**
69      * @param dataset
70      * @return
71      */

72     public DefaultCategoryDataset buildCategoryDataset(OlapModel olapModel)
73         throws OlapException {
74         long start = System.currentTimeMillis();
75
76         DefaultCategoryDataset dataset = null;
77         int dimCount = olapModel.getResult().getAxes().length;
78
79         switch (dimCount) {
80         case 1:
81             logger.info("1-dim data");
82             dataset = build1dimDataset(olapModel);
83
84             break;
85
86         case 2:
87             logger.info("2-dim data");
88             dataset = build2dimDataset(olapModel);
89
90             break;
91
92         default:
93             logger.error("less than 1 or more than 2 dimensions");
94             throw new IllegalArgumentException JavaDoc(
95                 "ChartRenderer requires a 1 or 2 dimensional result");
96         }
97
98         logger.debug("built datset in: " +
99             (System.currentTimeMillis() - start) + "ms");
100
101         return dataset;
102     }
103
104     /**
105      * Build a jfreechart CategoryDataset with a single series
106      * @param result TODO
107      *
108      */

109     private DefaultCategoryDataset build1dimDataset(OlapModel olapModel)
110         throws OlapException {
111         DefaultCategoryDataset dataset = new DefaultCategoryDataset();
112
113         // column axis
114
List JavaDoc columnPositions = olapModel.getResult().getAxes()[0].getPositions();
115         int colCount = columnPositions.size();
116
117         // cells
118
List JavaDoc cells = olapModel.getResult().getCells();
119
120         String JavaDoc series = "Series";
121
122         // loop on column positions
123
for (int i = 0; i < colCount; i++) {
124             Member[] colMembers = ((Position) columnPositions.get(i)).getMembers();
125
126             StringBuffer JavaDoc key = new StringBuffer JavaDoc();
127
128             // loop on col position members
129
for (int j = 0; j < colMembers.length; j++) {
130                 // build up composite name for this row
131
key.append(colMembers[j].getLabel() + ".");
132             }
133
134             dataset.addValue(getNumberValue((Cell) cells.get(i)), series,
135                 key.toString());
136         }
137
138         return dataset;
139     }
140
141     /**
142      * Build a jfreechart CategoryDataset with multiple series
143      * @param olapModel
144      * @param result TODO
145      *
146      */

147     private DefaultCategoryDataset build2dimDataset(OlapModel olapModel)
148         throws OlapException {
149         DefaultCategoryDataset dataset = new DefaultCategoryDataset();
150
151         // column axis
152
List JavaDoc columnPositions = olapModel.getResult().getAxes()[0].getPositions(); //ladX.getPositions();
153
int colCount = columnPositions.size();
154
155         // row axis
156
List JavaDoc rowPositions = olapModel.getResult().getAxes()[1].getPositions(); //ladY.getPositions();
157
int rowCount = rowPositions.size();
158         List JavaDoc cells = olapModel.getResult().getCells();
159
160         // get the full member tree
161
MemberTree myTree = ((MemberTree) olapModel.getExtension(MemberTree.ID));
162
163         // for each column, starting with the bottom member, progress up the mmeber chain until the root is reached
164
// keep track of the levels and hierarchies to avoid duplicates on level or hierarchys.
165
// *note: keeping track of the levels might be just extra work, I don't know if they CAN be repeated.
166
// if not, that logic can be easily removed (see buildName - above)
167
// For each hierarchy, If a root member is reached (getRootDistance()=0), then only include it if there have been no other
168
// lower level members already added:
169
// ie. All_dim1.dim1_lvl1.dim1_lvl2.All_dim2.dim2_lvl1 renders as dim1_lvl1.dim1_lvl2.dim2_lvl1
170
// whereas All_dim1.All_dim2 renders as the same.
171
// The important part is that we include each parent on the way up, to ensure a unique name to
172
// place in the map for the dataset (no longer overwriting each other)
173

174         for (int i = 0; i < colCount; i++) {
175             Position p = (Position) columnPositions.get(i);
176             Member[] colMembers = p.getMembers();
177
178             // build the label name for this column
179
String JavaDoc label = buildName(myTree, colMembers);
180
181             // For each row, use the same logic to build a unique key for each data item
182
for (int k = 0; k < rowCount; k++) {
183                 Position rp = (Position) rowPositions.get(k);
184                 Member[] rowMembers = rp.getMembers();
185
186                 // build key name
187
String JavaDoc key = buildName(myTree, rowMembers);
188
189                 Cell cell = (Cell) cells.get((k * colCount) + i);
190
191                 dataset.addValue(getNumberValue(cell), label.toString(),
192                     key.toString());
193             }
194         }
195
196         return dataset;
197     }
198
199     /**
200      * Get cell value as a Number. Parses the cell value string
201      * using the locale set in this.locale.
202      * @param cell
203      * @return value as Number (can be null)
204      */

205     private Number JavaDoc getNumberValue(Cell cell) {
206         //**** HACK AR 2004.01.10
207
//String value = cell.getFormattedValue();
208
Object JavaDoc value = cell.getValue();
209
210         //added to fix data format bug in range axis
211
if ((cell.getFormattedValue() != null) &&
212                 (cell.getFormattedValue() != "")) {
213             String JavaDoc fmtValue = cell.getFormattedValue();
214             fmtValue = fmtValue.trim();
215
216             if(numberFormatter == null){
217                     DecimalFormatSymbols JavaDoc dfs = new DecimalFormatSymbols JavaDoc(this.locale);
218                 if(fmtValue.endsWith(String.valueOf(dfs.getPercent()))){
219                     numberFormatter = NumberFormat.getPercentInstance(this.locale);
220                     // numberFormatter.setMaximumFractionDigits(0);
221
// numberFormatter.setMinimumFractionDigits(0);
222
}
223                 else if(fmtValue.startsWith(dfs.getCurrencySymbol())){
224                     numberFormatter = NumberFormat.getCurrencyInstance(this.locale);
225                     numberFormatter.setMaximumFractionDigits(0);
226                     numberFormatter.setMinimumFractionDigits(0);
227                 }
228                 
229                 
230             }
231
232         }
233
234         ////////////////
235
NumberFormat JavaDoc formatter = NumberFormat.getInstance(this.locale);
236
237         // formatter.setDecimalFormatSymbols(dfs);
238
Number JavaDoc number = null;
239
240         try {
241             // need this formatter so that we can properly render jfreecharts
242
number = formatter.parse(String.valueOf(value));
243         } catch (Exception JavaDoc e) {
244             // logger.debug(e);
245
number = null;
246         }
247
248         return number;
249     }
250
251     /**
252      * Get a unique name string for a dataitem derived from the member chain
253      *
254      * @param myTree (full member tree)
255      * @param members - the list to be processed (either X/Y axis)
256      * @return retValue as String
257      */

258     private String JavaDoc buildName(MemberTree myTree, Member[] members) {
259         String JavaDoc retValue = new String JavaDoc();
260         HashMap JavaDoc levelMap = new HashMap JavaDoc();
261         HashMap JavaDoc hierarchyMap = new HashMap JavaDoc();
262
263         for (int j = members.length - 1; j >= 0; j--) {
264             Member member = members[j];
265
266             while (member != null) {
267                 // only process if no other items from this level processed - should not be duplicates!
268
if (!levelMap.containsValue(member.getLevel())) {
269                     levelMap.put(member.getLevel().toString(), member.getLevel());
270
271                     if (member.getRootDistance() == 0) {
272                         // if root member, only add to name if no other members of the hierarchy are already added
273
if (!hierarchyMap.containsValue(
274                                     member.getLevel().getHierarchy()) ||
275                                 (myTree.getRootMembers(
276                                     member.getLevel().getHierarchy()).length > 1)) {
277                             hierarchyMap.put(member.getLevel().getHierarchy()
278                                                    .toString(),
279                                 member.getLevel().getHierarchy());
280                             retValue = member.getLabel() + "." + retValue;
281                         }
282                     } else {
283                         hierarchyMap.put(member.getLevel().getHierarchy()
284                                                .toString(),
285                             member.getLevel().getHierarchy());
286                         retValue = member.getLabel() + "." + retValue;
287                     }
288                 }
289
290                 member = myTree.getParent(member);
291             }
292         }
293
294         return retValue;
295     }
296
297     /**
298      * Very experimental, notice that the time dimension needs to be on the rows,
299      * and there can be no other dimensions on rows.
300      *
301      * Each dimension on the column will have it's own series, added to the TimeSeriesCollection
302      */

303     public XYDataset buildXYDataset(OlapModel olapModel)
304         throws OlapException {
305         long start = System.currentTimeMillis();
306
307         TimeSeriesCollection dataset = new TimeSeriesCollection();
308
309         // column axis
310
List JavaDoc columnPositions = olapModel.getResult().getAxes()[0].getPositions(); //ladX.getPositions();
311
int colCount = columnPositions.size();
312
313         // row axis
314
List JavaDoc rowPositions = olapModel.getResult().getAxes()[1].getPositions(); //ladY.getPositions();
315
int rowCount = rowPositions.size();
316         List JavaDoc cells = olapModel.getResult().getCells();
317
318         // get the full member tree
319
MemberTree myTree = ((MemberTree) olapModel.getExtension(MemberTree.ID));
320
321         // each column gets its own time series
322
for (int i = 0; i < colCount; i++) {
323             Position p = (Position) columnPositions.get(i);
324             Member[] colMembers = p.getMembers();
325
326             // build the label name for this column
327
String JavaDoc label = buildName(myTree, colMembers);
328             TimeSeries series = createTimeSeries(label,
329                     parseRootLevel(rowPositions));
330
331             for (int k = 0; k < rowCount; k++) {
332                 Position rp = (Position) rowPositions.get(k);
333                 Member[] rowMembers = rp.getMembers();
334                 Cell cell = (Cell) cells.get((k * colCount) + i);
335
336                 try {
337                     // TODO:
338
// should determine if this is a month, or day, year
339
// similar to buildName, need a buildMonth and/or buildDate,
340
// allowing date hierarchies.
341
RegularTimePeriod current = null;
342
343                     if (series.getTimePeriodClass() == Quarter.class) {
344                         current = createQuarter(myTree, rowMembers);
345                     } else if (series.getTimePeriodClass() == Month.class) {
346                         current = createMonth(myTree, rowMembers);
347                     } else if (series.getTimePeriodClass() == Day.class) {
348                         current = createDay(myTree, rowMembers);
349                     }
350
351                     series.add(current, getNumberValue(cell));
352                 } catch (ParseException JavaDoc e) {
353                     // logger.error(e);
354
}
355             }
356
357             dataset.addSeries(series);
358         }
359
360         dataset.setDomainIsPointsInTime(true);
361         logger.debug("created XY Dataset in: " +
362             (System.currentTimeMillis() - start) + " ms");
363
364         return dataset;
365     }
366
367     private TimeSeries createTimeSeries(String JavaDoc label, String JavaDoc rootLevel) {
368         logger.debug("creating timeseries for: " + rootLevel);
369
370         TimeSeries series = null;
371
372         if ("quarter".equalsIgnoreCase(rootLevel)) {
373             series = new TimeSeries(label, Quarter.class);
374         } else if ("month".equalsIgnoreCase(rootLevel)) {
375             series = new TimeSeries(label, Month.class);
376         } else if ("day".equalsIgnoreCase(rootLevel)) {
377             series = new TimeSeries(label, Day.class);
378         }
379
380         return series;
381     }
382
383     private RegularTimePeriod createQuarter(MemberTree memberTree,
384         Member[] members) {
385         String JavaDoc year = memberTree.getParent(members[0]).getLabel();
386         int quarter = 1;
387         String JavaDoc memberLabel = members[0].getLabel();
388
389         // month = parseMonth(memberLabel);
390
if ("Q1".equalsIgnoreCase(memberLabel)) {
391             quarter = 1;
392         } else if ("Q2".equalsIgnoreCase(memberLabel)) {
393             quarter = 2;
394         } else if ("Q3".equalsIgnoreCase(memberLabel)) {
395             quarter = 3;
396         } else if ("Q4".equalsIgnoreCase(memberLabel)) {
397             quarter = 4;
398         }
399
400         return new Quarter(quarter, Integer.parseInt(year));
401     }
402
403     /**
404     * @return month object - a org.jfree.data.time.Month (RegularTimePeriod)
405     */

406     private Month createMonth(MemberTree memberTree, Member[] members)
407         throws ParseException JavaDoc {
408         String JavaDoc year = memberTree.getParent(members[0]).getLabel();
409         int month = 1;
410         String JavaDoc memberLabel = members[0].getLabel();
411         month = parseMonth(memberLabel);
412
413         return new Month(month, Integer.parseInt(year));
414     }
415     
416
417     private RegularTimePeriod createDay(MemberTree memberTree, Member[] members) {
418         int day = 1;
419         String JavaDoc dayLabel = members[0].getLabel();
420         day = Integer.parseInt(dayLabel);
421         
422         int month = 1;
423         String JavaDoc monthLabel = memberTree.getParent(members[0]).getLabel();
424         month = parseMonth(monthLabel);
425         
426         int year = 1900;
427         String JavaDoc yearLabel = memberTree.getParent(members[0]).getLabel();
428
429         return new Day(day, month, year);
430     }
431
432     private int parseMonth(String JavaDoc memberLabel) {
433         Integer JavaDoc month = null;
434
435         //try to parse assuming int:
436
try {
437             month = new Integer JavaDoc(memberLabel);
438         } catch (NumberFormatException JavaDoc e) {
439             // logger.debug("could not parse as int");
440
}
441
442         // try string?
443
if (month == null) {
444             try {
445                 SimpleDateFormat JavaDoc formatter = new SimpleDateFormat JavaDoc("MMM");
446                 Date JavaDoc date = formatter.parse(memberLabel);
447                 month = new Integer JavaDoc(date.getMonth() + 1);
448             } catch (ParseException JavaDoc e) {
449                 // logger.debug(e);
450
}
451         }
452
453         return month.intValue();
454     }
455
456     private String JavaDoc parseRootLevel(List JavaDoc rowPositions) {
457         String JavaDoc rootLevel = null;
458
459         try {
460             rootLevel = ((Position) rowPositions.get(0)).getMembers()[0].getLevel()
461                                                                         .getLabel();
462         } catch (Exception JavaDoc e) {
463             logger.debug(e);
464         }
465
466         return rootLevel;
467     }
468     
469     public NumberFormat JavaDoc getNumberFormatter() {
470         return numberFormatter;
471     }
472 }
473
Popular Tags