/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.io.services;
import com.sun.lwuit.EncodedImage;
import com.sun.lwuit.Label;
import com.sun.lwuit.List;
import com.sun.lwuit.Image;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.geom.Dimension;
import com.sun.lwuit.io.ConnectionRequest;
import com.sun.lwuit.io.FileSystemStorage;
import com.sun.lwuit.io.NetworkEvent;
import com.sun.lwuit.io.NetworkManager;
import com.sun.lwuit.io.Storage;
import com.sun.lwuit.io.ui.FileEncodedImage;
import com.sun.lwuit.io.ui.StorageImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
/**
* Simplifies the process of implementing an image link for labels and lists by
* binding a request to a component. On the completion of the request a LWUIT
* encoded image is created and installs itself into the given component.
* For the case of a Label this is seamless, in case of a List renderer the model
* or the renderer must register itself as a listener and update the data when
* the response arrives.
*
* @author Shai Almog
*/
public class ImageDownloadService extends ConnectionRequest {
private Label parentLabel;
private EncodedImage result;
private List targetList;
private int targetOffset;
private String targetKey;
private boolean cacheImages;
private String destinationFile;
private Dimension toScale;
private String cacheId;
/**
* Accepts the url to bind to the list renderer, on completion the action listener
* will be invoked with the image so a list can be updated with the data
*
* @param url the image URL
* @param l an action listener callback
*/
public ImageDownloadService(String url, ActionListener l) {
setUrl(url);
addResponseListener(l);
setPost(false);
}
/**
* Constructs an image request that will automatically populate the given list
* when the response arrives. This assumes the GenericListCellRenderer style of
* list which relies on a hashtable based model approach.
*
* @param url the image URL
* @param targetList the list that should be updated when the data arrives
* @param targetOffset the offset within the list to insert the image
* @param targetKey the key for the hashtable in the target offset
*/
public ImageDownloadService(String url, List targetList, int targetOffset, String targetKey) {
this.targetList = targetList;
this.targetKey = targetKey;
this.targetOffset = targetOffset;
setUrl(url);
setPost(false);
}
/**
* Accepts the url to bind to the label, on completion the label will be updated
* and revalidated with the new image.
*
* @param url the image URL
* @param parentLabel the label to update
*/
public ImageDownloadService(String url, Label parentLabel) {
setUrl(url);
this.parentLabel = parentLabel;
setPost(false);
}
/**
* Constructs an image request that will automatically populate the given list
* when the response arrives, it will cache the file locally as a file
* in the file storage.
* This assumes the GenericListCellRenderer style of
* list which relies on a hashtable based model approach.
*
* @param url the image URL
* @param targetList the list that should be updated when the data arrives
* @param targetOffset the offset within the list to insert the image
* @param targetKey the key for the hashtable in the target offset
* @param destFile local file to store the data into the given path
*/
public static void createImageToFileSystem(String url, List targetList, int targetOffset,
String targetKey, String destFile, Dimension toScale) {
Image im = cacheImage(null, destFile);
if (im != null) {
Hashtable h = (Hashtable) targetList.getModel().getItemAt(targetOffset);
if(toScale != null){
im = im.scaled(toScale.getWidth(), toScale.getHeight());
}
h.put(targetKey, im);
targetList.repaint();
return;
}
//image not found on cache go and download from the url
ImageDownloadService i = new ImageDownloadService(url, targetList, targetOffset, targetKey);
i.cacheImages = true;
i.destinationFile = destFile;
i.toScale = toScale;
NetworkManager.getInstance().addToQueue(i);
}
/**
* Constructs an image request that will automatically populate the given list
* when the response arrives, it will cache the file locally as a file
* in the file storage.
* This assumes the GenericListCellRenderer style of
* list which relies on a hashtable based model approach.
*
* @param url the image URL
* @param targetList the list that should be updated when the data arrives
* @param targetOffset the offset within the list to insert the image
* @param targetKey the key for the hashtable in the target offset
* @param cacheId a unique identifier to be used to store the image into storage
* @param scale the scale of the image to put in the List or null
*/
public static void createImageToStorage(String url, List targetList, int targetOffset,
String targetKey, String cacheId, Dimension scale) {
Image im = cacheImage(cacheId, null);
if (im != null) {
Hashtable h = (Hashtable) targetList.getModel().getItemAt(targetOffset);
if(scale != null){
im = im.scaled(scale.getWidth(), scale.getHeight());
}
h.put(targetKey, im);
targetList.repaint();
return;
}
//image not found on cache go and download from the url
ImageDownloadService i = new ImageDownloadService(url, targetList, targetOffset, targetKey);
i.cacheImages = true;
i.cacheId = cacheId;
i.toScale = scale;
NetworkManager.getInstance().addToQueue(i);
}
/**
* Constructs an image request that will automatically populate the given Label
* when the response arrives, it will cache the file locally to the Storage
*
* @param url the image URL
* @param l the Label that should be updated when the data arrives
* to just use storage and the url as the key
* @param cacheId a unique identifier to be used to store the image into storage
* @param toScale the scale dimension or null
*/
public static void createImageToStorage(String url, Label l, String cacheId, Dimension toScale) {
Image im = cacheImage(cacheId, null);
if (im != null) {
if(toScale != null){
im = im.scaled(toScale.getWidth(), toScale.getHeight());
}
l.setIcon(im);
l.repaint();
return;
}
//image not found on cache go and download from the url
ImageDownloadService i = new ImageDownloadService(url, l);
i.setDuplicateSupported(true);
i.cacheImages = true;
i.toScale = toScale;
i.cacheId = cacheId;
NetworkManager.getInstance().addToQueue(i);
}
/**
* Constructs an image request that will automatically populate the given Label
* when the response arrives, it will cache the file locally.
*
* @param url the image URL
* @param callback the callback that should be updated when the data arrives
* @param destFile local file to store the data into the given path
*/
public static void createImageToFileSystem(String url, ActionListener callback, String destFile) {
Image im = cacheImage(null, destFile);
if (im != null) {
callback.actionPerformed(new NetworkEvent(null, im));
return;
}
//image not found on cache go and download from the url
ImageDownloadService i = new ImageDownloadService(url, callback);
i.cacheImages = true;
i.destinationFile = destFile;
NetworkManager.getInstance().addToQueue(i);
}
/**
* Constructs an image request that will automatically populate the given Label
* when the response arrives, it will cache the file locally.
*
* @param url the image URL
* @param callback the callback that should be updated when the data arrives
* @param cacheId a unique identifier to be used to store the image into storage
*/
public static void createImageToStorage(String url, ActionListener callback, String cacheId) {
Image im = cacheImage(cacheId, null);
if (im != null) {
callback.actionPerformed(new NetworkEvent(null, im));
return;
}
//image not found on cache go and download from the url
ImageDownloadService i = new ImageDownloadService(url, callback);
i.cacheImages = true;
i.cacheId = cacheId;
NetworkManager.getInstance().addToQueue(i);
}
private static Image cacheImage(String cacheKey, String destFile) {
if (destFile != null) {
if (FileSystemStorage.getInstance().exists(destFile)) {
FileEncodedImage f = FileEncodedImage.create(destFile, -1, -1);
return f;
}
} else if(cacheKey != null){
if (Storage.getInstance().exists(cacheKey)) {
StorageImage s = StorageImage.create(cacheKey, -1, -1);
return s;
}
}
return null;
}
/**
* @inheritDoc
*/
protected void readResponse(InputStream input) throws IOException {
if(cacheImages) {
if(destinationFile != null) {
result = FileEncodedImage.create(destinationFile, input, -1, -1);
} else {
EncodedImage e = EncodedImage.create(input);
result = StorageImage.create(cacheId, e.getImageData(), -1, -1);
//if the storage has failed create the image from the stream
if(result == null){
result = e;
}
}
} else {
result = EncodedImage.create(input);
}
// trigger an exception in case of an invalid image
result.getWidth();
Image image = result;
if (toScale != null) {
image = image.scaled(toScale.getWidth(), toScale.getHeight());
}
if(parentLabel != null) {
Dimension pref = parentLabel.getPreferredSize();
if(parentLabel.getComponentForm() != null) {
parentLabel.setIcon(image);
Dimension newPref = parentLabel.getPreferredSize();
// if the preferred size changed we need to reflow the UI
// this might not be necessary if the label already had an identically
// sized image in place or has a hardcoded preferred size.
if(pref.getWidth() != newPref.getWidth() || pref.getHeight() != newPref.getHeight()) {
parentLabel.getComponentForm().revalidate();
}
} else {
parentLabel.setIcon(image);
}
parentLabel.repaint();
return;
} else {
if(targetList != null) {
Hashtable h = (Hashtable)targetList.getModel().getItemAt(targetOffset);
h.put(targetKey, image);
targetList.repaint();
}
}
// if this is a list cell renderer component
fireResponseListener(new NetworkEvent(this, result));
}
/**
* Returns the image returned from the server, this method is useful for renderers
*
* @return the result
*/
public EncodedImage getResult() {
return result;
}
}