001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: */
013: package org.pentaho.plugin.jfreereport.reportcharts;
014:
015: import java.awt.BasicStroke;
016: import java.awt.Color;
017: import java.awt.Font;
018: import java.awt.Image;
019: import java.awt.Stroke;
020: import java.io.InputStream;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import javax.imageio.ImageIO;
025:
026: import org.jfree.chart.JFreeChart;
027: import org.jfree.chart.block.BlockBorder;
028: import org.jfree.chart.plot.Plot;
029: import org.jfree.chart.title.LegendTitle;
030: import org.jfree.data.general.AbstractDataset;
031: import org.jfree.data.general.Dataset;
032: import org.jfree.report.function.AbstractExpression;
033: import org.jfree.report.function.Expression;
034: import org.jfree.report.states.LayoutProcess;
035: import org.jfree.resourceloader.ResourceKey;
036: import org.jfree.resourceloader.ResourceManager;
037: import org.jfree.ui.RectangleEdge;
038: import org.jfree.util.Log;
039: import org.pentaho.messages.Messages;
040: import org.pentaho.util.ColorHelper;
041:
042: public abstract class AbstractChartExpression extends
043: AbstractExpression {
044: /**
045: * Used for turning off outline in bar legend
046: */
047: protected static final Stroke EmptyStroke = new BasicStroke(0.0f);
048:
049: private static Map LEGEND_LOCATIONS;
050:
051: private String dataSource;
052:
053: private String title;
054:
055: private String noDataMessage = null;
056:
057: private boolean antiAlias = true;
058:
059: private String legendLocation = "bottom"; //Default added my Ingo Klose 21.12.2006 //$NON-NLS-1$
060:
061: private String titleFont = "SansSerif-BOLD-14"; //$NON-NLS-1$
062:
063: private String labelFont = "SansSerif--8"; //$NON-NLS-1$
064:
065: private String legendFont = "SansSerif--8"; //$NON-NLS-1$
066:
067: private int chartWidth;
068:
069: private int chartHeight;
070:
071: private boolean showBorder;
072:
073: private String borderColor;
074:
075: private String backgroundColor;
076:
077: private boolean drawLegendBorder = true;
078:
079: private boolean showLegend;
080:
081: private boolean threeD;
082:
083: private boolean chartSectionOutline;
084:
085: private boolean useDrawable = true;
086:
087: private String backgroundImage;
088:
089: private String chartDirectory;
090:
091: private String chartFile;
092:
093: private String chartUrlMask;
094:
095: // For backward Compatibility only - now ignored by the engine.
096: private boolean returnFileNameOnly;
097: private boolean returnImageReference;
098:
099: private HashMap chartCache = new HashMap();
100:
101: // cache the images, since we roll through here multiple times...
102: private Image plotImageCache = null;
103:
104: protected AbstractChartExpression() {
105: }
106:
107: protected RectangleEdge translateEdge(String edge) {
108: if (LEGEND_LOCATIONS == null) {
109: LEGEND_LOCATIONS = new HashMap();
110: LEGEND_LOCATIONS.put("left", RectangleEdge.LEFT); //$NON-NLS-1$
111: LEGEND_LOCATIONS.put("west", RectangleEdge.LEFT); //$NON-NLS-1$
112: LEGEND_LOCATIONS.put("right", RectangleEdge.RIGHT); //$NON-NLS-1$
113: LEGEND_LOCATIONS.put("east", RectangleEdge.RIGHT); //$NON-NLS-1$
114: LEGEND_LOCATIONS.put("top", RectangleEdge.TOP); //$NON-NLS-1$
115: LEGEND_LOCATIONS.put("north", RectangleEdge.TOP); //$NON-NLS-1$
116: LEGEND_LOCATIONS.put("bottom", RectangleEdge.BOTTOM); //$NON-NLS-1$
117: LEGEND_LOCATIONS.put("south", RectangleEdge.BOTTOM); //$NON-NLS-1$
118: }
119: final RectangleEdge translatedEdge = (RectangleEdge) LEGEND_LOCATIONS
120: .get(edge);
121: if (translatedEdge != null) {
122: return translatedEdge;
123: }
124: return RectangleEdge.LEFT;
125: }
126:
127: public String getTitleFont() {
128: return titleFont;
129: }
130:
131: public void setTitleFont(final String value) {
132: this .titleFont = value;
133: }
134:
135: public String getLegendFont() {
136: return legendFont;
137: }
138:
139: public void setLegendFont(String value) {
140: legendFont = value;
141: }
142:
143: public String getLabelFont() {
144: return labelFont;
145: }
146:
147: public void setLabelFont(final String value) {
148: this .labelFont = value;
149: }
150:
151: public void setChartDirectory(String value) {
152: chartDirectory = value;
153: }
154:
155: public void setChartFile(String value) {
156: chartFile = value;
157: }
158:
159: public void setChartUrlMask(String value) {
160: chartUrlMask = value;
161: }
162:
163: public String getChartDirectory() {
164: return chartDirectory;
165: }
166:
167: public String getChartFile() {
168: return chartFile;
169: }
170:
171: public String getChartUrlMask() {
172: return chartUrlMask;
173: }
174:
175: public String getDataSource() {
176: return dataSource;
177: }
178:
179: public void setDataSource(final String dataSource) {
180: this .dataSource = dataSource;
181: }
182:
183: public String getTitle() {
184: return getPossibleExpressionStringValue(title);
185: }
186:
187: //
188: // For backward compatibility only - the engine doesn't use these anymore
189: //
190: public boolean isReturnFileNameOnly() {
191: return returnFileNameOnly;
192: }
193:
194: public void setReturnFileNameOnly(boolean value) {
195: returnFileNameOnly = value;
196: }
197:
198: public String getPossibleExpressionStringValue(String lookupValue) {
199: if (lookupValue == null) {
200: return null;
201: }
202:
203: Object maybeExpression = null;
204: try {
205: maybeExpression = getDataRow().get(lookupValue);
206: } catch (Exception ignored) {
207: // ignore the expression
208: }
209: if (maybeExpression != null) {
210: return maybeExpression.toString();
211: } else {
212: return lookupValue;
213: }
214: }
215:
216: public void setTitle(final String title) {
217: this .title = title;
218: }
219:
220: public void setChartWidth(int value) {
221: chartWidth = value;
222: }
223:
224: public int getChartWidth() {
225: return chartWidth;
226: }
227:
228: public void setChartHeight(int value) {
229: chartHeight = value;
230: }
231:
232: public int getChartHeight() {
233: return chartHeight;
234: }
235:
236: public boolean isAntiAlias() {
237: return antiAlias;
238: }
239:
240: public void setAntiAlias(final boolean value) {
241: antiAlias = value;
242: }
243:
244: public String getBorderColor() {
245: return borderColor;
246: }
247:
248: public void setBorderColor(String value) {
249: borderColor = value;
250: }
251:
252: public String getBackgroundColor() {
253: return backgroundColor;
254: }
255:
256: public void setBackgroundColor(String value) {
257: backgroundColor = value;
258: }
259:
260: public boolean isShowBorder() {
261: return showBorder;
262: }
263:
264: public void setShowBorder(boolean value) {
265: showBorder = value;
266: }
267:
268: public String getLegendLocation() {
269: return legendLocation;
270: }
271:
272: public void setLegendLocation(String value) {
273: legendLocation = value;
274: }
275:
276: public boolean isDrawLegendBorder() {
277: return drawLegendBorder;
278: }
279:
280: public void setDrawLegendBorder(boolean value) {
281: drawLegendBorder = value;
282: }
283:
284: public boolean isShowLegend() {
285: return showLegend;
286: }
287:
288: public void setShowLegend(final boolean value) {
289: showLegend = value;
290: }
291:
292: public boolean isThreeD() {
293: return threeD;
294: }
295:
296: public void setThreeD(final boolean value) {
297: threeD = value;
298: }
299:
300: public boolean isChartSectionOutline() {
301: return chartSectionOutline;
302: }
303:
304: public void setChartSectionOutline(final boolean value) {
305: chartSectionOutline = value;
306: }
307:
308: public boolean isUseDrawable() {
309: return useDrawable;
310: }
311:
312: public void setUseDrawable(boolean value) {
313: useDrawable = value;
314: }
315:
316: public void setBackgroundImage(String value) {
317: this .backgroundImage = value;
318: }
319:
320: public String getBackgroundImage() {
321: return this .backgroundImage;
322: }
323:
324: public JFreeChart getChartFromCache(Object key) {
325: final Object o = chartCache.get(key);
326: JFreeChart chart = null;
327: if (o != null) {
328: chart = (JFreeChart) o;
329: }
330: return chart;
331: }
332:
333: public void putChartInCache(JFreeChart chart, Object key) {
334: chartCache.put(key, chart);
335: }
336:
337: public Object getValue() {
338: final Object maybeCollector = getDataRow().get(getDataSource());
339: if (!(maybeCollector instanceof ICollectorFunction)) {
340: Log
341: .debug(Messages
342: .getString("CATEGORICALCHARTEXPRESSION.USER_NOT_A_DATASET")); //$NON-NLS-1$
343: return null;
344: }
345:
346: final ICollectorFunction collector = (ICollectorFunction) maybeCollector;
347: AbstractDataset dataset = (AbstractDataset) collector
348: .getDatasourceValue();
349:
350: // TODO: check for dataset.getRowCount() == 0
351: if (dataset == null) {
352: return null;
353: }
354:
355: Object key = collector.getCacheKey() != null ? collector
356: .getCacheKey() : getName();
357:
358: final Object o = getChartFromCache(key);
359: JFreeChart chart = null;
360: if (o != null) {
361: chart = (JFreeChart) o;
362: } else {
363: chart = getChart(dataset);
364: putChartInCache(chart, key);
365: }
366: setChartProperties(chart);
367: return getValue(chart);
368: }
369:
370: public abstract JFreeChart getChart(Dataset dataset);
371:
372: /**
373: * @deprecated Deprecating every return value except for the chart object.
374: * This deprecates the use of the imageurl-field as a valid means of displaying
375: * a chart built from an expression in a JFreeReport. This was taken out per
376: * Marc, as the caching is wrong for the temp files written, and is so fragile
377: * that we spend oober amounts of time in here every time we try to address the break.
378: * Its not worth maintaining, as it was a bad implementation to begin with.
379: *
380: * @param chart
381: * @return chart
382: */
383: protected Object getValue(JFreeChart chart) {
384: if (useDrawable) {
385: return chart;
386: }
387:
388: // Otherwise, the report writer has set drawable=false. We assume this to
389: // mean that they are attempting to use an imageurl-field to display the
390: // chart, which is no longer supported.
391:
392: // Write a decent explanation into the log, then throw an IllegalArgumentException
393:
394: Log
395: .error(Messages
396: .getErrorString("ABSTRACTCHARTEXPRESSION.ERROR_0008_ERROR_IMAGEURL_FIELD_DEPRECATION")); //$NON-NLS-1$
397: Log
398: .error(Messages
399: .getErrorString("ABSTRACTCHARTEXPRESSION.ERROR_0009_ERROR_USE_DRAWABLE")); //$NON-NLS-1$
400: throw new IllegalArgumentException(
401: Messages
402: .getErrorString("ABSTRACTCHARTEXPRESSION.ERROR_0009_ERROR_USE_DRAWABLE2")); //$NON-NLS-1$
403: }
404:
405: protected void setChartProperties(JFreeChart chart) {
406: // Misc Properties
407: Font tFont = Font.decode(getTitleFont());
408:
409: if (chart.getTitle() != null)
410: chart.getTitle().setFont(tFont);
411:
412: if (!isAntiAlias()) {
413: chart.setAntiAlias(false);
414: }
415: chart.setBorderVisible(isShowBorder());
416: Color backCol = getColorFromString(backgroundColor);
417: if (backCol != null) {
418: chart.setBackgroundPaint(backCol);
419: }
420:
421: Color borderCol = getColorFromString(borderColor);
422: if (borderCol != null) {
423: chart.setBorderPaint(borderCol);
424: }
425:
426: //remove legend if showLegend = false
427: if (!isShowLegend()) {
428: chart.removeLegend();
429: }
430: //if true format legend
431: else {
432: LegendTitle chLegend = chart.getLegend();
433: if (chLegend != null) {
434: RectangleEdge loc = translateEdge(legendLocation
435: .toLowerCase());
436: if (loc != null) {
437: chLegend.setPosition(loc);
438: }
439: if (getLegendFont() != null) {
440: chLegend.setItemFont(Font.decode(getLegendFont()));
441: }
442: if (!isDrawLegendBorder()) {
443: chLegend.setBorder(BlockBorder.NONE);
444: }
445: }
446: }
447: Plot plot = chart.getPlot();
448:
449: plot.setNoDataMessage(getNoDataMessage());
450:
451: if (!isChartSectionOutline()) {
452: plot.setOutlineStroke(EmptyStroke);
453: }
454: if (backgroundImage != null) {
455: if (plotImageCache != null) {
456: plot.setBackgroundImage(plotImageCache);
457: } else {
458: ResourceKey contentBase = getRuntime()
459: .getProcessingContext().getContentBase();
460: ResourceManager manager = getRuntime()
461: .getProcessingContext().getResourceManager();
462: try {
463: ResourceKey key = manager.deriveKey(contentBase,
464: backgroundImage);
465: InputStream is = manager.load(key)
466: .getResourceAsStream(manager);
467: Image image = ImageIO.read(is);
468: plot.setBackgroundImage(image);
469: plotImageCache = image;
470: } catch (Exception e) {
471: Log
472: .error(
473: Messages
474: .getErrorString("ABSTRACTCHARTEXPRESSION.ERROR_0007_ERROR_RETRIEVING_PLOT_IMAGE"), e); //$NON-NLS-1$
475: throw new IllegalStateException(e);
476: }
477: }
478: }
479: }
480:
481: protected Color getColorFromString(String colStr) {
482: if (colStr == null) {
483: return null;
484: }
485:
486: try {
487: return Color.decode(colStr);
488: } catch (NumberFormatException ex) {
489: // Ignored - try other parser...
490: }
491: return ColorHelper.lookupColor(colStr);
492:
493: }
494:
495: public Expression getInstance() {
496: final AbstractChartExpression instance = (AbstractChartExpression) super
497: .getInstance();
498: return instance;
499: }
500:
501: //
502: // For backward compatibility only - the engine doesn't use these anymore
503: //
504: public boolean isReturnImageReference() {
505: return returnImageReference;
506: }
507:
508: public void setReturnImageReference(
509: final boolean returnImageReference) {
510: this .returnImageReference = returnImageReference;
511: }
512:
513: public String getNoDataMessage() {
514: return this .noDataMessage != null ? this .noDataMessage
515: : Messages.getString("CHART.USER_NO_DATA_AVAILABLE"); //$NON-NLS-1$
516: }
517:
518: public void setNoDataMessage(String noDataMessage) {
519: this .noDataMessage = noDataMessage;
520: }
521:
522: /**
523: * Overrides the dependency level to only execute this function on the pagination and content-generation level.
524: * @return LayoutProcess.LEVEL_PAGINATE.
525: */
526: public int getDependencyLevel() {
527: return LayoutProcess.LEVEL_PAGINATE;
528: }
529:
530: public void setDependencyLevel(int ignored) {
531: // do nothing
532: }
533:
534: }
|