KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pentaho > plugin > jfreereport > helper > PentahoHtmlFilesystem


1 /*
2  * Copyright 2006 Pentaho Corporation. All rights reserved.
3  * This software was developed by Pentaho Corporation and is provided under the terms
4  * of the Mozilla Public License, Version 1.1, or any later version. You may not use
5  * this file except in compliance with the license. If you need a copy of the license,
6  * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
7  * BI Platform. The Initial Developer is Pentaho Corporation.
8  *
9  * Software distributed under the Mozilla Public License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
11  * the license for the specific language governing your rights and limitations.
12 */

13 package org.pentaho.plugin.jfreereport.helper;
14
15 import java.awt.Image JavaDoc;
16 import java.io.File JavaDoc;
17 import java.io.FileOutputStream JavaDoc;
18 import java.io.IOException JavaDoc;
19 import java.io.OutputStream JavaDoc;
20 import java.net.URL JavaDoc;
21 import java.text.MessageFormat JavaDoc;
22 import java.util.HashMap JavaDoc;
23
24 import org.jfree.report.ImageContainer;
25 import org.jfree.report.LocalImageContainer;
26 import org.jfree.report.URLImageContainer;
27 import org.jfree.report.modules.output.table.html.HtmlFilesystem;
28 import org.jfree.report.modules.output.table.html.ZIPHtmlFilesystem;
29 import org.jfree.report.modules.output.table.html.ref.EmptyContentReference;
30 import org.jfree.report.modules.output.table.html.ref.ExternalStyleSheetReference;
31 import org.jfree.report.modules.output.table.html.ref.HtmlImageReference;
32 import org.jfree.report.modules.output.table.html.ref.HtmlReference;
33 import org.jfree.report.modules.output.table.html.util.CounterReference;
34 import org.jfree.report.resourceloader.ImageFactory;
35 import org.jfree.report.util.ImageComparator;
36 import org.jfree.report.util.StringUtil;
37 import org.jfree.util.Log;
38 import org.jfree.util.WaitingImageObserver;
39 import org.pentaho.core.repository.IContentLocation;
40 import org.pentaho.core.system.IApplicationContext;
41 import org.pentaho.core.system.PentahoSystem;
42 import org.pentaho.messages.Messages;
43
44 import com.keypoint.PngEncoder;
45
46 /**
47  * Creation-Date: 07.07.2006, 21:10:54
48  *
49  * @author Thomas Morgner
50  */

51 public class PentahoHtmlFilesystem implements HtmlFilesystem
52 {
53 // private IContentLocation dataDirectory;
54
private String JavaDoc actionName;
55
56   public PentahoHtmlFilesystem()
57   {
58   }
59
60   /**
61    * the root stream for writing the main html file.
62    */

63   private OutputStream JavaDoc rootStream;
64
65   /**
66    * A collection of all used file names for generating external content.
67    */

68   private HashMap JavaDoc usedNames;
69
70   /**
71    * A collection of all referenced external content.
72    */

73   private HashMap JavaDoc usedImages;
74
75   /**
76    * A collection of all previously encoded images.
77    */

78   private HashMap JavaDoc encodedImages;
79
80   /**
81    * the image comparator used to compare generated images.
82    */

83   private ImageComparator comparator;
84
85   /**
86    * A flag indicating whether to copy external references into the data directory.
87    */

88   private boolean copyExternalImages;
89
90   /**
91    * A flag defining whether to use the digest image compare method.
92    */

93   private boolean digestImageCompare;
94
95   private MessageFormat JavaDoc contentPathPattern;
96
97   /**
98    * Creates a new ZIPHtmlFilesystem. The given output stream is used to write a generated
99    * Zip-File. The given data directory must denote a relative directory within the ZIP
100    * file.
101    *
102    * @param out the target output stream.
103    * @param dataDirectory the data directory, relative within the ZIP file.
104    * @throws java.io.IOException if an IO error occurs.
105    */

106   public PentahoHtmlFilesystem (final OutputStream JavaDoc out,
107                                 final IContentLocation dataDirectory,
108                                 final String JavaDoc actionName,
109                                 final boolean copyExternalImages,
110                                 final String JavaDoc contentPathPattern)
111   {
112     if (out == null)
113     {
114       throw new NullPointerException JavaDoc();
115     }
116 // if (dataDirectory == null)
117
// {
118
// throw new NullPointerException();
119
// }
120
if (contentPathPattern == null)
121     {
122       throw new NullPointerException JavaDoc();
123     }
124
125     this.actionName = actionName;
126     this.rootStream = out;
127     this.copyExternalImages = copyExternalImages;
128 // this.dataDirectory = dataDirectory;
129

130     this.contentPathPattern = new MessageFormat JavaDoc(contentPathPattern);
131
132     this.usedNames = new HashMap JavaDoc();
133     this.usedImages = new HashMap JavaDoc();
134     this.comparator = new ImageComparator();
135     this.encodedImages = new HashMap JavaDoc();
136   }
137
138   /**
139    * The root stream is used to write the main HTML-File. Any external content is
140    * referenced from this file.
141    *
142    * @return the output stream of the main HTML file.
143    *
144    * @throws IOException if an IO error occured, while providing the root stream.
145    */

146   public OutputStream JavaDoc getRootStream ()
147           throws IOException JavaDoc
148   {
149     return rootStream;
150   }
151
152   public String JavaDoc getActionName()
153   {
154     return actionName;
155   }
156
157   /**
158    * Returns true, if external content should be copied into the DataDirectory of the
159    * ZIPFile or false if the content should be included as linked content.
160    * <p/>
161    * Linked content reduces the filesize, but the reader of the report will need access to
162    * the linked files. If you pan to use the report offline, then it is best to copy all
163    * referenced data into the zip file.
164    *
165    * @return true, if external referenced content should be copied into the ZIP file,
166    * false otherwise.
167    */

168   public boolean isCopyExternalImages ()
169   {
170     return copyExternalImages;
171   }
172
173   /**
174    * Defines, whether external content should be copied into the DataDirectory of the
175    * ZIPFile or should be included as linked content.
176    * <p/>
177    * Linked content reduces the filesize, but the reader of the report will need access to
178    * the linked files. If you pan to use the report offline, then it is best to copy all
179    * referenced data into the zip file.
180    *
181    * @param copyExternalImages true, if external referenced content should be copied into
182    * the ZIP file, false otherwise.
183    */

184   public void setCopyExternalImages (final boolean copyExternalImages)
185   {
186     this.copyExternalImages = copyExternalImages;
187   }
188
189   /**
190    * Tests, whether the given URL points to a supported file format for common browsers.
191    * Returns true if the URL references a JPEG, PNG or GIF image, false otherwise.
192    * <p/>
193    * The checked filetypes are the ones recommended by the W3C.
194    *
195    * @param url the url that should be tested.
196    * @return true, if the content type is supported by the browsers, false otherwise.
197    */

198   protected boolean isSupportedImageFormat (final URL JavaDoc url)
199   {
200     final String JavaDoc file = url.getFile();
201     if (StringUtil.endsWithIgnoreCase(file, ".jpg")) //$NON-NLS-1$
202
{
203       return true;
204     }
205     if (StringUtil.endsWithIgnoreCase(file, ".jpeg")) //$NON-NLS-1$
206
{
207       return true;
208     }
209     if (StringUtil.endsWithIgnoreCase(file, ".png")) //$NON-NLS-1$
210
{
211       return true;
212     }
213     if (StringUtil.endsWithIgnoreCase(file, ".gif")) //$NON-NLS-1$
214
{
215       return true;
216     }
217     return false;
218   }
219
220   /**
221    * Creates a HtmlReference for ImageData. If the target filesystem does not support this
222    * reference type, return an empty content reference, but never null.
223    * <p/>
224    * If the image was generated during the report processing or is not in a commonly
225    * supported format, then the image is recoded as PNG and the recoded image is included
226    * in the data directory.
227    * <p/>
228    * If external referenced data should be copied into the data directory, then the Image
229    * content is read and copied into the data directory.
230    *
231    * @param reference the image reference containing the data.
232    * @return the generated HtmlReference, never null.
233    *
234    * @throws IOException if IO errors occured while creating the reference.
235    * @see ZIPHtmlFilesystem#isSupportedImageFormat
236    */

237   public HtmlReference createImageReference (final ImageContainer reference)
238           throws IOException JavaDoc
239   {
240
241     // The image has an assigned URL ...
242
if (reference instanceof URLImageContainer)
243     {
244       final URLImageContainer urlImage = (URLImageContainer) reference;
245
246       // first we check for cached instances ...
247
final URL JavaDoc url = urlImage.getSourceURL();
248       if (url != null)
249       {
250         final String JavaDoc name = (String JavaDoc) usedImages.get(url);
251         if (name != null)
252         {
253           return new HtmlImageReference(name);
254         }
255
256         // sadly this one seems to be new, not yet cached ...
257
// so we have to create something new ...
258

259         // it is one of the supported image formats ...
260
// we we can embedd it directly ...
261
if (isSupportedImageFormat(url))
262         {
263           // check, whether we should copy the contents into the local
264
// data directory ...
265
// if (isCopyExternalImages() && urlImage.isLoadable())
266
// {
267
// final IOUtils iou = IOUtils.getInstance();
268
// final String entryName = createName(iou.getFileName(url));
269
// final String filename = iou.stripFileExtension(entryName);
270
// final String extension = iou.getFileExtension(entryName);
271
// final String mimeType = getMimeType(extension);
272
// IContentItem item = dataDirectory.newContentItem
273
// (filename, entryName, extension, mimeType,
274
// url.toString(), IContentItem.WRITEMODE_OVERWRITE);
275
// System.out.println("created new contentitem: " + item.getId());
276
// OutputStream out = item.getOutputStream(getActionName());
277
// final InputStream urlIn = new BufferedInputStream(url.openStream());
278
// IOUtils.getInstance().copyStreams(urlIn, out);
279
// urlIn.close();
280
// out.close();
281
//
282
// usedImages.put(url, entryName);
283
// final String entryPath = contentPathPattern.format
284
// (new Object[]{item.getId()});
285
// return new HtmlImageReference(entryPath);
286
// }
287
// else
288
// {
289
final String JavaDoc baseName = urlImage.getSourceURL().toExternalForm();
290             usedImages.put(url, baseName);
291             return new HtmlImageReference(baseName);
292 // }
293
// done: Remote image with supported format
294
}
295
296         // The image is not directly embeddable, so we have to convert it
297
// into a supported format (PNG in this case)
298

299         // if the image is not loadable, we can't do anything ...
300
// print the default empty content instead ...
301
if (urlImage.isLoadable() == false)
302         {
303           return new EmptyContentReference();
304         }
305
306         // Check, whether the imagereference contains an AWT image.
307
Image JavaDoc image = null;
308
309         // The image is not directly embeddable, so we have to convert it
310
// into a supported format (PNG in this case)
311
if (reference instanceof LocalImageContainer)
312         {
313           // if so, then we can use that image instance for the recoding
314
final LocalImageContainer li = (LocalImageContainer) reference;
315           image = li.getImage();
316         }
317         if (image == null)
318         {
319           image = ImageFactory.getInstance().createImage(url);
320         }
321         if (image != null)
322         {
323           // now encode the image. We don't need to create digest data
324
// for the image contents, as the image is perfectly identifyable
325
// by its URL
326
final String JavaDoc entryName = encodeImage(image, false);
327           usedImages.put(url, entryName);
328           return new HtmlImageReference(entryName);
329         }
330       }
331       else if (urlImage.getSourceURLString() != null)
332       {
333         return new HtmlImageReference(urlImage.getSourceURLString());
334       }
335     }
336
337     // check, whether the image is a locally created image
338
// such an image has no assigned URL, so we have to find an
339
// artificial name
340
if (reference instanceof LocalImageContainer)
341     {
342       // that image has no useable URL, but contains local image
343
// data, so we can start to encode it. We will create a
344
// fingerprint of the image contents to cache the image.
345
//
346
// This will not save any time (we,, it even costs more time), but
347
// will help to reduce the space used by the output.
348
//
349
// LocalImageContainer instances are free to supply more
350
// suitable comparator information
351
final LocalImageContainer li = (LocalImageContainer) reference;
352       final Image JavaDoc image = li.getImage();
353       if (image != null)
354       {
355         if (li.isIdentifiable())
356         {
357           final Object JavaDoc identity = li.getIdentity();
358           String JavaDoc name = (String JavaDoc) usedImages.get(identity);
359           if (name == null)
360           {
361             name = encodeImage(image, false);
362             usedImages.put(identity, name);
363           }
364           return new HtmlImageReference(name);
365         }
366         return new HtmlImageReference(encodeImage(image, true));
367       }
368     }
369
370     // it is neither a local nor a URL image container, we don't handle
371
// that..
372
return new EmptyContentReference();
373   }
374
375   private String JavaDoc getMimeType(final String JavaDoc extension)
376   {
377     if ("jpg".equalsIgnoreCase(extension)) //$NON-NLS-1$
378
{
379       return "image/jpeg"; //$NON-NLS-1$
380
}
381     if ("jpeg".equalsIgnoreCase(extension)) //$NON-NLS-1$
382
{
383       return "image/jpeg"; //$NON-NLS-1$
384
}
385     if ("gif".equalsIgnoreCase(extension)) //$NON-NLS-1$
386
{
387       return "image/gif"; //$NON-NLS-1$
388
}
389     if ("png".equalsIgnoreCase(extension)) //$NON-NLS-1$
390
{
391       return "image/png"; //$NON-NLS-1$
392
}
393     return null;
394   }
395
396   /**
397    * Encodes the given image as PNG, stores the image in the generated file and returns
398    * the name of the new image file.
399    *
400    * @param image the image to be encoded
401    * @param createComparator true, if the image creation should be cached to avoid
402    * duplicate images
403    * @return the name of the image, never null.
404    *
405    * @throws IOException if an IO erro occured.
406    */

407   private String JavaDoc encodeImage (final Image JavaDoc image, final boolean createComparator)
408           throws IOException JavaDoc
409   {
410     // quick caching ... use a weak list ...
411
final WaitingImageObserver obs = new WaitingImageObserver(image);
412     obs.waitImageLoaded();
413
414     final PngEncoder encoder = new PngEncoder(image,
415             PngEncoder.ENCODE_ALPHA, PngEncoder.FILTER_NONE, 5);
416     final byte[] data = encoder.pngEncode();
417
418     final Object JavaDoc object;
419     if (createComparator)
420     {
421       object = comparator.createCompareData(data, isDigestImageCompare() == false);
422       final String JavaDoc name = (String JavaDoc) encodedImages.get(object);
423       if (name != null)
424       {
425         return name;
426       }
427     }
428     else
429     {
430       object = null;
431     }
432
433     // write the encoded picture ...
434
final String JavaDoc fileName = createName("picture"); //$NON-NLS-1$
435
final String JavaDoc extension = "png"; //$NON-NLS-1$
436

437     final String JavaDoc entryName = fileName + "." + extension; //$NON-NLS-1$
438

439     IApplicationContext ctx = PentahoSystem.getApplicationContext();
440     File JavaDoc directory = null;
441     if (ctx != null)
442     {
443       directory = new File JavaDoc(ctx.getFileOutputPath("system/tmp/")); //$NON-NLS-1$
444
}
445     else
446     {
447       Log.error(Messages.getErrorString("ABSTRACTCHARTEXPRESSION.ERROR_0006_PENTAHOSYSTEM_UNINITIALIZED"));//$NON-NLS-1$
448
String JavaDoc tempDir = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
449
directory = new File JavaDoc(tempDir);
450     }
451     
452     File JavaDoc file = File.createTempFile("tmp_chart", ".png", directory); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$
453
file.deleteOnExit();
454     
455     OutputStream JavaDoc out = new FileOutputStream JavaDoc(file);
456     out.write(data);
457     out.close();
458
459
460     if (createComparator)
461     {
462       encodedImages.put(object, entryName);
463     }
464
465     if (ctx != null) {
466         return ctx.getBaseUrl() + "getImage?image=" + file.getName(); //$NON-NLS-1$
467
}
468     return null;
469   }
470
471   /**
472    * Creates an unique name for resources in the data directory.
473    *
474    * @param base the basename.
475    * @return the unique name generated using the basename.
476    */

477   private String JavaDoc createName (final String JavaDoc base)
478   {
479     CounterReference ref = (CounterReference) usedNames.get(base);
480     if (ref == null)
481     {
482       ref = new CounterReference();
483       usedNames.put(base, ref);
484       return base;
485     }
486     else
487     {
488       ref.increase();
489       return base + ref.getCount();
490     }
491   }
492
493   /**
494    * Creates a HtmlReference for StyleSheetData. If the target filesystem does not support
495    * external stylesheets, return an inline stylesheet reference.
496    *
497    * @param styleSheet the stylesheet data, which should be referenced.
498    * @return the generated HtmlReference, never null.
499    *
500    * @throws IOException if IO errors occured while creating the reference.
501    */

502   public HtmlReference createCSSReference (final String JavaDoc styleSheet)
503           throws IOException JavaDoc
504   {
505
506     IApplicationContext ctx = PentahoSystem.getApplicationContext();
507     File JavaDoc directory = null;
508     if (ctx != null)
509     {
510       directory = new File JavaDoc(ctx.getFileOutputPath("system/tmp/")); //$NON-NLS-1$
511
}
512     else
513     {
514       Log.error(Messages.getErrorString("ABSTRACTCHARTEXPRESSION.ERROR_0006_PENTAHOSYSTEM_UNINITIALIZED"));//$NON-NLS-1$
515
String JavaDoc tempDir = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
516
directory = new File JavaDoc(tempDir);
517     }
518     
519     File JavaDoc file = File.createTempFile("tmp_chart", ".css", directory); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$
520
file.deleteOnExit();
521     
522     OutputStream JavaDoc out = new FileOutputStream JavaDoc(file);
523     out.write(styleSheet.getBytes());
524     out.close();
525
526     if (ctx != null) {
527         return new ExternalStyleSheetReference(ctx.getBaseUrl() + "getImage?image=" + file.getName()); //$NON-NLS-1$
528
}
529     return null;
530   }
531
532   /**
533    * Close the Filesystem and write any buffered content. The filesystem will not be
534    * accessed, after close was called.
535    *
536    * @throws IOException if the close operation failed.
537    */

538   public void close ()
539           throws IOException JavaDoc
540   {
541     rootStream.flush();
542     rootStream.close();
543   }
544
545   /**
546    * Returns, whether to use digest image compare instead of internal java methods. This
547    * method reduces memory consumption for the price of complexer computations (and
548    * reduced execution speed).
549    *
550    * @return true, if the digest compare should be used, false otherwise.
551    */

552   public boolean isDigestImageCompare ()
553   {
554     return digestImageCompare;
555   }
556
557   /**
558    * Defines, whether to use digest image compare instead of internal java methods. This
559    * method reduces memory consumption for the price of complexer computations (and
560    * reduced execution speed).
561    *
562    * @param digestImageCompare set to true, if the digest compare should be used, false
563    * otherwise.
564    */

565   public void setDigestImageCompare (final boolean digestImageCompare)
566   {
567     this.digestImageCompare = digestImageCompare;
568   }
569
570 }
571
Popular Tags