All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.code.appengine.imageio.spi.ServiceRegistry Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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.
 */
/**
 * @author Rustem V. Rafikov
 */
package com.google.code.appengine.imageio.spi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
import java.util.Map.Entry;

import org.apache.harmony.x.imageio.internal.nls.Messages;

public class ServiceRegistry {

    CategoriesMap categories = new CategoriesMap(this);

    public ServiceRegistry(Iterator> categoriesIterator) {
        if (null == categoriesIterator) {
            throw new IllegalArgumentException(Messages.getString("imageio.5D"));
        }
        while(categoriesIterator.hasNext()) {
            Class c =  categoriesIterator.next();
            categories.addCategory(c);
        }
    }

    public static  Iterator lookupProviders(Class providerClass, ClassLoader loader) {
        return new LookupProvidersIterator(providerClass, loader);
    }

    public static  Iterator lookupProviders(Class providerClass) {
        return lookupProviders(providerClass, Thread.currentThread().getContextClassLoader());
    }

    public  boolean registerServiceProvider(T provider, Class category) {
        return categories.addProvider(provider, category);
    }

    public void registerServiceProviders(Iterator providers) {
        for (Iterator iterator = providers; iterator.hasNext();) {
            categories.addProvider(iterator.next(), null);
        }
    }

    public void registerServiceProvider(Object provider) {
        categories.addProvider(provider, null);
    }

    public  boolean deregisterServiceProvider(T provider, Class category) {
        return categories.removeProvider(provider, category);
    }

    public void deregisterServiceProvider(Object provider) {
        categories.removeProvider(provider);
    }

//    @SuppressWarnings("unchecked")
    public  Iterator getServiceProviders(Class category, Filter filter, boolean useOrdering) {
        return new FilteredIterator(filter, (Iterator) categories.getProviders(category, useOrdering));
    }

//    @SuppressWarnings("unchecked")
    public  Iterator getServiceProviders(Class category, boolean useOrdering) {
        return (Iterator) categories.getProviders(category, useOrdering);
    }

    public  T getServiceProviderByClass(Class providerClass) {
        return categories.getServiceProviderByClass(providerClass);
    }

    public  boolean setOrdering(Class category, T firstProvider, T secondProvider) {
        return categories.setOrdering(category, firstProvider, secondProvider);
    }

    public  boolean unsetOrdering(Class category, T firstProvider, T secondProvider) {
        return categories.unsetOrdering(category, firstProvider, secondProvider);
    }

    public void deregisterAll(Class category) {
        categories.removeAll(category);
    }

    public void deregisterAll() {
        categories.removeAll();
    }

    @Override
    public void finalize() throws Throwable {
        deregisterAll();
    }

    public boolean contains(Object provider) {
        if (provider == null) {
            throw new IllegalArgumentException(Messages.getString("imageio.5E"));
        }
        
        return categories.contains(provider);
    }

    public Iterator> getCategories() {
        return categories.list();
    }

    public static interface Filter {
        boolean filter(Object provider);
    }

    private static class CategoriesMap {
        Map, ProvidersMap> categories = new HashMap, ProvidersMap>();

        ServiceRegistry registry;

        public CategoriesMap(ServiceRegistry registry) {
            this.registry = registry;
        }

        boolean contains(Object provider) {
            for (Map.Entry, ProvidersMap> e : categories.entrySet()) {
                ProvidersMap providers = e.getValue();
                if (providers.contains(provider)) {
                    return true;
                }
            }
            
            return false;
        }

         boolean setOrdering(Class category, T firstProvider, T secondProvider) {
            ProvidersMap providers = categories.get(category);
            
            if (providers == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.92", category));
            }
            
            return providers.setOrdering(firstProvider, secondProvider);
        }
        
         boolean unsetOrdering(Class category, T firstProvider, T secondProvider) {
            ProvidersMap providers = categories.get(category);
            
            if (providers == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.92", category));
            }
            
            return providers.unsetOrdering(firstProvider, secondProvider);
        }
        
        Iterator getProviders(Class category, boolean useOrdering) {
            ProvidersMap providers = categories.get(category);
            if (null == providers) {
                throw new IllegalArgumentException(Messages.getString("imageio.92", category));
            }
            return providers.getProviders(useOrdering);
        }
        
         T getServiceProviderByClass(Class providerClass) {
        	for (Map.Entry, ProvidersMap> e : categories.entrySet()) {
        		if (e.getKey().isAssignableFrom(providerClass)) {
        			T provider = e.getValue().getServiceProviderByClass(providerClass);
        			if (provider != null) {
        				return provider;
        			}
        		}
        	}
        	return null;
        }

        Iterator> list() {
            return categories.keySet().iterator();
        }

        void addCategory(Class category) {
            categories.put(category, new ProvidersMap());
        }

        /**
         * Adds a provider to the category. If category is
         * null then the provider will be added to all categories
         * which the provider is assignable from.
         * @param provider provider to add
         * @param category category to add provider to
         * @return if there were such provider in some category
         */
        boolean addProvider(Object provider, Class category) {
            if (provider == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.5E"));
            }

            boolean rt;
            if (category == null) {
                rt = findAndAdd(provider);
            } else {
                rt  = addToNamed(provider, category);
            }

            if (provider instanceof RegisterableService) {
                ((RegisterableService) provider).onRegistration(registry, category);
            }

            return rt;
        }

        private boolean addToNamed(Object provider, Class category) {
            if (!category.isAssignableFrom(provider.getClass())) {
                throw new ClassCastException();
            }
            Object obj = categories.get(category);

            if (null == obj) {
                throw new IllegalArgumentException(Messages.getString("imageio.92", category));
            }

            return ((ProvidersMap) obj).addProvider(provider);
        }

        private boolean findAndAdd(Object provider) {
            boolean rt = false;
            for (Entry, ProvidersMap> e : categories.entrySet()) {
                if (e.getKey().isAssignableFrom(provider.getClass())) {
                    rt |= e.getValue().addProvider(provider);
                }
            }
            return rt;
        }
        
        boolean removeProvider(Object provider, Class category) {
            if (provider == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.5E"));
            }
            
            if (!category.isAssignableFrom(provider.getClass())) {
                throw new ClassCastException();
            }
            
            Object obj = categories.get(category);
            
            if (obj == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.92", category));
            }
            
            return ((ProvidersMap) obj).removeProvider(provider, registry, category);
        }
        
        void removeProvider(Object provider) {
            if (provider == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.5E"));
            }
            
            for (Entry, ProvidersMap> e : categories.entrySet()) {
                ProvidersMap providers = e.getValue();
                providers.removeProvider(provider, registry, e.getKey());
            }
        }
        
        void removeAll(Class category) {
            Object obj = categories.get(category);
            
            if (obj == null) {
                throw new IllegalArgumentException(Messages.getString("imageio.92", category));
            }
            
            ((ProvidersMap) obj).clear(registry);         
        }
        
        void removeAll() {
            for ( Map.Entry, ProvidersMap> e : categories.entrySet()) {
                removeAll(e.getKey());                
            }
        }
    }

    private static class ProvidersMap {

        Map, Object> providers = new HashMap, Object>();
        Map nodeMap = new HashMap();

        boolean addProvider(Object provider) {
            ProviderNode node =  new ProviderNode(provider);
            nodeMap.put(provider, node);
            Object obj = providers.put(provider.getClass(), provider);
            
            if (obj !=  null) {
                nodeMap.remove(obj);
                return false;
            }
            
            return true;
        }

        boolean contains(Object provider) {
            return providers.containsValue(provider);
        }

        boolean removeProvider(Object provider,
                ServiceRegistry registry, Class category) {
            
            Object obj = providers.get(provider.getClass());
            if ((obj == null) || (obj != provider)) {
                return false;
            }
            
            providers.remove(provider.getClass());
            nodeMap.remove(provider);
            
            if (provider instanceof RegisterableService) {
                ((RegisterableService) provider).onDeregistration(registry, category);
            }            
            
            return true;
        }
        
        void clear(ServiceRegistry registry) {
            for (Map.Entry, Object> e : providers.entrySet()) {
                Object provider = e.getValue();
                
                if (provider instanceof RegisterableService) {
                    ((RegisterableService) provider).onDeregistration(registry, e.getKey());
                }
            }
            
            providers.clear();
            nodeMap.clear();
        }

        Iterator> getProviderClasses() {
            return providers.keySet().iterator();
        }

        Iterator getProviders(boolean useOrdering) {
            if (useOrdering) {
                return new OrderedProviderIterator(nodeMap.values().iterator());              
            }
            
            return providers.values().iterator();
        }
        
        @SuppressWarnings("unchecked")
		 T getServiceProviderByClass(Class providerClass) {
        	return (T)providers.get(providerClass);
        }
        
        public  boolean setOrdering(T firstProvider, T secondProvider) {
            if (firstProvider == secondProvider) {
                throw new IllegalArgumentException(Messages.getString("imageio.98"));
            }
            
            if ((firstProvider == null) || (secondProvider == null)) {
                throw new IllegalArgumentException(Messages.getString("imageio.5E"));
            }
           
            ProviderNode firstNode = nodeMap.get(firstProvider);
            ProviderNode secondNode = nodeMap.get(secondProvider);
                    
            // if the ordering is already set, return false
            if ((firstNode == null) || (firstNode.contains(secondNode))) {
                return false;
            }
            
            // put secondProvider into firstProvider's outgoing nodes list
            firstNode.addOutEdge(secondNode);
            // increase secondNode's incoming edge by 1
            secondNode.addInEdge();         
            
            return true;
        }
        
        public  boolean unsetOrdering(T firstProvider, T secondProvider) {
            if (firstProvider == secondProvider) {
                throw new IllegalArgumentException(Messages.getString("imageio.98"));
            }
            
            if ((firstProvider == null) || (secondProvider == null)) {
                throw new IllegalArgumentException(Messages.getString("imageio.5E"));
            }
            
            ProviderNode firstNode = nodeMap.get(firstProvider);
            ProviderNode secondNode = nodeMap.get(secondProvider); 
                    
            // if the ordering is not set, return false
            if ((firstNode == null) || (!firstNode.contains(secondNode))) {
                return false;
            }
                    
            // remove secondProvider from firstProvider's outgoing nodes list
            firstNode.removeOutEdge(secondNode);
            // decrease secondNode's incoming edge by 1
            secondNode.removeInEdge();
                    
            return true;            
        }
    }

    private static class FilteredIterator implements Iterator {

        private Filter filter;
        private Iterator backend;
        private E nextObj;

        public FilteredIterator(Filter filter, Iterator backend) {
            this.filter = filter;
            this.backend = backend;
            findNext();
        }

        public E next() {
            if (nextObj == null) {
                throw new NoSuchElementException();
            }
            E tmp = nextObj;
            findNext();
            return tmp;
        }

        public boolean hasNext() {
            return nextObj != null;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        /**
         * Sets nextObj to a next provider matching the criterion given by the filter
         */
        private void findNext() {
            nextObj = null;
            while (backend.hasNext()) {
                E o = backend.next();
                if (filter.filter(o)) {
                    nextObj = o;
                    return;
                }
            }
        }
    }
    
    private static class ProviderNode {
        // number of incoming edges
        private int incomingEdges;  
        // all outgoing nodes
        private Set outgoingNodes; 
        private Object provider;
                
        public ProviderNode(Object provider) {
            incomingEdges = 0;
            outgoingNodes = new HashSet();
            this.provider = provider;
        }
            
        public Object getProvider() {
            return provider;
        }
        
        public Iterator getOutgoingNodes() {
            return outgoingNodes.iterator();
        }
        
        public boolean addOutEdge(Object secondProvider) {
            return outgoingNodes.add(secondProvider);
        }
        
        public  boolean removeOutEdge(Object provider) {
            return outgoingNodes.remove(provider);
        }
        
        public void addInEdge() {
            incomingEdges++;
        }
        
        public void removeInEdge() {
            incomingEdges--;
        }
        
        public int getIncomingEdges() {
            return incomingEdges;
        }
        
        public boolean contains(Object provider) {
            return outgoingNodes.contains(provider);
        }
    }

    /**
     * The iterator implements Kahn topological sorting algorithm.
     * @see Wikipedia
     * for further reference.
     */
    private static class OrderedProviderIterator implements Iterator {

        // the stack contains nodes which has no lesser nodes
        // except those already returned by the iterator
        private Stack firstNodes = new Stack();

        // a dynamic counter of incoming nodes
        // when a node is returned by iterator, the counters for connected
        // nodes decrement
        private Map incomingEdges = new HashMap();
        
        public OrderedProviderIterator(Iterator it) {
            // find all the nodes that with no incoming edges and
            // add them to firstNodes
            while (it.hasNext()) {
                ProviderNode node = (ProviderNode) it.next();
                incomingEdges.put(node, new Integer(node.getIncomingEdges()));
                if (node.getIncomingEdges() == 0) {
                    firstNodes.push(node);
                }
            }
        }
            
        public boolean hasNext() {
            return !firstNodes.empty();
        }

        public Object next() {
            if (firstNodes.empty()) {
               throw new NoSuchElementException();
            }
                            
            // get a node from firstNodes
            ProviderNode node = firstNodes.pop();
                            
            // find all the outgoing nodes
            Iterator it = node.getOutgoingNodes();
            while (it.hasNext()) {
                ProviderNode outNode = (ProviderNode) it.next();
                
                // remove the incoming edge from the node.
                int edges = incomingEdges.get(outNode);
                edges--;
                incomingEdges.put(outNode, new Integer(edges));
                
                // add to the firstNodes if this node's incoming edge is equal to 0
                if (edges == 0) {
                    firstNodes.push(outNode);
                }
            }
            
            incomingEdges.remove(node);
                            
            return node.getProvider();
        }
        
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
    
    static class LookupProvidersIterator implements Iterator {

        private Set providerNames = new HashSet();
        private Iterator it = null;
        private ClassLoader loader = null;
        
        public LookupProvidersIterator(Class providerClass, ClassLoader loader) {
            this.loader = loader;
            
            Enumeration e = null;
            try {
                e = loader.getResources("META-INF/services/"+providerClass.getName()); //$NON-NLS-1$
                while (e.hasMoreElements()) {
                    Set names = parse((URL)e.nextElement());
                    providerNames.addAll(names);
                }
            } catch (IOException e1) {
                // Ignored
            }

            it = providerNames.iterator();
        }
        
        /*
         * Parse the provider-configuration file as specified 
         * @see JAR File Specification
         */
        private Set parse(URL u) {
            InputStream input = null;
            BufferedReader reader = null;
            Set names = new HashSet();
            try {
                input = u.openStream();
                reader = new BufferedReader(new InputStreamReader(input, "utf-8")); //$NON-NLS-1$
                
                String line;
                while ((line = reader.readLine()) != null) {
                    // The comment character is '#' (0x23)
                    // on each line all characters following the first comment character are ignored
                    int sharpIndex = line.indexOf('#');
                    if (sharpIndex>=0) {
                        line = line.substring(0, sharpIndex);
                    }
                    
                    // Whitespaces are ignored
                    line = line.trim();
                    
                    if (line.length()>0) {
                        // a java class name, check if identifier correct
                        char[] namechars = line.toCharArray();
                        for (int i = 0; i < namechars.length; i++) {
                            if (!(Character.isJavaIdentifierPart(namechars[i]) || namechars[i] == '.')) {
                                throw new ServiceConfigurationError(Messages.getString("imageio.99", line));
                            }
                        }
                        names.add(line);
                    }
                }
            } catch (IOException e) {
                throw new ServiceConfigurationError(e.toString());
            } finally {
                try {
                    if (reader != null) {
                        reader.close();
                    }
                    if (input != null) {
                        input.close();
                    }
                } catch (IOException e) {
                    throw new ServiceConfigurationError(e.toString());
                }
            }
            
            return names;
        }
        
        public boolean hasNext() {
            return it.hasNext();
        }

        public Object next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            
            String name = (String)it.next();
            try {
                return Class.forName(name, true, loader).newInstance();
            } catch (Exception e) {
                throw new ServiceConfigurationError(e.toString());
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();   
        }
    }
    
    /**
     * An Error that can be thrown when something wrong occurs in loading a service
     * provider.
     */
    static class ServiceConfigurationError extends Error {
        
        private static final long serialVersionUID = 74132770414881L;

        /**
         * The constructor
         * 
         * @param msg
         *            the message of this error
         */
        public ServiceConfigurationError(String msg) {
            super(msg);
        }

        /**
         * The constructor
         * 
         * @param msg
         *            the message of this error
         * @param cause 
         *            the cause of this error
         */
        public ServiceConfigurationError(String msg, Throwable cause) {
            super(msg, cause);
        }
    }
}