1:
49:
50: package ;
51:
52: import ;
53: import ;
54: import ;
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:
68: import ;
69: import ;
70: import ;
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:
86:
90: public class MultiplePiePlot extends Plot implements Cloneable, Serializable {
91:
92:
93: private static final long serialVersionUID = -355377800470807389L;
94:
95:
96: private JFreeChart pieChart;
97:
98:
99: private CategoryDataset dataset;
100:
101:
102: private TableOrder dataExtractOrder;
103:
104:
105: private double limit = 0.0;
106:
107:
111: private Comparable aggregatedItemsKey;
112:
113:
117: private transient Paint aggregatedItemsPaint;
118:
119:
123: private transient Map sectionPaints;
124:
125:
128: public MultiplePiePlot() {
129: this(null);
130: }
131:
132:
137: public MultiplePiePlot(CategoryDataset dataset) {
138: super();
139: this.dataset = dataset;
140: PiePlot piePlot = new PiePlot(null);
141: this.pieChart = new JFreeChart(piePlot);
142: this.pieChart.removeLegend();
143: this.dataExtractOrder = TableOrder.BY_COLUMN;
144: this.pieChart.setBackgroundPaint(null);
145: TextTitle seriesTitle = new TextTitle("Series Title",
146: new Font("SansSerif", Font.BOLD, 12));
147: seriesTitle.setPosition(RectangleEdge.BOTTOM);
148: this.pieChart.setTitle(seriesTitle);
149: this.aggregatedItemsKey = "Other";
150: this.aggregatedItemsPaint = Color.lightGray;
151: this.sectionPaints = new HashMap();
152: }
153:
154:
159: public CategoryDataset getDataset() {
160: return this.dataset;
161: }
162:
163:
169: public void setDataset(CategoryDataset dataset) {
170:
171:
172: if (this.dataset != null) {
173: this.dataset.removeChangeListener(this);
174: }
175:
176:
177: this.dataset = dataset;
178: if (dataset != null) {
179: setDatasetGroup(dataset.getGroup());
180: dataset.addChangeListener(this);
181: }
182:
183:
184: datasetChanged(new DatasetChangeEvent(this, dataset));
185: }
186:
187:
192: public JFreeChart getPieChart() {
193: return this.pieChart;
194: }
195:
196:
201: public void setPieChart(JFreeChart pieChart) {
202: this.pieChart = pieChart;
203: notifyListeners(new PlotChangeEvent(this));
204: }
205:
206:
211: public TableOrder getDataExtractOrder() {
212: return this.dataExtractOrder;
213: }
214:
215:
221: public void setDataExtractOrder(TableOrder order) {
222: if (order == null) {
223: throw new IllegalArgumentException("Null 'order' argument");
224: }
225: this.dataExtractOrder = order;
226: notifyListeners(new PlotChangeEvent(this));
227: }
228:
229:
235: public double getLimit() {
236: return this.limit;
237: }
238:
239:
245: public void setLimit(double limit) {
246: this.limit = limit;
247: notifyListeners(new PlotChangeEvent(this));
248: }
249:
250:
258: public Comparable getAggregatedItemsKey() {
259: return this.aggregatedItemsKey;
260: }
261:
262:
270: public void setAggregatedItemsKey(Comparable key) {
271: if (key == null) {
272: throw new IllegalArgumentException("Null 'key' argument.");
273: }
274: this.aggregatedItemsKey = key;
275: notifyListeners(new PlotChangeEvent(this));
276: }
277:
278:
286: public Paint getAggregatedItemsPaint() {
287: return this.aggregatedItemsPaint;
288: }
289:
290:
298: public void setAggregatedItemsPaint(Paint paint) {
299: if (paint == null) {
300: throw new IllegalArgumentException("Null 'paint' argument.");
301: }
302: this.aggregatedItemsPaint = paint;
303: notifyListeners(new PlotChangeEvent(this));
304: }
305:
306:
311: public String getPlotType() {
312: return "Multiple Pie Plot";
313:
314: }
315:
316:
326: public void draw(Graphics2D g2,
327: Rectangle2D area,
328: Point2D anchor,
329: PlotState parentState,
330: PlotRenderingInfo info) {
331:
332:
333:
334: RectangleInsets insets = getInsets();
335: insets.trim(area);
336: drawBackground(g2, area);
337: drawOutline(g2, area);
338:
339:
340: if (DatasetUtilities.isEmptyOrNull(this.dataset)) {
341: drawNoDataMessage(g2, area);
342: return;
343: }
344:
345: int pieCount = 0;
346: if (this.dataExtractOrder == TableOrder.BY_ROW) {
347: pieCount = this.dataset.getRowCount();
348: }
349: else {
350: pieCount = this.dataset.getColumnCount();
351: }
352:
353:
354: int displayCols = (int) Math.ceil(Math.sqrt(pieCount));
355: int displayRows
356: = (int) Math.ceil((double) pieCount / (double) displayCols);
357:
358:
359: if (displayCols > displayRows && area.getWidth() < area.getHeight()) {
360: int temp = displayCols;
361: displayCols = displayRows;
362: displayRows = temp;
363: }
364:
365: prefetchSectionPaints();
366:
367: int x = (int) area.getX();
368: int y = (int) area.getY();
369: int width = ((int) area.getWidth()) / displayCols;
370: int height = ((int) area.getHeight()) / displayRows;
371: int row = 0;
372: int column = 0;
373: int diff = (displayRows * displayCols) - pieCount;
374: int xoffset = 0;
375: Rectangle rect = new Rectangle();
376:
377: for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) {
378: rect.setBounds(x + xoffset + (width * column), y + (height * row),
379: width, height);
380:
381: String title = null;
382: if (this.dataExtractOrder == TableOrder.BY_ROW) {
383: title = this.dataset.getRowKey(pieIndex).toString();
384: }
385: else {
386: title = this.dataset.getColumnKey(pieIndex).toString();
387: }
388: this.pieChart.setTitle(title);
389:
390: PieDataset piedataset = null;
391: PieDataset dd = new CategoryToPieDataset(this.dataset,
392: this.dataExtractOrder, pieIndex);
393: if (this.limit > 0.0) {
394: piedataset = DatasetUtilities.createConsolidatedPieDataset(
395: dd, this.aggregatedItemsKey, this.limit);
396: }
397: else {
398: piedataset = dd;
399: }
400: PiePlot piePlot = (PiePlot) this.pieChart.getPlot();
401: piePlot.setDataset(piedataset);
402: piePlot.setPieIndex(pieIndex);
403:
404:
405: for (int i = 0; i < piedataset.getItemCount(); i++) {
406: Comparable key = piedataset.getKey(i);
407: Paint p;
408: if (key.equals(this.aggregatedItemsKey)) {
409: p = this.aggregatedItemsPaint;
410: }
411: else {
412: p = (Paint) this.sectionPaints.get(key);
413: }
414: piePlot.setSectionPaint(i, p);
415: }
416:
417: ChartRenderingInfo subinfo = null;
418: if (info != null) {
419: subinfo = new ChartRenderingInfo();
420: }
421: this.pieChart.draw(g2, rect, subinfo);
422: if (info != null) {
423: info.getOwner().getEntityCollection().addAll(
424: subinfo.getEntityCollection());
425: info.addSubplotInfo(subinfo.getPlotInfo());
426: }
427:
428: ++column;
429: if (column == displayCols) {
430: column = 0;
431: ++row;
432:
433: if (row == displayRows - 1 && diff != 0) {
434: xoffset = (diff * width) / 2;
435: }
436: }
437: }
438:
439: }
440:
441:
447: private void prefetchSectionPaints() {
448:
449:
450:
451:
452: if (this.dataExtractOrder == TableOrder.BY_ROW) {
453:
454: for (int c = 0; c < this.dataset.getColumnCount(); c++) {
455: Comparable key = this.dataset.getColumnKey(c);
456: Paint p = (Paint) this.sectionPaints.get(key);
457: if (p == null) {
458: this.sectionPaints.put(key,
459: this.getDrawingSupplier().getNextPaint());
460: }
461: }
462: }
463: else {
464:
465: for (int r = 0; r < this.dataset.getRowCount(); r++) {
466: Comparable key = this.dataset.getRowKey(r);
467: Paint p = (Paint) this.sectionPaints.get(key);
468: if (p == null) {
469: this.sectionPaints.put(key,
470: this.getDrawingSupplier().getNextPaint());
471: }
472: }
473: }
474:
475: }
476:
477:
482: public LegendItemCollection getLegendItems() {
483:
484: LegendItemCollection result = new LegendItemCollection();
485:
486: if (this.dataset != null) {
487: List keys = null;
488:
489: prefetchSectionPaints();
490: if (this.dataExtractOrder == TableOrder.BY_ROW) {
491: keys = this.dataset.getColumnKeys();
492: }
493: else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
494: keys = this.dataset.getRowKeys();
495: }
496:
497: if (keys != null) {
498: int section = 0;
499: Iterator iterator = keys.iterator();
500: while (iterator.hasNext()) {
501: Comparable key = (Comparable) iterator.next();
502: String label = key.toString();
503: String description = label;
504: Paint paint = (Paint) this.sectionPaints.get(key);
505: LegendItem item = new LegendItem(label, description,
506: null, null, Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
507: paint, Plot.DEFAULT_OUTLINE_STROKE, paint);
508:
509: result.add(item);
510: section++;
511: }
512: }
513: if (this.limit > 0.0) {
514: result.add(new LegendItem(this.aggregatedItemsKey.toString(),
515: this.aggregatedItemsKey.toString(), null, null,
516: Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
517: this.aggregatedItemsPaint,
518: Plot.DEFAULT_OUTLINE_STROKE,
519: this.aggregatedItemsPaint));
520: }
521: }
522: return result;
523: }
524:
525:
534: public boolean equals(Object obj) {
535: if (obj == this) {
536: return true;
537: }
538: if (!(obj instanceof MultiplePiePlot)) {
539: return false;
540: }
541: MultiplePiePlot that = (MultiplePiePlot) obj;
542: if (this.dataExtractOrder != that.dataExtractOrder) {
543: return false;
544: }
545: if (this.limit != that.limit) {
546: return false;
547: }
548: if (!this.aggregatedItemsKey.equals(that.aggregatedItemsKey)) {
549: return false;
550: }
551: if (!PaintUtilities.equal(this.aggregatedItemsPaint,
552: that.aggregatedItemsPaint)) {
553: return false;
554: }
555: if (!ObjectUtilities.equal(this.pieChart, that.pieChart)) {
556: return false;
557: }
558: if (!super.equals(obj)) {
559: return false;
560: }
561: return true;
562: }
563:
564:
571: private void writeObject(ObjectOutputStream stream) throws IOException {
572: stream.defaultWriteObject();
573: SerialUtilities.writePaint(this.aggregatedItemsPaint, stream);
574: }
575:
576:
584: private void readObject(ObjectInputStream stream)
585: throws IOException, ClassNotFoundException {
586: stream.defaultReadObject();
587: this.aggregatedItemsPaint = SerialUtilities.readPaint(stream);
588: this.sectionPaints = new HashMap();
589: }
590:
591:
592: }