1:
54:
55: package ;
56:
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: import ;
71: import ;
72:
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: import ;
89: import ;
90:
91:
95: public class MultiplePiePlot extends Plot implements Cloneable, Serializable {
96:
97:
98: private static final long serialVersionUID = -355377800470807389L;
99:
100:
101: private JFreeChart pieChart;
102:
103:
104: private CategoryDataset dataset;
105:
106:
107: private TableOrder dataExtractOrder;
108:
109:
110: private double limit = 0.0;
111:
112:
116: private Comparable aggregatedItemsKey;
117:
118:
122: private transient Paint aggregatedItemsPaint;
123:
124:
128: private transient Map sectionPaints;
129:
130:
133: public MultiplePiePlot() {
134: this(null);
135: }
136:
137:
142: public MultiplePiePlot(CategoryDataset dataset) {
143: super();
144: setDataset(dataset);
145: PiePlot piePlot = new PiePlot(null);
146: this.pieChart = new JFreeChart(piePlot);
147: this.pieChart.removeLegend();
148: this.dataExtractOrder = TableOrder.BY_COLUMN;
149: this.pieChart.setBackgroundPaint(null);
150: TextTitle seriesTitle = new TextTitle("Series Title",
151: new Font("SansSerif", Font.BOLD, 12));
152: seriesTitle.setPosition(RectangleEdge.BOTTOM);
153: this.pieChart.setTitle(seriesTitle);
154: this.aggregatedItemsKey = "Other";
155: this.aggregatedItemsPaint = Color.lightGray;
156: this.sectionPaints = new HashMap();
157: }
158:
159:
164: public CategoryDataset getDataset() {
165: return this.dataset;
166: }
167:
168:
174: public void setDataset(CategoryDataset dataset) {
175:
176:
177: if (this.dataset != null) {
178: this.dataset.removeChangeListener(this);
179: }
180:
181:
182: this.dataset = dataset;
183: if (dataset != null) {
184: setDatasetGroup(dataset.getGroup());
185: dataset.addChangeListener(this);
186: }
187:
188:
189: datasetChanged(new DatasetChangeEvent(this, dataset));
190: }
191:
192:
199: public JFreeChart getPieChart() {
200: return this.pieChart;
201: }
202:
203:
211: public void setPieChart(JFreeChart pieChart) {
212: if (pieChart == null) {
213: throw new IllegalArgumentException("Null 'pieChart' argument.");
214: }
215: if (!(pieChart.getPlot() instanceof PiePlot)) {
216: throw new IllegalArgumentException("The 'pieChart' argument must "
217: + "be a chart based on a PiePlot.");
218: }
219: this.pieChart = pieChart;
220: fireChangeEvent();
221: }
222:
223:
228: public TableOrder getDataExtractOrder() {
229: return this.dataExtractOrder;
230: }
231:
232:
238: public void setDataExtractOrder(TableOrder order) {
239: if (order == null) {
240: throw new IllegalArgumentException("Null 'order' argument");
241: }
242: this.dataExtractOrder = order;
243: fireChangeEvent();
244: }
245:
246:
252: public double getLimit() {
253: return this.limit;
254: }
255:
256:
262: public void setLimit(double limit) {
263: this.limit = limit;
264: fireChangeEvent();
265: }
266:
267:
275: public Comparable getAggregatedItemsKey() {
276: return this.aggregatedItemsKey;
277: }
278:
279:
287: public void setAggregatedItemsKey(Comparable key) {
288: if (key == null) {
289: throw new IllegalArgumentException("Null 'key' argument.");
290: }
291: this.aggregatedItemsKey = key;
292: fireChangeEvent();
293: }
294:
295:
303: public Paint getAggregatedItemsPaint() {
304: return this.aggregatedItemsPaint;
305: }
306:
307:
315: public void setAggregatedItemsPaint(Paint paint) {
316: if (paint == null) {
317: throw new IllegalArgumentException("Null 'paint' argument.");
318: }
319: this.aggregatedItemsPaint = paint;
320: fireChangeEvent();
321: }
322:
323:
328: public String getPlotType() {
329: return "Multiple Pie Plot";
330:
331: }
332:
333:
343: public void draw(Graphics2D g2,
344: Rectangle2D area,
345: Point2D anchor,
346: PlotState parentState,
347: PlotRenderingInfo info) {
348:
349:
350:
351: RectangleInsets insets = getInsets();
352: insets.trim(area);
353: drawBackground(g2, area);
354: drawOutline(g2, area);
355:
356:
357: if (DatasetUtilities.isEmptyOrNull(this.dataset)) {
358: drawNoDataMessage(g2, area);
359: return;
360: }
361:
362: int pieCount = 0;
363: if (this.dataExtractOrder == TableOrder.BY_ROW) {
364: pieCount = this.dataset.getRowCount();
365: }
366: else {
367: pieCount = this.dataset.getColumnCount();
368: }
369:
370:
371: int displayCols = (int) Math.ceil(Math.sqrt(pieCount));
372: int displayRows
373: = (int) Math.ceil((double) pieCount / (double) displayCols);
374:
375:
376: if (displayCols > displayRows && area.getWidth() < area.getHeight()) {
377: int temp = displayCols;
378: displayCols = displayRows;
379: displayRows = temp;
380: }
381:
382: prefetchSectionPaints();
383:
384: int x = (int) area.getX();
385: int y = (int) area.getY();
386: int width = ((int) area.getWidth()) / displayCols;
387: int height = ((int) area.getHeight()) / displayRows;
388: int row = 0;
389: int column = 0;
390: int diff = (displayRows * displayCols) - pieCount;
391: int xoffset = 0;
392: Rectangle rect = new Rectangle();
393:
394: for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) {
395: rect.setBounds(x + xoffset + (width * column), y + (height * row),
396: width, height);
397:
398: String title = null;
399: if (this.dataExtractOrder == TableOrder.BY_ROW) {
400: title = this.dataset.getRowKey(pieIndex).toString();
401: }
402: else {
403: title = this.dataset.getColumnKey(pieIndex).toString();
404: }
405: this.pieChart.setTitle(title);
406:
407: PieDataset piedataset = null;
408: PieDataset dd = new CategoryToPieDataset(this.dataset,
409: this.dataExtractOrder, pieIndex);
410: if (this.limit > 0.0) {
411: piedataset = DatasetUtilities.createConsolidatedPieDataset(
412: dd, this.aggregatedItemsKey, this.limit);
413: }
414: else {
415: piedataset = dd;
416: }
417: PiePlot piePlot = (PiePlot) this.pieChart.getPlot();
418: piePlot.setDataset(piedataset);
419: piePlot.setPieIndex(pieIndex);
420:
421:
422: for (int i = 0; i < piedataset.getItemCount(); i++) {
423: Comparable key = piedataset.getKey(i);
424: Paint p;
425: if (key.equals(this.aggregatedItemsKey)) {
426: p = this.aggregatedItemsPaint;
427: }
428: else {
429: p = (Paint) this.sectionPaints.get(key);
430: }
431: piePlot.setSectionPaint(key, p);
432: }
433:
434: ChartRenderingInfo subinfo = null;
435: if (info != null) {
436: subinfo = new ChartRenderingInfo();
437: }
438: this.pieChart.draw(g2, rect, subinfo);
439: if (info != null) {
440: info.getOwner().getEntityCollection().addAll(
441: subinfo.getEntityCollection());
442: info.addSubplotInfo(subinfo.getPlotInfo());
443: }
444:
445: ++column;
446: if (column == displayCols) {
447: column = 0;
448: ++row;
449:
450: if (row == displayRows - 1 && diff != 0) {
451: xoffset = (diff * width) / 2;
452: }
453: }
454: }
455:
456: }
457:
458:
464: private void prefetchSectionPaints() {
465:
466:
467:
468:
469:
470: PiePlot piePlot = (PiePlot) getPieChart().getPlot();
471:
472: if (this.dataExtractOrder == TableOrder.BY_ROW) {
473:
474: for (int c = 0; c < this.dataset.getColumnCount(); c++) {
475: Comparable key = this.dataset.getColumnKey(c);
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: else {
487:
488: for (int r = 0; r < this.dataset.getRowCount(); r++) {
489: Comparable key = this.dataset.getRowKey(r);
490: Paint p = piePlot.getSectionPaint(key);
491: if (p == null) {
492: p = (Paint) this.sectionPaints.get(key);
493: if (p == null) {
494: p = getDrawingSupplier().getNextPaint();
495: }
496: }
497: this.sectionPaints.put(key, p);
498: }
499: }
500:
501: }
502:
503:
508: public LegendItemCollection getLegendItems() {
509:
510: LegendItemCollection result = new LegendItemCollection();
511:
512: if (this.dataset != null) {
513: List keys = null;
514:
515: prefetchSectionPaints();
516: if (this.dataExtractOrder == TableOrder.BY_ROW) {
517: keys = this.dataset.getColumnKeys();
518: }
519: else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
520: keys = this.dataset.getRowKeys();
521: }
522:
523: if (keys != null) {
524: int section = 0;
525: Iterator iterator = keys.iterator();
526: while (iterator.hasNext()) {
527: Comparable key = (Comparable) iterator.next();
528: String label = key.toString();
529: String description = label;
530: Paint paint = (Paint) this.sectionPaints.get(key);
531: LegendItem item = new LegendItem(label, description,
532: null, null, Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
533: paint, Plot.DEFAULT_OUTLINE_STROKE, paint);
534: item.setDataset(getDataset());
535: result.add(item);
536: section++;
537: }
538: }
539: if (this.limit > 0.0) {
540: result.add(new LegendItem(this.aggregatedItemsKey.toString(),
541: this.aggregatedItemsKey.toString(), null, null,
542: Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
543: this.aggregatedItemsPaint,
544: Plot.DEFAULT_OUTLINE_STROKE,
545: this.aggregatedItemsPaint));
546: }
547: }
548: return result;
549: }
550:
551:
560: public boolean equals(Object obj) {
561: if (obj == this) {
562: return true;
563: }
564: if (!(obj instanceof MultiplePiePlot)) {
565: return false;
566: }
567: MultiplePiePlot that = (MultiplePiePlot) obj;
568: if (this.dataExtractOrder != that.dataExtractOrder) {
569: return false;
570: }
571: if (this.limit != that.limit) {
572: return false;
573: }
574: if (!this.aggregatedItemsKey.equals(that.aggregatedItemsKey)) {
575: return false;
576: }
577: if (!PaintUtilities.equal(this.aggregatedItemsPaint,
578: that.aggregatedItemsPaint)) {
579: return false;
580: }
581: if (!ObjectUtilities.equal(this.pieChart, that.pieChart)) {
582: return false;
583: }
584: if (!super.equals(obj)) {
585: return false;
586: }
587: return true;
588: }
589:
590:
597: private void writeObject(ObjectOutputStream stream) throws IOException {
598: stream.defaultWriteObject();
599: SerialUtilities.writePaint(this.aggregatedItemsPaint, stream);
600: }
601:
602:
610: private void readObject(ObjectInputStream stream)
611: throws IOException, ClassNotFoundException {
612: stream.defaultReadObject();
613: this.aggregatedItemsPaint = SerialUtilities.readPaint(stream);
614: this.sectionPaints = new HashMap();
615: }
616:
617:
618: }