Package org.codehaus.plexus.component

Source Code of org.codehaus.plexus.component.ComponentIndex$ClassComparator

package org.codehaus.plexus.component;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import static org.codehaus.plexus.PlexusConstants.PLEXUS_DEFAULT_HINT;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.Collections;

public class ComponentIndex<V>
{
    /**
     * Should values be indexed by all types or simply the supplied type?
     */
    private final boolean indexByAllTypes;

    /**
     * This is the actual index.
     * ClassLoader -> Class -> RoleHint -> Values
     */
    private final Map<ClassLoader, SortedMap<Class<?>, Multimap<String, V>>> index =
        new LinkedHashMap<ClassLoader, SortedMap<Class<?>, Multimap<String, V>>>();

    /**
     * Creates a component index that indexes by all super types and interfaces of supplied type.
     */
    public ComponentIndex()
    {
        this( false );
    }

    /**
     * Creates a component index.
     * @param indexByAllTypes if true, values are indexed by all super types and interfaces of supplied type; otherwise
     * values are only indexed by supplied type
     */
    public ComponentIndex( boolean indexByAllTypes )
    {
        this.indexByAllTypes = indexByAllTypes;
    }

    /**
     * Are values are indexed by all super types and interfaces of supplied type?
     * @return true, values are indexed by all super types and interfaces of supplied type; otherwise
     * false and values are only indexed by supplied type
     */
    public boolean isIndexByAllTypes()
    {
        return indexByAllTypes;
    }

    /**
     * Gets the value associated with the specified type and roleHint.
     *
     * Values are searched for in classloader order starting from the thread context class loader or type class loader
     * if thread context class loader is not set.
     *
     * @param type the type (or super type if enabled) associated with the value; not null
     * @param roleHint the roleHint associated with the value, or null for the default roleHint
     * @return the value associated with the type and roleHint, or null
     */
    public synchronized V get( Class<?> type, String roleHint )
    {
        return get( type, roleHint, Thread.currentThread().getContextClassLoader() );
    }

    /**
     * Gets the value associated with the specified type and roleHint.
     *
     * Values are searched for in classloader order starting from the specified class loader, or thread context class
     * loader or type class loader if specified class loader is null.
     *
     * @param type the type (or super type if enabled) associated with the value; not null
     * @param roleHint the roleHint associated with the value, or null for the default roleHint
     * @param classLoader the class loader to search from
     * @return the value associated with the type and roleHint, or null
     */
    public synchronized V get( Class<?> type, String roleHint, ClassLoader classLoader )
    {
        if ( type == null )
        {
            throw new NullPointerException( "type is null" );
        }
        if ( roleHint == null )
        {
            roleHint = PLEXUS_DEFAULT_HINT;
        }

        Collection<V> values = findAll( type, classLoader ).get( roleHint );
        if ( values.isEmpty() )
        {
            return null;
        }
        return values.iterator().next();
    }

    public synchronized Collection<V> getAll( )
    {
        ArrayList<V> values = new ArrayList<V>();

        for ( SortedMap<Class<?>, Multimap<String, V>> roleIndex : index.values() )
        {
            for ( Multimap<String, V> roleHintIndex : roleIndex.values() )
            {
                values.addAll(roleHintIndex.values());
            }
        }
        return values;
    }

    /**
     * Gets all values associated with the specified type.
     *
     * Values are searched for in classloader order starting from the thread context class loader or type class loader
     * if thread context class loader is not set.
     *
     * The values are sorted in class loader search order then by registration order.
     *
     * @param type the type (or super type if enabled) associated with the value; not null
     * @return all values associated with the type; never null
     */
    public synchronized List<V> getAll( Class<?> type )
    {
        return getAll( type, Thread.currentThread().getContextClassLoader() );
    }

    /**
     * Gets all values associated with the specified type.
     *
     * Values are searched for in classloader order starting from the specified class loader, or thread context class
     * loader or type class loader if specified class loader is null.
     *
     * The values are sorted in class loader search order then by registration order.
     *
     * @param type the type (or super type if enabled) associated with the value; not null
     * @param classLoader the class loader to search from
     * @return all values associated with the type; never null
     */
    public synchronized List<V> getAll( Class<?> type, ClassLoader classLoader )
    {
        if ( type == null )
        {
            throw new NullPointerException( "type is null" );
        }

        return new ArrayList<V>( findAll( type, classLoader ).values() );
    }

    /**
     * Gets a map of all values associated with the specified type indexed by roleHint.
     *
     * Values are searched for in classloader order starting from the thread context class loader or type class loader
     * if thread context class loader is not set.
     *
     * @param type the type (or super type if enabled) associated with the value; not null
     * @return all of the value associated with the type; never null
     */
    public synchronized Map<String, V> getAllAsMap( Class<?> type )
    {
        return getAllAsMap( type, Thread.currentThread().getContextClassLoader() );
    }

    /**
     * Gets a map of all values associated with the specified type indexed by roleHint.
     *
     * Values are searched for in classloader order starting from the specified class loader, or thread context class
     * loader or type class loader if specified class loader is null.
     *
     * @param type the type (or super type if enabled) associated with the value; not null
     * @param classLoader the class loader to search from
     * @return all of the value associated with the type; never null
     */
    public synchronized Map<String, V> getAllAsMap( Class<?> type, ClassLoader classLoader )
    {
        if ( type == null )
        {
            throw new NullPointerException( "type is null" );
        }

        Map<String, V> descriptors = new TreeMap<String, V>();
        for ( Entry<String, V> entry : findAll( type, classLoader ).entries() )
        {
            if ( !descriptors.containsKey( entry.getKey() ) )
            {
                descriptors.put( entry.getKey(), entry.getValue() );
            }
        }
        return descriptors;
    }

    private synchronized Multimap<String, V> findAll( Class<?> type, ClassLoader classLoader )
    {
         if ( classLoader == null )
        {
            classLoader = type.getClassLoader();
        }

        // Determine class loaders to search
        LinkedHashSet<ClassLoader> classLoaders = new LinkedHashSet<ClassLoader>();
        for ( ClassLoader cl = classLoader; cl != null; cl = cl.getParent() )
        {
            if ( cl instanceof ClassRealm )
            {
                ClassRealm realm = (ClassRealm) cl;
                while ( realm != null )
                {
                    classLoaders.add( realm );
                    realm = realm.getParentRealm();
                }
            }
            else
            {
                // todo lots of plexus code depends on a global search when there is a class loader associated with
                // the thread but the cl is not a class realm
                // classLoaders.add( cl );
            }
        }
        // todo remove this when plexus code is updated to manage thread context class loader correctly
        if ( classLoaders.isEmpty() )
        {
            classLoaders.addAll( index.keySet() );
        }

        // Get all valid component descriptors
        Multimap<String, V> roleHintIndex = Multimaps.newHashMultimap();
        for ( ClassLoader cl : classLoaders )
        {
            SortedMap<Class<?>, Multimap<String, V>> roleIndex = index.get( cl );
            if ( roleIndex != null )
            {
                Multimap<String, V> values = roleIndex.get( type );
                if ( values != null )
                {
                    roleHintIndex.putAll( values );
                }
            }
        }
        return Multimaps.unmodifiableMultimap( roleHintIndex );
    }

    /**
     * Associate a value with the specified class loader, type and roleHint.  The value is also associated with all
     * superclasses and interfaces of the specified type unless index by all types is disabled.
     */
    public synchronized void add( ClassLoader classLoader, Class<?> type, String roleHint, V value )
    {
        if ( classLoader == null )
        {
            throw new NullPointerException( "classLoader is null" );
        }
        if ( type == null )
        {
            throw new NullPointerException( "type is null" );
        }
        if ( roleHint == null )
        {
            roleHint = PLEXUS_DEFAULT_HINT;
        }
        if ( value == null )
        {
            throw new NullPointerException( "value is null" );
        }

        SortedMap<Class<?>, Multimap<String, V>> roleIndex = index.get( classLoader );
        if ( roleIndex == null )
        {
            roleIndex = new TreeMap<Class<?>, Multimap<String, V>>( ClassComparator.INSTANCE );
            index.put( classLoader, roleIndex );
        }

        for ( Class<?> clazz : getAllTypes( type ) )
        {
            Multimap<String, V> roleHintIndex = roleIndex.get( clazz );
            if ( roleHintIndex == null )
            {
                roleHintIndex = new ArrayListMultimap<String, V>();
                roleIndex.put( clazz, roleHintIndex );
            }
            roleHintIndex.put( roleHint, value );
        }
    }

    /**
     * Removes the specified value from the index.  This is operation requires a linear search of the whole index, and
     * is therefor very expensive.
     * @param value the value to remove
     */
    public synchronized void remove( V value )
    {
        if ( value == null )
        {
            throw new NullPointerException( "value is null" );
        }

        for ( SortedMap<Class<?>, Multimap<String, V>> roleIndex : index.values() )
        {
            for ( Multimap<String, V> roleHintIndex : roleIndex.values() )
            {
                for ( Iterator<V> iterator = roleHintIndex.values().iterator(); iterator.hasNext(); )
                {
                    V v = iterator.next();
                    if ( value.equals( v ) )
                    {
                        iterator.remove();
                    }
                }
            }
        }
    }

    /**
     * Removes all values associated with the specified class loader.  This operation is very fast.
     */
    public synchronized List<V> removeAll( ClassLoader classLoader )
    {
        if ( classLoader == null )
        {
            throw new NullPointerException( "classLoader is null" );
        }

        ArrayList<V> values = new ArrayList<V>();

        SortedMap<Class<?>, Multimap<String, V>> roleIndex = index.remove( classLoader );
        for ( Multimap<String, V> roleHintIndex : roleIndex.values() )
        {
            values.addAll(roleHintIndex.values());
        }
        return values;
    }

    /**
     * Removes all values from this index.
     */
    public synchronized Collection<V> clear()
    {
        Collection<V> all = getAll();
        index.clear();
        return all;
    }

    private Set<Class<?>> getAllTypes( Class<?> type )
    {
        if ( type.isArray() )
        {
            throw new IllegalArgumentException( "type is an array: type=" + type );
        }

        // if we are not indexing by all types, simply return a set containing the source type
        if ( !indexByAllTypes )
        {
            return Collections.<Class<?>>singleton( type );
        }

        // All found types
        Set<Class<?>> allTypes = new LinkedHashSet<Class<?>>();

        // Types that must still be processed... may contain entries that
        // have already been added to allTypes, so check all types before
        // actuall processing to avoid infinite loops
        LinkedList<Class<?>> typesToProcess = new LinkedList<Class<?>>();
        typesToProcess.add( type );

        while ( !typesToProcess.isEmpty() )
        {
            Class<?> clazz = typesToProcess.removeFirst();

            // have we already processed this type
            if ( !allTypes.contains( clazz ) )
            {
                allTypes.add( clazz );

                // schedule superclass for processing
                Class<?> superclass = clazz.getSuperclass();
                if ( superclass != null )
                {
                    typesToProcess.addFirst( superclass );
                }

                // schedule all interfaces for processing
                typesToProcess.addAll( 0, Arrays.<Class<?>>asList( clazz.getInterfaces() ) );
            }
        }
        return allTypes;
    }

    private static final class ClassComparator implements Comparator<Class<?>>, Serializable
    {
        private static final ClassComparator INSTANCE = new ClassComparator();

        public int compare( Class<?> class1, Class<?> class2 )
        {
            return class1.getName().compareTo( class2.getName() );
        }
    }
}
TOP

Related Classes of org.codehaus.plexus.component.ComponentIndex$ClassComparator

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.