Package org.locationtech.udig.project.internal.impl

Source Code of org.locationtech.udig.project.internal.impl.LayerResource$Sorter

/*
*    uDig - User Friendly Desktop Internet GIS client
*    http://udig.refractions.net
*    (C) 2012, 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.project.internal.impl;

import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.locationtech.udig.catalog.ID;
import org.locationtech.udig.catalog.IGeoResource;
import org.locationtech.udig.catalog.IGeoResourceInfo;
import org.locationtech.udig.catalog.IResolve;
import org.locationtech.udig.catalog.IService;
import org.locationtech.udig.core.internal.ExtensionPointList;
import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.IResourceCachingInterceptor;
import org.locationtech.udig.project.IResourceInterceptor;
import org.locationtech.udig.project.internal.ProjectPlugin;
import org.locationtech.udig.project.preferences.PreferenceConstants;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;

/**
* Wraps a IGeoResource for a layer.
* <p>
* This is to ensure that each item that is resolved to is the same instance regardless of how the
* IGeoResource's resolve method is implemented.
*
* @author Jesse
* @since 1.0.0
*/
public class LayerResource extends IGeoResource {

    /**
     * The layer that owns/created the Resource
     */
    private final LayerImpl layer;

    /**
     * The resource that is wrapped by this object
     */
    IGeoResource geoResource;

    private volatile InterceptorsBag interceptors;

    /** Used to sort resource interceptors that are applied prior to the resource being cached. */
    private Sorter preSorter;

    /**
     * Used to sort resource interceptors that are applied after the cache.
     */
    private Sorter postSorter;
   
    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals( Object obj ) {
        if (obj instanceof LayerResource) {
            LayerResource other = (LayerResource) obj;
            return geoResource.equals(other.geoResource);
        }
        return false;
    }

    /**
     * @see org.locationtech.udig.catalog.IGeoResource#hashCode()
     */
    public int hashCode() {
        return geoResource.hashCode();
    }

    /**
     * Construct <code>LayerImpl.LayerResource</code>.
     */
    public LayerResource( LayerImpl impl, IGeoResource resource ) {
        service = null; // we do not have a parent we are a simple wrapper
        layer = impl;
        this.geoResource = resource;
    }

    /**
     * We can resolve to our layer; or anything our wrapped getResource can do.
     * @param adaptee Requested information
     * @return true if content of requested type (adaptee) is available
     */
    public <T> boolean canResolve( Class<T> adaptee ) {
        if (ILayer.class.isAssignableFrom(adaptee)) {
            return true;
        }

        return geoResource.canResolve(adaptee);
    }

    @Override
    public URL getIdentifier() {
        return geoResource.getIdentifier();
    }

    @Override
    public ID getID() {
        return this.geoResource.getID();
    }
    /**
     * @see org.locationtech.udig.catalog.IResolve#getMessage()
     */
    public Throwable getMessage() {
        return geoResource.getMessage();
    }

    /**
     * @see org.locationtech.udig.catalog.IResolve#getStatus()
     */
    public Status getStatus() {
        return geoResource.getStatus();
    }

    public String getTitle() {
        return geoResource.getTitle();
    }
   
    /**
     * @see org.locationtech.udig.catalog.IGeoResource#getPersistentProperties()
     */
    public Map<String, Serializable> getPersistentProperties() {
        return geoResource.getPersistentProperties();
    }

    /**
     * @see org.locationtech.udig.catalog.IResolve#members(org.eclipse.core.runtime.IProgressMonitor)
     */
    public List<IResolve> members( IProgressMonitor monitor ) {
        // JG: I am not sure if this is correct; the children may need to be wrapped up as LayerResource themselves
        return geoResource.members(monitor);
    }

    /**
     * @see org.locationtech.udig.catalog.IGeoResource#resolve(java.lang.Class,
     *      org.eclipse.core.runtime.IProgressMonitor)
     */
    public synchronized <T> T resolve( Class<T> adaptee, IProgressMonitor monitor )
            throws IOException {
        if (ILayer.class.isAssignableFrom(adaptee)) {
            return adaptee.cast(layer);
        }
        if (IGeoResource.class == adaptee){
            return adaptee.cast(this); // access to the original
        }
        if (this.geoResource.getClass().isAssignableFrom(adaptee)){
            return adaptee.cast(geoResource); // access to the original
        }
//        T resolve = geoResource.resolve(adaptee, monitor);
//        if (resolve != null) {
//            return resolve;
//        }
        T resource = processResourceCachingStrategy(monitor, adaptee);
        if (resource == null){
            return null; // could not do it
        }
        resource = processPostResourceInterceptors(resource, adaptee);
        return resource;
    }
    @Override
    public IResolve parent( IProgressMonitor monitor ) throws IOException {
        return geoResource.parent(monitor);
    }
    private <T> T processPreResourceInterceptors( T resource, Class<T> requestedType ) {
        List<SafeResourceInterceptor<T>> pre = getPreInterceptors(resource);
        return runInterceptors(requestedType, resource, pre);
    }
    /**
     * This is where all the interceptors actually get applied.
     *
     * @param <T> type of requested result
     * @param requestedType type of requested result
     * @param origionalResource value of requested type to be wrapped/configured
     * @param pre list of interceptors to "pre" process the resource before use
     * @return resource as modified/wrapped by any applicable interceptors
     */
    private <T> T runInterceptors( Class<T> requestedType, T origionalResource, List<SafeResourceInterceptor<T>> pre ) {
        // initially we start with the origional resource
        T resource = origionalResource;
        for( SafeResourceInterceptor<T> interceptor : pre ) {
            if (resource == null){
                // if the resource is null then we have nothing further we can do
                // (the resource was provided to us as null; or one of the interceptors
                //  has determined we don't have permission/clearance to view the data)
                return null;
            }
            if (isAssignable(resource, interceptor.targetType)){
                // if the interceptor is applicable; run it against the current resource
                resource = interceptor.run(layer, resource, requestedType);
                // The resource returned may be a wrapper (adding functionality) or
                // it may simply configure the resource (say provided the current transaction)
                // or it could return null if it determins that the resource should not be used
                //
            }
            // we need to continue through all the interceptors giving each a chance wrap/configurer
            // Currently all interceptors are considered equal; if needed we can introduce a priority
            // and apply them in a specific order
        }
        return resource; // the final resource
    }

    private <T> List<SafeResourceInterceptor<T>> getPreInterceptors( T resource ) {
        loadInterceptors();
        Set<Entry<IConfigurationElement, SafeResourceInterceptor<?>>> entires = interceptors.pre.entrySet();
        List<SafeResourceInterceptor<T>> result = findValidInterceptors(resource, entires);
        if (preSorter != null) {
            Collections.sort(result, preSorter);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private <T> List<SafeResourceInterceptor<T>> findValidInterceptors( T resource,
            Set<Entry<IConfigurationElement, SafeResourceInterceptor<?>>> entires ) {
        List<SafeResourceInterceptor<T>> result = new ArrayList<SafeResourceInterceptor<T>>();
        for( Entry<IConfigurationElement, SafeResourceInterceptor<?>> entry : entires ) {
            String attribute = entry.getKey().getAttribute("target"); //$NON-NLS-1$
            if (attribute == null) {
                result.add((SafeResourceInterceptor<T>) entry.getValue());
            } else {
                boolean assignableFrom = isAssignable(resource, attribute);
                if (assignableFrom)
                    result.add((SafeResourceInterceptor<T>) entry.getValue());
            }
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private <T> boolean isAssignable( T resource, String attribute ) {
        if (attribute == null)
            return true;
        boolean assignableFrom;
        try {
            Class< ? extends Object> class1 = resource.getClass();
            ClassLoader classLoader = class1.getClassLoader();
            if (classLoader == null) {
                // its a library class so use normal class loader
                classLoader = Thread.currentThread().getContextClassLoader();
            }
            Class<T> loadClass = (Class<T>) classLoader.loadClass(attribute);
            assignableFrom = (loadClass).isAssignableFrom(class1);
        } catch (ClassNotFoundException e) {
            return false;
        }
        return assignableFrom;
    }
    /**
     * Carefully checks if the requested information is available.
     * <p>
     * If the information has been asked for before; it is hoped a cached copy is available
     * as dictated by the various interceptors in play.
     * <p>
     * If a cached copy is not available; the geoResource is asked; and the result
     * wrapped up in any preResourceInterceptors before being cached for later.
     * <p>
     * This is the heart of Layer; tread carefully.
     *
     * @param <T> Type of requested information
     * @param monitor Used to track progress and canel
     * @param requestedType requested information
     * @return the information requested; or null if it is unavailable
     * @throws IOException
     */
    private <T> T processResourceCachingStrategy( IProgressMonitor monitor, Class<T> requestedType )
            throws IOException {
        IResourceCachingInterceptor cachingStrategy = getCachingInterceptors();
        if (cachingStrategy == null) {
            // no caching in use; proceed as normal
            T rawResource = geoResource.resolve(requestedType, monitor);
            if (rawResource == null){               
                return null; // not available!
            }
            T resource = processPreResourceInterceptors(rawResource, requestedType);
            return resource;      
        }
        // we have a caching strategy
        //
        if (cachingStrategy.isCached(layer, geoResource, requestedType)) {
            T resource = cachingStrategy.get(layer, requestedType);
            return resource;
        } else {
            T rawResource;
            if (IGeoResourceInfo.class.isAssignableFrom(requestedType)) {
                rawResource = requestedType.cast(geoResource.getInfo(monitor));
            } else {
                rawResource = geoResource.resolve(requestedType, monitor);
            }
           
            if (rawResource == null){
                return null; // not available!
            }
            T resource = processPreResourceInterceptors(rawResource, requestedType);
            // cache for next time
            cachingStrategy.put(layer, resource, requestedType);
            return resource;
        }
    }

    private IResourceCachingInterceptor getCachingInterceptors() {
        loadInterceptors();
        String string = ProjectPlugin.getPlugin().getPreferenceStore().getString(
                PreferenceConstants.P_LAYER_RESOURCE_CACHING_STRATEGY);
        return interceptors.caching.get(string);
    }
    private <T> T processPostResourceInterceptors( T originalResource, Class<T> requestedType ) {
        // get a list of interceptors willing to work
        List<SafeResourceInterceptor<T>> interceptors = getPostInterceptors(originalResource);
       
        // allow the interceptors to configure or wrap up the originalResource
        T resource = runInterceptors(requestedType, originalResource, interceptors);
       
        return resource;
    }

    private <T> List<SafeResourceInterceptor<T>> getPostInterceptors( T resource ) {
        loadInterceptors();
        List<SafeResourceInterceptor<T>> findValidInterceptors = findValidInterceptors(resource, interceptors.post
                .entrySet());
        if (postSorter != null) {
            Collections.sort(findValidInterceptors, postSorter);
        }
        return findValidInterceptors;
    }

    @SuppressWarnings("unchecked")
    private void loadInterceptors() {
        if (interceptors == null) {
            synchronized (this) {
                if (interceptors == null) {

                    final Map<IConfigurationElement, SafeResourceInterceptor<?>> pre = new HashMap<IConfigurationElement, SafeResourceInterceptor<?>>();
                    final Map<String, IResourceCachingInterceptor> caching = new HashMap<String, IResourceCachingInterceptor>();
                    final Map<IConfigurationElement, SafeResourceInterceptor<?>> post = new HashMap<IConfigurationElement, SafeResourceInterceptor<?>>();

                    List<IConfigurationElement> list = ExtensionPointList
                            .getExtensionPointList("org.locationtech.udig.project.resourceInterceptor"); //$NON-NLS-1$
                    for( IConfigurationElement element : list ) {
                        try {
                            if (element.getName().equals("cachingStrategy")) { //$NON-NLS-1$
                                IResourceCachingInterceptor strategy = (IResourceCachingInterceptor) element
                                        .createExecutableExtension("class"); //$NON-NLS-1$
                                caching.put(element.getNamespaceIdentifier()
                                        + "." + element.getAttribute("id"), strategy); //$NON-NLS-1$ //$NON-NLS-2$
                            } else {
                                IResourceInterceptor<?> tmp = (IResourceInterceptor<?>) element
                                        .createExecutableExtension("class"); //$NON-NLS-1$

                                SafeResourceInterceptor<?> interceptor = new SafeResourceInterceptor(tmp, element
                                        .getAttribute("target")); //$NON-NLS-1$
                                String order = element.getAttribute("order"); //$NON-NLS-1$
                                if ("PRE".equals(order)) { //$NON-NLS-1$
                                    pre.put(element, interceptor);
                                } else if ("POST".equals(order)) { //$NON-NLS-1$
                                    post.put(element, interceptor);
                                }
                            }
                        } catch (CoreException e) {
                            ProjectPlugin
                                    .log(
                                            "Failed to load resource interceptor:" + element.getNamespaceIdentifier() + "." + element.getAttribute("id"), e); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
                        }
                    }

                    interceptors = new InterceptorsBag(pre, caching, post);
                }
            }
        }
    }
    @Override
    protected IGeoResourceInfo createInfo( IProgressMonitor monitor ) throws IOException {
        return resolve(IGeoResourceInfo.class, monitor);
    }

    public LayerImpl getLayer() {
        return layer;
    }

    /**
     * ONLY FOR TESTING
     *
     * @param <T>
     * @param comparator the comparator for sorting
     * @param sortPre if true the preResourceInterceptors will be sorted other wise the
     *        postResourceInterceptors will be sorted.
     */
    public void testingOnly_sort( Comparator<IResourceInterceptor< ? extends Object>> comparator,
            boolean sortPre ) {
        if (sortPre)
            preSorter = new Sorter(comparator);
        else
            postSorter = new Sorter(comparator);

    }
    /**
     * Data structure of the interceptors currently in play.
     */
    private static class InterceptorsBag {
        final Map<IConfigurationElement, SafeResourceInterceptor<?>> pre;
        final Map<String, IResourceCachingInterceptor> caching;
        final Map<IConfigurationElement, SafeResourceInterceptor<?>> post;
        public InterceptorsBag( final Map<IConfigurationElement, SafeResourceInterceptor<?>> pre,
                final Map<String, IResourceCachingInterceptor> caching,
                final Map<IConfigurationElement, SafeResourceInterceptor<?>> post ) {
            super();
            this.pre = pre;
            this.caching = caching;
            this.post = post;
        }
    }
    /** Used to compare wrappers */
    private static class Sorter implements Comparator<SafeResourceInterceptor< ? extends Object>> {

        private Comparator<IResourceInterceptor< ? extends Object>> wrapped;

        public Sorter( Comparator<IResourceInterceptor< ? extends Object>> comparator ) {
            wrapped = comparator;
        }

        public int compare( SafeResourceInterceptor< ? extends Object> o1, SafeResourceInterceptor< ? extends Object> o2 ) {
            return wrapped.compare(o1.interceptor, o2.interceptor);
        }
    }

    /**
     * Safety wrapper; willing to log exceptions (when running the wrapped interceptor).
     *
     * @author Jesse
     * @since 1.1.0
     */
    private static class SafeResourceInterceptor<T> implements IResourceInterceptor<T> {
        public String targetType;
        private IResourceInterceptor<T> interceptor;
       
        public SafeResourceInterceptor( IResourceInterceptor<T> interceptor, String targetType ) {
            this.interceptor = interceptor;
            this.targetType = targetType;
        }
        /**
         * Safely calls the wrapped resource ResourceInterceptor; logging any errors thrown.
         */
        public T run( ILayer layer, T resource, Class< ? super T> requestedType ) {
            try {
                return interceptor.run(layer, resource, requestedType);
            } catch (Throwable t) {
                ProjectPlugin.log("Exception when running interceptor: " + interceptor, t); //$NON-NLS-1$
                return resource;
            }
        }

        @Override
        public String toString() {
            return interceptor.toString();
        }
    }

    @Override
    public IService service( IProgressMonitor monitor ) throws IOException {
        return geoResource.service(monitor);
    }
}
TOP

Related Classes of org.locationtech.udig.project.internal.impl.LayerResource$Sorter

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.