/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.application; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.gov.ga.earthsci.common.ui.util.ILoadingIconFrameListener; import au.gov.ga.earthsci.common.ui.util.LoadingIconAnimator; import au.gov.ga.earthsci.common.ui.viewers.IFireableLabelProvider; import au.gov.ga.earthsci.core.retrieve.IRetrieval; import au.gov.ga.earthsci.core.retrieve.IRetrievalData; import au.gov.ga.earthsci.core.retrieve.RetrievalAdapter; import au.gov.ga.earthsci.core.retrieve.RetrievalServiceFactory; /** * Helper class which provides the ability to load icons for structured viewers, * and show the loading icon in the meantime. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class IconLoader implements ILoadingIconFrameListener { private static final Logger logger = LoggerFactory.getLogger(IconLoader.class); private final IFireableLabelProvider labelProvider; private final ImageRegistry imageRegistry = new ImageRegistry(); private final Set<Object> loadingElements = new HashSet<Object>(); private final Set<Object> errorElements = new HashSet<Object>(); private final Map<URL, Set<Object>> urlElements = new HashMap<URL, Set<Object>>(); private final Object semaphore = new Object(); public IconLoader(IFireableLabelProvider labelProvider) { this.labelProvider = labelProvider; } /** * Retrieve an icon for a viewer element from the given url. If the url * hasn't yet been retrieved, a frame of the loading icon will be returned. * If the url could not be retrieved, an error icon is returned. * * @param element * Viewer element * @param url * Icon url * @return Icon to be displayed */ public Image getImage(final Object element, URL url) { synchronized (semaphore) { //first check the registry to see if the image has been loaded yet Image image = getImageForURL(url); if (image != null) { setLoading(element, false); return image; } //if an error occured when loading the icon, return the error icon if (errorElements.contains(element)) { return au.gov.ga.earthsci.application.ImageRegistry.getInstance().get( au.gov.ga.earthsci.application.ImageRegistry.ICON_ERROR); } //if its not already loading, begin loading if (!isLoading(element)) { scheduleRetrievalJob(element, url); setLoading(element, true); } return LoadingIconAnimator.get().getCurrentFrame(); } } public void dispose() { imageRegistry.dispose(); } private void scheduleRetrievalJob(Object element, final URL url) { if (!urlElements.containsKey(url)) { urlElements.put(url, new HashSet<Object>()); final IRetrieval retrieval = RetrievalServiceFactory.getServiceInstance().retrieve(this, url); retrieval.addListener(new RetrievalAdapter() { @Override public void cached(IRetrieval retrieval) { retrievalDone(retrieval.getData(), url); } @Override public void complete(IRetrieval retrieval) { if (retrieval.hasResult() && !retrieval.getResult().isFromCache()) { retrievalDone(retrieval.getData(), url); } } }); retrieval.start(); } urlElements.get(url).add(element); } private void retrievalDone(IRetrievalData data, URL url) { Image image = null; if (data != null) { try { InputStream is = data.getInputStream(); try { image = new Image(Display.getDefault(), is); } finally { is.close(); } } catch (Exception e) { logger.error("Error loading image from " + url, e); //$NON-NLS-1$ } } Object[] loadingElementsArray = null; synchronized (semaphore) { final Set<Object> elements = urlElements.remove(url); for (Object element : elements) { setLoading(element, false); } if (image != null) { setImageForURL(url, image); } else { errorElements.addAll(elements); } loadingElementsArray = elements.toArray(); } final Object[] array = loadingElementsArray; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { labelProvider.fireLabelProviderChanged(new LabelProviderChangedEvent(labelProvider, array)); } }); } private Image getImageForURL(URL url) { return imageRegistry.get(url.toString()); } private void setImageForURL(URL url, Image image) { imageRegistry.put(url.toString(), image); } private boolean isLoading(Object element) { return loadingElements.contains(element); } private void setLoading(Object element, boolean loading) { if (loading) { boolean wasEmpty = loadingElements.isEmpty(); loadingElements.add(element); if (wasEmpty) { LoadingIconAnimator.get().addListener(this); } } else { loadingElements.remove(element); boolean isEmpty = loadingElements.isEmpty(); if (isEmpty) { LoadingIconAnimator.get().removeListener(this); } } } @Override public void nextFrame(Image image) { Object[] loadingElementsArray = null; synchronized (semaphore) { if (!loadingElements.isEmpty()) { loadingElementsArray = loadingElements.toArray(new Object[loadingElements.size()]); } } if (loadingElementsArray != null) { final Object[] elements = loadingElementsArray; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { labelProvider.fireLabelProviderChanged(new LabelProviderChangedEvent(labelProvider, elements)); } }); } } }