001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * DrawableLoadFilter.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.filter;
030:
031: import java.io.IOException;
032: import java.io.ObjectOutputStream;
033: import java.io.ObjectInputStream;
034: import java.net.URL;
035: import java.sql.Blob;
036: import java.sql.SQLException;
037: import java.util.HashSet;
038:
039: import org.jfree.report.function.ExpressionRuntime;
040: import org.jfree.report.resourceloader.DrawableFactory;
041: import org.jfree.report.util.KeyedQueue;
042: import org.jfree.util.Log;
043:
044: /**
045: * The DrawableLoadFilter is used to load drawable image files (like WMF's) during the
046: * report generation process. This filter expects its datasource to return a java.net.URL.
047: * If the datasource does not return an URL, <code>null</code> is returned as result of
048: * calling "getValue()".
049: * <p/>
050: * This filter is mostly used in conjunction with the URLFilter, which creates URLs from
051: * Strings and files if nessesary.
052: * <p/>
053: * The url is used to create a new Drawable object which is returned to the caller. The
054: * loaded/created Drawable is also stored in an internal cache.
055: * <p/>
056: * This filter can be used to dynamically change images of a report, a very nice feature
057: * for photo albums and catalogs for instance.
058: * <p/>
059: * This filter will return null, if something else than an URL was retrieved from the
060: * assigned datasource
061: *
062: * @author Thomas Morgner
063: */
064: public class DrawableLoadFilter implements DataFilter {
065: /**
066: * The cache for previously loaded images. If the maximum size of the cache reached,
067: */
068: private transient KeyedQueue imageCache;
069: /**
070: * The cache for failed images. This prevents unneccessary retries on known-to-be-buggy URLs.
071: */
072: private transient HashSet failureCache;
073: /**
074: * The datasource from where to read the urls.
075: */
076: private DataSource source;
077:
078: /**
079: * creates a new ImageLoadFilter with a cache size of 10.
080: */
081: public DrawableLoadFilter() {
082: this (10);
083: }
084:
085: /**
086: * Creates a new ImageLoadFilter with the defined cache size.
087: *
088: * @param cacheSize the cache size.
089: */
090: public DrawableLoadFilter(final int cacheSize) {
091: imageCache = new KeyedQueue(cacheSize);
092: failureCache = new HashSet();
093: }
094:
095: /**
096: * Reads this filter's datasource and if the source returned an URL, tries to form a
097: * imagereference. If the image is loaded in a previous run and is still in the cache,
098: * no new reference is created and the previously loaded reference is returned.
099: *
100: * @param runtime the expression runtime that is used to evaluate formulas and expressions when computing the value of
101: * this filter.
102: * @return the current value for this filter.
103: */
104: public Object getValue(final ExpressionRuntime runtime) {
105: final DataSource ds = getDataSource();
106: if (ds == null) {
107: return null;
108: }
109: final Object o = ds.getValue(runtime);
110: if (o == null) {
111: return null;
112: }
113:
114: if (o instanceof URL) {
115:
116: // a valid url is found, lookup the url in the cache, maybe the image is loaded and
117: // still there.
118: final URL url = (URL) o;
119: final String urlString = String.valueOf(url);
120: Object retval = imageCache.get(urlString);
121: if (retval == null) {
122: if (failureCache.contains(urlString)) {
123: return null;
124: }
125: try {
126: retval = DrawableFactory.getInstance()
127: .createDrawable(url);
128: } catch (IOException ioe) {
129: if (Log.isWarningEnabled()) {
130: Log
131: .warn("Error while loading the drawable from "
132: + url);
133: } else if (Log.isDebugEnabled()) {
134: Log.debug(
135: "Error while loading the drawable from "
136: + url, ioe);
137: }
138: failureCache.add(urlString);
139: return null;
140: }
141: if (retval == null) {
142: return null;
143: }
144: }
145: // update the cache and put the image at the top of the list
146: imageCache.put(urlString, retval);
147: return retval;
148: } else if (o instanceof byte[]) {
149: try {
150: final byte[] data = (byte[]) o;
151: return DrawableFactory.getInstance().createDrawable(
152: data, null, null);
153: } catch (IOException e) {
154: Log
155: .warn(
156: "Error while loading the image from an byte-array",
157: e);
158: }
159: return null;
160:
161: } else if (o instanceof Blob) {
162: try {
163: final Blob b = (Blob) o;
164: final byte[] data = b.getBytes(0, (int) b.length());
165: return DrawableFactory.getInstance().createDrawable(
166: data, null, null);
167: } catch (SQLException e) {
168: Log.warn("Error while loading the image from an blob",
169: e);
170: } catch (IOException e) {
171: Log.warn("Error while loading the image from an blob",
172: e);
173: }
174: return null;
175: } else {
176: return null;
177: }
178: }
179:
180: /**
181: * Returns the data source for the filter.
182: *
183: * @return The data source.
184: */
185: public DataSource getDataSource() {
186: return source;
187: }
188:
189: /**
190: * Sets the data source.
191: *
192: * @param ds The data source.
193: */
194: public void setDataSource(final DataSource ds) {
195: if (ds == null) {
196: throw new NullPointerException();
197: }
198:
199: source = ds;
200: }
201:
202: /**
203: * Clones the filter.
204: *
205: * @return a clone.
206: *
207: * @throws CloneNotSupportedException this should never happen.
208: */
209: public Object clone() throws CloneNotSupportedException {
210: final DrawableLoadFilter il = (DrawableLoadFilter) super
211: .clone();
212: il.imageCache = (KeyedQueue) imageCache.clone();
213: il.failureCache = (HashSet) failureCache.clone();
214: if (source != null) {
215: il.source = (DataSource) source.clone();
216: }
217: return il;
218: }
219:
220: /**
221: * A helper method that is called during the serialization process.
222: *
223: * @param out the serialization output stream.
224: * @throws IOException if an IO error occured.
225: */
226: private void writeObject(final ObjectOutputStream out)
227: throws IOException {
228: out.defaultWriteObject();
229: out.writeInt(imageCache.getLimit());
230: }
231:
232: /**
233: * A helper method that is called during the de-serialization process.
234: *
235: * @param in the serialization input stream.
236: * @throws IOException if an IOError occurs.
237: * @throws ClassNotFoundException if a dependent class cannot be found.
238: */
239: private void readObject(final ObjectInputStream in)
240: throws IOException, ClassNotFoundException {
241: in.defaultReadObject();
242: final int limit = in.readInt();
243: imageCache = new KeyedQueue(limit);
244: failureCache = new HashSet();
245: }
246:
247: }
|