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:
197: public JFreeChart getPieChart() {
198: return this.pieChart;
199: }
200:
201:
209: public void setPieChart(JFreeChart pieChart) {
210: if (pieChart == null) {
211: throw new IllegalArgumentException("Null 'pieChart' argument.");
212: }
213: if (!(pieChart.getPlot() instanceof PiePlot)) {
214: throw new IllegalArgumentException("The 'pieChart' argument must "
215: + "be a chart based on a PiePlot.");
216: }
217: this.pieChart = pieChart;
218: notifyListeners(new PlotChangeEvent(this));
219: }
220:
221:
226: public TableOrder getDataExtractOrder() {
227: return this.dataExtractOrder;
228: }
229:
230:
236: public void setDataExtractOrder(TableOrder order) {
237: if (order == null) {
238: throw new IllegalArgumentException("Null 'order' argument");
239: }
240: this.dataExtractOrder = order;
241: notifyListeners(new PlotChangeEvent(this));
242: }
243:
244:
250: public double getLimit() {
251: return this.limit;
252: }
253:
254:
260: public void setLimit(double limit) {
261: this.limit = limit;
262: notifyListeners(new PlotChangeEvent(this));
263: }
264:
265:
273: public Comparable getAggregatedItemsKey() {
274: return this.aggregatedItemsKey;
275: }
276:
277:
285: public void setAggregatedItemsKey(Comparable key) {
286: if (key == null) {
287: throw new IllegalArgumentException("Null 'key' argument.");
288: }
289: this.aggregatedItemsKey = key;
290: notifyListeners(new PlotChangeEvent(this));
291: }
292:
293:
301: public Paint getAggregatedItemsPaint() {
302: return this.aggregatedItemsPaint;
303: }
304:
305:
313: public void setAggregatedItemsPaint(Paint paint) {
314: if (paint == null) {
315: throw new IllegalArgumentException("Null 'paint' argument.");
316: }
317: this.aggregatedItemsPaint = paint;
318: notifyListeners(new PlotChangeEvent(this));
319: }
320:
321:
326: public String getPlotType() {
327: return "Multiple Pie Plot";
328:
329: }
330:
331:
341: public void draw(Graphics2D g2,
342: Rectangle2D area,
343: Point2D anchor,
344: PlotState parentState,
345: PlotRenderingInfo info) {
346:
347:
348:
349: RectangleInsets insets = getInsets();
350: insets.trim(area);
351: drawBackground(g2, area);
352: drawOutline(g2, area);
353:
354:
355: if (DatasetUtilities.isEmptyOrNull(this.dataset)) {
356: drawNoDataMessage(g2, area);
357: return;
358: }
359:
360: int pieCount = 0;
361: if (this.dataExtractOrder == TableOrder.BY_ROW) {
362: pieCount = this.dataset.getRowCount();
363: }
364: else {
365: pieCount = this.dataset.getColumnCount();
366: }
367:
368:
369: int displayCols = (int) Math.ceil(Math.sqrt(pieCount));
370: int displayRows
371: = (int) Math.ceil((double) pieCount / (double) displayCols);
372:
373:
374: if (displayCols > displayRows && area.getWidth() < area.getHeight()) {
375: int temp = displayCols;
376: displayCols = displayRows;
377: displayRows = temp;
378: }
379:
380: prefetchSectionPaints();
381:
382: int x = (int) area.getX();
383: int y = (int) area.getY();
384: int width = ((int) area.getWidth()) / displayCols;
385: int height = ((int) area.getHeight()) / displayRows;
386: int row = 0;
387: int column = 0;
388: int diff = (displayRows * displayCols) - pieCount;
389: int xoffset = 0;
390: Rectangle rect = new Rectangle();
391:
392: for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) {
393: rect.setBounds(x + xoffset + (width * column), y + (height * row),
394: width, height);
395:
396: String title = null;
397: if (this.dataExtractOrder == TableOrder.BY_ROW) {
398: title = this.dataset.getRowKey(pieIndex).toString();
399: }
400: else {
401: title = this.dataset.getColumnKey(pieIndex).toString();
402: }
403: this.pieChart.setTitle(title);
404:
405: PieDataset piedataset = null;
406: PieDataset dd = new CategoryToPieDataset(this.dataset,
407: this.dataExtractOrder, pieIndex);
408: if (this.limit > 0.0) {
409: piedataset = DatasetUtilities.createConsolidatedPieDataset(
410: dd, this.aggregatedItemsKey, this.limit);
411: }
412: else {
413: piedataset = dd;
414: }
415: PiePlot piePlot = (PiePlot) this.pieChart.getPlot();
416: piePlot.setDataset(piedataset);
417: piePlot.setPieIndex(pieIndex);
418:
419:
420: for (int i = 0; i < piedataset.getItemCount(); i++) {
421: Comparable key = piedataset.getKey(i);
422: Paint p;
423: if (key.equals(this.aggregatedItemsKey)) {
424: p = this.aggregatedItemsPaint;
425: }
426: else {
427: p = (Paint) this.sectionPaints.get(key);
428: }
429: piePlot.setSectionPaint(key, p);
430: }
431:
432: ChartRenderingInfo subinfo = null;
433: if (info != null) {
434: subinfo = new ChartRenderingInfo();
435: }
436: this.pieChart.draw(g2, rect, subinfo);
437: if (info != null) {
438: info.getOwner().getEntityCollection().addAll(
439: subinfo.getEntityCollection());
440: info.addSubplotInfo(subinfo.getPlotInfo());
441: }
442:
443: ++column;
444: if (column == displayCols) {
445: column = 0;
446: ++row;
447:
448: if (row == displayRows - 1 && diff != 0) {
449: xoffset = (diff * width) / 2;
450: }
451: }
452: }
453:
454: }
455:
456:
462: private void prefetchSectionPaints() {
463:
464:
465:
466:
467:
468: PiePlot piePlot = (PiePlot) getPieChart().getPlot();
469:
470: if (this.dataExtractOrder == TableOrder.BY_ROW) {
471:
472: for (int c = 0; c < this.dataset.getColumnCount(); c++) {
473: Comparable key = this.dataset.getColumnKey(c);
474: Paint p = piePlot.getSectionPaint(key);
475: if (p == null) {
476: p = (Paint) this.sectionPaints.get(key);
477: if (p == null) {
478: p = getDrawingSupplier().getNextPaint();
479: }
480: }
481: this.sectionPaints.put(key, p);
482: }
483: }
484: else {
485:
486: for (int r = 0; r < this.dataset.getRowCount(); r++) {
487: Comparable key = this.dataset.getRowKey(r);
488: Paint p = piePlot.getSectionPaint(key);
489: if (p == null) {
490: p = (Paint) this.sectionPaints.get(key);
491: if (p == null) {
492: p = getDrawingSupplier().getNextPaint();
493: }
494: }
495: this.sectionPaints.put(key, p);
496: }
497: }
498:
499: }
500:
501:
506: public LegendItemCollection getLegendItems() {
507:
508: LegendItemCollection result = new LegendItemCollection();
509:
510: if (this.dataset != null) {
511: List keys = null;
512:
513: prefetchSectionPaints();
514: if (this.dataExtractOrder == TableOrder.BY_ROW) {
515: keys = this.dataset.getColumnKeys();
516: }
517: else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
518: keys = this.dataset.getRowKeys();
519: }
520:
521: if (keys != null) {
522: int section = 0;
523: Iterator iterator = keys.iterator();
524: while (iterator.hasNext()) {
525: Comparable key = (Comparable) iterator.next();
526: String label = key.toString();
527: String description = label;
528: Paint paint = (Paint) this.sectionPaints.get(key);
529: LegendItem item = new LegendItem(label, description,
530: null, null, Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
531: paint, Plot.DEFAULT_OUTLINE_STROKE, paint);
532: item.setDataset(getDataset());
533: result.add(item);
534: section++;
535: }
536: }
537: if (this.limit > 0.0) {
538: result.add(new LegendItem(this.aggregatedItemsKey.toString(),
539: this.aggregatedItemsKey.toString(), null, null,
540: Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
541: this.aggregatedItemsPaint,
542: Plot.DEFAULT_OUTLINE_STROKE,
543: this.aggregatedItemsPaint));
544: }
545: }
546: return result;
547: }
548:
549:
558: public boolean equals(Object obj) {
559: if (obj == this) {
560: return true;
561: }
562: if (!(obj instanceof MultiplePiePlot)) {
563: return false;
564: }
565: MultiplePiePlot that = (MultiplePiePlot) obj;
566: if (this.dataExtractOrder != that.dataExtractOrder) {
567: return false;
568: }
569: if (this.limit != that.limit) {
570: return false;
571: }
572: if (!this.aggregatedItemsKey.equals(that.aggregatedItemsKey)) {
573: return false;
574: }
575: if (!PaintUtilities.equal(this.aggregatedItemsPaint,
576: that.aggregatedItemsPaint)) {
577: return false;
578: }
579: if (!ObjectUtilities.equal(this.pieChart, that.pieChart)) {
580: return false;
581: }
582: if (!super.equals(obj)) {
583: return false;
584: }
585: return true;
586: }
587:
588:
595: private void writeObject(ObjectOutputStream stream) throws IOException {
596: stream.defaultWriteObject();
597: SerialUtilities.writePaint(this.aggregatedItemsPaint, stream);
598: }
599:
600:
608: private void readObject(ObjectInputStream stream)
609: throws IOException, ClassNotFoundException {
610: stream.defaultReadObject();
611: this.aggregatedItemsPaint = SerialUtilities.readPaint(stream);
612: this.sectionPaints = new HashMap();
613: }
614:
615:
616: }