Package org.locationtech.udig.catalog.ui

Source Code of org.locationtech.udig.catalog.ui.ResolveTitlesDecorator$LabelData

/*
*    uDig - User Friendly Desktop Internet GIS client
*    http://udig.refractions.net
*    (C) 2004, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*
*/
package org.locationtech.udig.catalog.ui;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;

import org.locationtech.udig.catalog.CatalogPlugin;
import org.locationtech.udig.catalog.IForward;
import org.locationtech.udig.catalog.IGeoResource;
import org.locationtech.udig.catalog.IProcess;
import org.locationtech.udig.catalog.IResolve;
import org.locationtech.udig.catalog.IResolveChangeEvent;
import org.locationtech.udig.catalog.IResolveChangeListener;
import org.locationtech.udig.catalog.IResolveDelta;
import org.locationtech.udig.catalog.IResolveFolder;
import org.locationtech.udig.catalog.ISearch;
import org.locationtech.udig.catalog.IService;
import org.locationtech.udig.catalog.IResolveChangeEvent.Type;
import org.locationtech.udig.catalog.IResolveDelta.Kind;
import org.locationtech.udig.catalog.IServiceInfo;
import org.locationtech.udig.catalog.ui.internal.Messages;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.IColorDecorator;
import org.eclipse.jface.viewers.IFontDecorator;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;

/**
* Decorate labels with actual titles from the info objects.
*
* @author jgarnett
* @since 0.6.0
*/
public class ResolveTitlesDecorator implements ILabelDecorator, IColorDecorator, IFontDecorator {

    final static Set<ILabelProviderListener> listeners = new CopyOnWriteArraySet<ILabelProviderListener>();
    final Set<ILabelProviderListener> instanceListeners = new HashSet<ILabelProviderListener>();

    private ResolveLabelProviderSimple source;

    private final Map<IResolve, LabelData> decorated = new IdentityHashMap<IResolve, LabelData>();
    private final Map<IResolve, LabelData> images = new IdentityHashMap<IResolve, LabelData>();
    private final Map<IResolve, ImageDescriptor> imageDescriptorCache=new IdentityHashMap<IResolve, ImageDescriptor>();

    final Queue<IResolve> toDecorate = new ConcurrentLinkedQueue<IResolve>();
    final Queue<IResolve> imagesToDecorate = new ConcurrentLinkedQueue<IResolve>();
   
    final UpdateLabel textWorker = new UpdateLabel(toDecorate, decorated, true);
    final Queue<UpdateLabel> availableImageWorkers = new ConcurrentLinkedQueue<UpdateLabel>();
    final List<UpdateLabel> allImageWorkers=new ArrayList<UpdateLabel>();


    private volatile boolean disposed;
   
    private Font brokenFont;

    private IResolveChangeListener listener=new IResolveChangeListener(){

        public void changed( IResolveChangeEvent event ) {
            if (PlatformUI.getWorkbench().isClosing())
                return;
            if (event.getType() == Type.POST_CHANGE && event.getDelta() != null) {
                final List<IResolve> changed = new ArrayList<IResolve>();
                //  TODO enable when resolve information is available
//                updateCache(event.getDelta(), changed);

                if (!changed.isEmpty()) {
                    Runnable runnable = new Runnable(){
                        public void run() {
                            LabelProviderChangedEvent labelEvent = new LabelProviderChangedEvent(source,
                                    changed.toArray());
                            for( ILabelProviderListener l : listeners ) {
                                l.labelProviderChanged(labelEvent);
                            }
                           
                        }
                    };
                   
                    if( Display.getCurrent()==null ){
                        Display.getDefault().asyncExec(runnable);
                    }else{
                        runnable.run();
                    }
                }

            }
        }

        private void updateCache( IResolveDelta delta, List<IResolve> changed ) {

            if (delta.getKind() == Kind.REPLACED || delta.getKind() == Kind.REMOVED
                    || delta.getKind() == Kind.CHANGED) {
                if (delta.getResolve() != null) {
                    decorated.remove(delta.getResolve());
                    changed.add(delta.getResolve());
                }
                List<IResolveDelta> children = delta.getChildren();
                for( IResolveDelta delta2 : children ) {
                    updateCache(delta2, changed);
                }
            }
        }

    };
   
    /**
     * Prevent fetching of remote icons (localhost and files are okay)
     */
    private boolean decorateImages;

    /**
     * Wrap around the ResolveLabelProviderSimple and add some state markup.
     *
     * @param resolveLabelProviderSimple
     */
    public ResolveTitlesDecorator( ResolveLabelProviderSimple resolveLabelProviderSimple ) {
        this( resolveLabelProviderSimple, false);
    }
    /**
     * @param resolveLabelProviderSimple
     * @param decorateImages
     */
    public ResolveTitlesDecorator( ResolveLabelProviderSimple resolveLabelProviderSimple, boolean decorateImages ) {
        this.source = resolveLabelProviderSimple;
        this.decorateImages=decorateImages;
        CatalogPlugin.addListener(listener);
        CatalogPlugin.removeListener(source);
        for( int i = 0; i < 4; i++ ) {
            UpdateLabel worker = new UpdateLabel(imagesToDecorate, images, false);
            availableImageWorkers.add(worker);
            allImageWorkers.add(worker);
        }
    }
    /*
     * @see org.eclipse.jface.viewers.ILabelDecorator#decorateImage(org.eclipse.swt.graphics.Image,
     *      java.lang.Object)
     */
    public Image decorateImage( Image image, Object element ) {
       if( disposed ) return null;
        if (!(element instanceof IResolve))
            return null;

        IResolve resolve = (IResolve) element;
       
        if (images.containsKey(element)) {
            LabelData data = images.get(element);
            // if data is null then it is being loaded already so return
            if (data == null)
                return null;
        }

        // look image up in registry to see if it is already loaded
        ImageRegistry imageRegistry = CatalogUIPlugin.getDefault().getImageRegistry();
        Image i;
        synchronized (imageRegistry) {
            i = imageRegistry.get(resolve.getIdentifier().toString());
            // if it is loaded and not disposed then we're good, return it.
            if( i!=null && !i.isDisposed()){
                return i;
            }
           
            if (i!=null && i.isDisposed() )
                imageRegistry.remove(resolve.getIdentifier().toString());
        }
       
        // we tried to look up a cached version... If not around and viewer doesn't want decorated images then we'll return.
       
        if( !resolve.getID().isLocal() && !decorateImages ){
            return null;
        }
       
        // put an element in the map so that it will not be loaded again.
        images.put(resolve, null);

        // put resolve in queue for loading
        imagesToDecorate.offer(resolve);
        synchronized (imageRegistry) {
            // get a worker for loading images and schedule it. 
            // If pool is empty then don't worry request will be processed.
            UpdateLabel imageWorker = availableImageWorkers.poll();
            if( imageWorker!=null )
                imageWorker.schedule();
        }
        return null;

    }

    public String decorateText( String text, Object element ) {
        if( disposed ) return null;
        if (!(element instanceof IResolve)){
            return null;
        }

        IResolve resolve = (IResolve) element;
       
        if (decorated.containsKey(element)) {
            LabelData data = decorated.get(element);
            if (data == null){
                return null;
            }
            if( resolve instanceof IService ){
                if( resolve.getID().getTypeQualifier() != null ){
                    return data.text + " ("+resolve.getID().getTypeQualifier() +")";
                }
            }
            return data.text;           
        }

        decorated.put(resolve, null);
        if (resolve.getTitle() != null) {
            LabelData data = new LabelData();
            data.text = resolve.getTitle();
            decorated.put(resolve, data);      
            if( resolve instanceof IService ){
                if( resolve.getID().getTypeQualifier() != null ){
                    return data.text + " ("+resolve.getID().getTypeQualifier() +")";
                }
            }
            return data.text;           
        }
        toDecorate.offer(resolve);
        textWorker.schedule();
        return null;
    }

    public Color decorateBackground( Object element ) {
        return null;
    }
    public Color decorateForeground( Object element ) {
        if( disposed ) return null;
        if (!(element instanceof IResolve))
            return null;

        IResolve resolve = (IResolve) element;
        if (resolve.getStatus() == org.locationtech.udig.catalog.IResolve.Status.BROKEN) {
            return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
        }
        if (resolve.getStatus() == org.locationtech.udig.catalog.IResolve.Status.RESTRICTED_ACCESS) {
            return Display.getCurrent().getSystemColor(SWT.COLOR_DARK_YELLOW);
        }
        return null;
    }
   
    public Font decorateFont( Object element ) {
        if (!(element instanceof IResolve))
            return null;

        IResolve resolve = (IResolve) element;
        if (resolve.getStatus() == org.locationtech.udig.catalog.IResolve.Status.BROKEN
                || resolve.getStatus() == org.locationtech.udig.catalog.IResolve.Status.RESTRICTED_ACCESS) {
            return getBrokenFont();
        }
        return null;
    }

    private Font getBrokenFont() {
        if( brokenFont==null ){
            Font systemFont = Display.getCurrent().getSystemFont();
            FontData fd = systemFont.getFontData()[0];
            brokenFont=new Font(systemFont.getDevice(), fd.getName(), fd.getHeight(), SWT.ITALIC );
        }
       
        return brokenFont;
    }
    /**
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
     */
    public void dispose() {
        assert Display.getCurrent()!=null;
        disposed=true;
        toDecorate.clear();
        textWorker.cancel();
        images.clear();
        for( UpdateLabel updater : allImageWorkers ) {
            updater.cancel();
        }
        // clean up
        decorated.clear();
        listeners.removeAll(instanceListeners);
        instanceListeners.clear();
        CatalogPlugin.removeListener(listener);
        if( brokenFont!=null )
            brokenFont.dispose();
    }

    /**
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
     *      java.lang.String)
     */
    public boolean isLabelProperty( Object element, String property ) {
        return true;
    }
    public ILabelProvider getSource() {
        return source;
    }

    /**
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
     */
    public void removeListener( ILabelProviderListener listener ) {
        assert Display.getCurrent()!=null;
        listeners.remove(listener);
        instanceListeners.remove(listener);
    }

    /*
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
     */
    public void addListener( ILabelProviderListener listener ) {
        assert Display.getCurrent()!=null;
        listeners.add(listener);
        instanceListeners.add(listener);
    }

    /**
     * This Job executes in the background and tries to update the labels.
     * <p>
     * It can actually get "stuck" on any bad label or icon (such as a WMS that takes a while); so
     * it is important to have default titles that work okay.
     */
    private class UpdateLabel extends Job {

        DisplayUpdater updater;
        private Queue<IResolve> toDecorate;
        private Map<IResolve, LabelData> decorated;
        private boolean text;
       
        public UpdateLabel(Queue<IResolve> toDecorate, Map<IResolve, LabelData> decorated, boolean text) {
            // message is just for debugging since job is a system job
            super("Decorate Resolve "+(text?"Titles":"Images")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            setSystem(true);
            setPriority(DECORATE);
            this.toDecorate=toDecorate;
            this.decorated=decorated;
            this.text=text;
        }

        public IStatus run( final IProgressMonitor monitor ) {
            updater=new DisplayUpdater(monitor);
            try{
                while( true ) {
                    final IResolve element = toDecorate.poll();
                    if(element == null) break;
                    // check if some how request has already been fullfilled.
                    LabelData labelData = decorated.get(element);
                    if (text && labelData != null)
                        continue;
                    if (!text && labelData!=null)
                        continue;
                   
                    URL identifier = element.getIdentifier();
                    monitor.beginTask(Messages.ResolveTitlesDecorator_0 + identifier.getFile(),
                            IProgressMonitor.UNKNOWN);
                    if( monitor.isCanceled() ){
                        return Status.OK_STATUS;
                    }
                    LabelData data = new LabelData();
                    try {
                        if(text){
                          if(element instanceof IGeoResource) {
                            IGeoResource resource = (IGeoResource) element;
                            data.text = resource.getInfo(monitor).getTitle();
                            IService service = resource.service(monitor);
                            if( service != null ){
                                service.getPersistentProperties().put(resource.getID() + "_title", data.text);
                            }
                          } else if(element instanceof IService) {
                            IService service = (IService) element;
                            IServiceInfo info = service.getInfo(monitor);
                            if( info != null ){
                                data.text = info.getTitle();
                                service.getPersistentProperties().put("title", data.text);
                            }
                          } else if(element instanceof IProcess) {
                            IProcess proc = (IProcess) element;
                            data.text = proc.getInfo(monitor).getTitle();
                          } else if(element instanceof ISearch) {
                            ISearch search = (ISearch) element;
                            data.text = search.getInfo(monitor).getTitle();
                          } else {
                            IResolveFolder folder = (IResolveFolder) element;
                            data.text = folder.getID().toString();
                          }
                        }
                    } catch (Throwable e) {
                        CatalogUIPlugin.log("Error fetching the Title for the resource", e); //$NON-NLS-1$
                        continue;
                    }
                    try {
                        if(!text)
                            creatImage(data,element);
                    } catch (Throwable e) {
                        CatalogUIPlugin.log("Error fetching the Image for the resource", e); //$NON-NLS-1$
                        continue;
                    }
                    decorated.put(element, data);
                    if( monitor.isCanceled() )
                        return Status.OK_STATUS;
                   
                    addUpdate(monitor, element);
   
                }
                return Status.OK_STATUS;
            }finally{
                if( !text ){
                    // need to protect the items put in the image registry
                    // in addition to availableImageWorkers
                    synchronized (images) {
                        availableImageWorkers.add(this);
                    }
                }
            }

        }

        /**
         *
         * @param monitor
         * @param element
         */
        private void addUpdate( final IProgressMonitor monitor, final IResolve element ) {
            // we are processing as many resolve as possible.
            // so there is a possibility that we will resolve a few
            // before the view is updated so batch the updates
            // since the updates are in the updater thread
            // we have to synchronize on the updates list.
            synchronized (updater.updates) {
                if (updater.updates.isEmpty()){
                    updater=new DisplayUpdater(monitor);
                   
                    updater.updates.add(element);
                    final Display display = Display.getDefault();
                    display.asyncExec(new Runnable(){
                        public void run() {
                            display.timerExec(500, updater);
                        }
                    });
                }else{
                    updater.updates.add(element);
                }
            }
        }

        private Image creatImage( LabelData data, final IResolve element ) throws IOException {
            String key = element.getIdentifier().toString();
            ImageRegistry imageRegistry = CatalogUIPlugin.getDefault().getImageRegistry();
            // need to protect the items put in the image registry
            // in addition to availableImageWorkers
            Image i;
            synchronized (imageRegistry) {
                i = imageRegistry.get(key);
            }
            if (i==null || i.isDisposed()){
                data.image = CatalogUIPlugin.icon(element);
                imageDescriptorCache.put(element, data.image);
                try{
                    if( data.image!=null)
                        i = data.image.createImage();
                }catch (Throwable e) {
                    CatalogUIPlugin.log("Error creating the Image for the resource", e); //$NON-NLS-1$
                }
            }else{
                data.image=imageDescriptorCache.get(element);// get cached
            }
            if( i==null )
                i=CatalogUIPlugin.image(element);
            synchronized (imageRegistry) {
                Image i2 = imageRegistry.get(key);
                if( i2!=null && !i2.isDisposed()){
                    return i;
                }
               
                if (i2!=null && i2.isDisposed() )
                    imageRegistry.remove(key);
               
                imageRegistry.put(key, i);
            }
            return i;
        }

    }
   
    private class DisplayUpdater implements Runnable{

        IProgressMonitor monitor;
        Queue<IResolve> updates=new LinkedList<IResolve>();

        public DisplayUpdater( IProgressMonitor monitor2 ) {
            this.monitor=monitor2;
        }

        public void run() {
            if( disposed ) return ;

            while( !updates.isEmpty() ) {
                Object[] element;
                synchronized (updates) {
                    element = updates.toArray();
                    updates.clear();
                }
                for( ILabelProviderListener l : listeners ) {
                    if (monitor.isCanceled())
                        return;
                    l.labelProviderChanged(new LabelProviderChangedEvent(source, element));
                }
            }
        }
       
    }
    private static class LabelData {
        public ImageDescriptor image;
        String text;
    }

   
}
TOP

Related Classes of org.locationtech.udig.catalog.ui.ResolveTitlesDecorator$LabelData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.