1:
52:
53: package ;
54:
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70:
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77: import ;
78: import ;
79: import ;
80: import ;
81: import ;
82: import ;
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88:
89:
93: public class MultiplePiePlot extends Plot implements Cloneable, Serializable {
94:
95:
96: private static final long serialVersionUID = -355377800470807389L;
97:
98:
99: private JFreeChart pieChart;
100:
101:
102: private CategoryDataset dataset;
103:
104:
105: private TableOrder dataExtractOrder;
106:
107:
108: private double limit = 0.0;
109:
110:
114: private Comparable aggregatedItemsKey;
115:
116:
120: private transient Paint aggregatedItemsPaint;
121:
122:
126: private transient Map sectionPaints;
127:
128:
131: public MultiplePiePlot() {
132: this(null);
133: }
134:
135:
140: public MultiplePiePlot(CategoryDataset dataset) {
141: super();
142: this.dataset = dataset;
143: PiePlot piePlot = new PiePlot(null);
144: this.pieChart = new JFreeChart(piePlot);
145: this.pieChart.removeLegend();
146: this.dataExtractOrder = TableOrder.BY_COLUMN;
147: this.pieChart.setBackgroundPaint(null);
148: TextTitle seriesTitle = new TextTitle("Series Title",
149: new Font("SansSerif", Font.BOLD, 12));
150: seriesTitle.setPosition(RectangleEdge.BOTTOM);
151: this.pieChart.setTitle(seriesTitle);
152: this.aggregatedItemsKey = "Other";
153: this.aggregatedItemsPaint = Color.lightGray;
154: this.sectionPaints = new HashMap();
155: }
156:
157:
162: public CategoryDataset getDataset() {
163: return this.dataset;
164: }
165:
166:
172: public void setDataset(CategoryDataset dataset) {
173:
174:
175: if (this.dataset != null) {
176: this.dataset.removeChangeListener(this);
177: }
178:
179:
180: this.dataset = dataset;
181: if (dataset != null) {
182: setDatasetGroup(dataset.getGroup());
183: dataset.addChangeListener(this);
184: }
185:
186:
187: datasetChanged(new DatasetChangeEvent(this, dataset));
188: }
189:
190:
195: public JFreeChart getPieChart() {
196: return this.pieChart;
197: }
198:
199:
204: public void setPieChart(JFreeChart pieChart) {
205: this.pieChart = pieChart;
206: notifyListeners(new PlotChangeEvent(this));
207: }
208:
209:
214: public TableOrder getDataExtractOrder() {
215: return this.dataExtractOrder;
216: }
217:
218:
224: public void setDataExtractOrder(TableOrder order) {
225: if (order == null) {
226: throw new IllegalArgumentException("Null 'order' argument");
227: }
228: this.dataExtractOrder = order;
229: notifyListeners(new PlotChangeEvent(this));
230: }
231:
232:
238: public double getLimit() {
239: return this.limit;
240: }
241:
242:
248: public void setLimit(double limit) {
249: this.limit = limit;
250: notifyListeners(new PlotChangeEvent(this));
251: }
252:
253:
261: public Comparable getAggregatedItemsKey() {
262: return this.aggregatedItemsKey;
263: }
264:
265:
273: public void setAggregatedItemsKey(Comparable key) {
274: if (key == null) {
275: throw new IllegalArgumentException("Null 'key' argument.");
276: }
277: this.aggregatedItemsKey = key;
278: notifyListeners(new PlotChangeEvent(this));
279: }
280:
281:
289: public Paint getAggregatedItemsPaint() {
290: return this.aggregatedItemsPaint;
291: }
292:
293:
301: public void setAggregatedItemsPaint(Paint paint) {
302: if (paint == null) {
303: throw new IllegalArgumentException("Null 'paint' argument.");
304: }
305: this.aggregatedItemsPaint = paint;
306: notifyListeners(new PlotChangeEvent(this));
307: }
308:
309:
314: public String getPlotType() {
315: return "Multiple Pie Plot";
316:
317: }
318:
319:
329: public void draw(Graphics2D g2,
330: Rectangle2D area,
331: Point2D anchor,
332: PlotState parentState,
333: PlotRenderingInfo info) {
334:
335:
336:
337: RectangleInsets insets = getInsets();
338: insets.trim(area);
339: drawBackground(g2, area);
340: drawOutline(g2, area);
341:
342:
343: if (DatasetUtilities.isEmptyOrNull(this.dataset)) {
344: drawNoDataMessage(g2, area);
345: return;
346: }
347:
348: int pieCount = 0;
349: if (this.dataExtractOrder == TableOrder.BY_ROW) {
350: pieCount = this.dataset.getRowCount();
351: }
352: else {
353: pieCount = this.dataset.getColumnCount();
354: }
355:
356:
357: int displayCols = (int) Math.ceil(Math.sqrt(pieCount));
358: int displayRows
359: = (int) Math.ceil((double) pieCount / (double) displayCols);
360:
361:
362: if (displayCols > displayRows && area.getWidth() < area.getHeight()) {
363: int temp = displayCols;
364: displayCols = displayRows;
365: displayRows = temp;
366: }
367:
368: prefetchSectionPaints();
369:
370: int x = (int) area.getX();
371: int y = (int) area.getY();
372: int width = ((int) area.getWidth()) / displayCols;
373: int height = ((int) area.getHeight()) / displayRows;
374: int row = 0;
375: int column = 0;
376: int diff = (displayRows * displayCols) - pieCount;
377: int xoffset = 0;
378: Rectangle rect = new Rectangle();
379:
380: for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) {
381: rect.setBounds(x + xoffset + (width * column), y + (height * row),
382: width, height);
383:
384: String title = null;
385: if (this.dataExtractOrder == TableOrder.BY_ROW) {
386: title = this.dataset.getRowKey(pieIndex).toString();
387: }
388: else {
389: title = this.dataset.getColumnKey(pieIndex).toString();
390: }
391: this.pieChart.setTitle(title);
392:
393: PieDataset piedataset = null;
394: PieDataset dd = new CategoryToPieDataset(this.dataset,
395: this.dataExtractOrder, pieIndex);
396: if (this.limit > 0.0) {
397: piedataset = DatasetUtilities.createConsolidatedPieDataset(
398: dd, this.aggregatedItemsKey, this.limit);
399: }
400: else {
401: piedataset = dd;
402: }
403: PiePlot piePlot = (PiePlot) this.pieChart.getPlot();
404: piePlot.setDataset(piedataset);
405: piePlot.setPieIndex(pieIndex);
406:
407:
408: for (int i = 0; i < piedataset.getItemCount(); i++) {
409: Comparable key = piedataset.getKey(i);
410: Paint p;
411: if (key.equals(this.aggregatedItemsKey)) {
412: p = this.aggregatedItemsPaint;
413: }
414: else {
415: p = (Paint) this.sectionPaints.get(key);
416: }
417: piePlot.setSectionPaint(key, p);
418: }
419:
420: ChartRenderingInfo subinfo = null;
421: if (info != null) {
422: subinfo = new ChartRenderingInfo();
423: }
424: this.pieChart.draw(g2, rect, subinfo);
425: if (info != null) {
426: info.getOwner().getEntityCollection().addAll(
427: subinfo.getEntityCollection());
428: info.addSubplotInfo(subinfo.getPlotInfo());
429: }
430:
431: ++column;
432: if (column == displayCols) {
433: column = 0;
434: ++row;
435:
436: if (row == displayRows - 1 && diff != 0) {
437: xoffset = (diff * width) / 2;
438: }
439: }
440: }
441:
442: }
443:
444:
450: private void prefetchSectionPaints() {
451:
452:
453:
454:
455:
456: PiePlot piePlot = (PiePlot) getPieChart().getPlot();
457:
458: if (this.dataExtractOrder == TableOrder.BY_ROW) {
459:
460: for (int c = 0; c < this.dataset.getColumnCount(); c++) {
461: Comparable key = this.dataset.getColumnKey(c);
462: Paint p = piePlot.getSectionPaint(key);
463: if (p == null) {
464: p = (Paint) this.sectionPaints.get(key);
465: if (p == null) {
466: p = getDrawingSupplier().getNextPaint();
467: }
468: }
469: this.sectionPaints.put(key, p);
470: }
471: }
472: else {
473:
474: for (int r = 0; r < this.dataset.getRowCount(); r++) {
475: Comparable key = this.dataset.getRowKey(r);
476: Paint p = piePlot.getSectionPaint(key);
477: if (p == null) {
478: p = (Paint) this.sectionPaints.get(key);
479: if (p == null) {
480: p = getDrawingSupplier().getNextPaint();
481: }
482: }
483: this.sectionPaints.put(key, p);
484: }
485: }
486:
487: }
488:
489:
494: public LegendItemCollection getLegendItems() {
495:
496: LegendItemCollection result = new LegendItemCollection();
497:
498: if (this.dataset != null) {
499: List keys = null;
500:
501: prefetchSectionPaints();
502: if (this.dataExtractOrder == TableOrder.BY_ROW) {
503: keys = this.dataset.getColumnKeys();
504: }
505: else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
506: keys = this.dataset.getRowKeys();
507: }
508:
509: if (keys != null) {
510: int section = 0;
511: Iterator iterator = keys.iterator();
512: while (iterator.hasNext()) {
513: Comparable key = (Comparable) iterator.next();
514: String label = key.toString();
515: String description = label;
516: Paint paint = (Paint) this.sectionPaints.get(key);
517: LegendItem item = new LegendItem(label, description,
518: null, null, Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
519: paint, Plot.DEFAULT_OUTLINE_STROKE, paint);
520:
521: result.add(item);
522: section++;
523: }
524: }
525: if (this.limit > 0.0) {
526: result.add(new LegendItem(this.aggregatedItemsKey.toString(),
527: this.aggregatedItemsKey.toString(), null, null,
528: Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
529: this.aggregatedItemsPaint,
530: Plot.DEFAULT_OUTLINE_STROKE,
531: this.aggregatedItemsPaint));
532: }
533: }
534: return result;
535: }
536:
537:
546: public boolean equals(Object obj) {
547: if (obj == this) {
548: return true;
549: }
550: if (!(obj instanceof MultiplePiePlot)) {
551: return false;
552: }
553: MultiplePiePlot that = (MultiplePiePlot) obj;
554: if (this.dataExtractOrder != that.dataExtractOrder) {
555: return false;
556: }
557: if (this.limit != that.limit) {
558: return false;
559: }
560: if (!this.aggregatedItemsKey.equals(that.aggregatedItemsKey)) {
561: return false;
562: }
563: if (!PaintUtilities.equal(this.aggregatedItemsPaint,
564: that.aggregatedItemsPaint)) {
565: return false;
566: }
567: if (!ObjectUtilities.equal(this.pieChart, that.pieChart)) {
568: return false;
569: }
570: if (!super.equals(obj)) {
571: return false;
572: }
573: return true;
574: }
575:
576:
583: private void writeObject(ObjectOutputStream stream) throws IOException {
584: stream.defaultWriteObject();
585: SerialUtilities.writePaint(this.aggregatedItemsPaint, stream);
586: }
587:
588:
596: private void readObject(ObjectInputStream stream)
597: throws IOException, ClassNotFoundException {
598: stream.defaultReadObject();
599: this.aggregatedItemsPaint = SerialUtilities.readPaint(stream);
600: this.sectionPaints = new HashMap();
601: }
602:
603:
604: }