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

org.scijava.object.ObjectIndex Maven / Gradle / Ivy

Go to download

SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.

There is a newer version: 2.99.0
Show newest version
/*
 * #%L
 * SciJava Common shared library for SciJava software.
 * %%
 * Copyright (C) 2009 - 2017 Board of Regents of the University of
 * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
 * Institute of Molecular Cell Biology and Genetics, University of
 * Konstanz, and KNIME GmbH.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.object;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.scijava.util.ClassUtils;

/**
 * Data structure for managing lists of registered objects.
 * 

* The object index keeps lists of objects segregated by type. The type * hierarchy beneath which each object is classified can be customized through * subclassing (e.g., see {@link org.scijava.plugin.PluginIndex}), but by * default, each registered object is added to all type lists with which its * class is compatible. For example, an object of type {@link String} would be * added to the following type lists: {@link String}, * {@link java.io.Serializable}, {@link Comparable}, {@link CharSequence} and * {@link Object}. A subsequent request for all objects of type * {@link Comparable} (via a call to {@link #get(Class)}) would return a list * that includes the object. *

*

* Note that similar to {@link List}, it is possible for the same object to be * added to the index more than once, in which case it will appear on relevant * type lists multiple times. *

*

* Note that similar to {@link List}, it is possible for the same object to be * added to the index more than once, in which case it will appear on compatible * type lists multiple times. *

* * @author Curtis Rueden */ public class ObjectIndex implements Collection { /** * "Them as counts counts moren them as dont count."
* —Russell Hoban, Riddley Walker */ protected final Map, List> hoard = new ConcurrentHashMap<>(); private final Class baseClass; /** List of objects to add later as needed (i.e., lazily). */ private final List> pending = new LinkedList<>(); public ObjectIndex(final Class baseClass) { this.baseClass = baseClass; } // -- ObjectIndex methods -- /** Gets the base class of the items being managed. */ public Class getBaseClass() { return baseClass; } /** * Gets a list of all registered objects. * * @return Read-only list of all registered objects, or an empty list if none * (this method never returns null). */ public List getAll() { // NB: We return the special "All" list here, since in some cases, // *no* other list contains *all* elements of the index. // We cannot use Object.class, since interface type hierarchies do not // extend Object. In particular, PluginIndex classifies objects beneath the // SciJavaPlugin type hierarchy, which does not extend Object. // And we cannot use getBaseClass() because the actual base class of the // objects stored in the index may differ from the type hierarchy beneath // which they are classified. In particular, PluginIndex classifies its // PluginInfo objects beneath the SciJavaPlugin type hierarchy, and not that // of PluginInfo. return get(All.class); } /** * Gets a list of registered objects compatible with the given type. * * @return New list of registered objects of the given type, or an empty * list if no such objects exist (this method never returns null). */ public List get(final Class type) { // lazily register any pending objects if (!pending.isEmpty()) resolvePending(); List list = retrieveList(type); // NB: Return a copy of the data, to facilitate thread safety. list = new ArrayList<>(list); return list; } /** * Registers objects which will be created lazily as needed. *

* This is useful if creation of the objects is expensive for some reason. In * that case, the object index can wait to actually request and register the * objects until the next accessor method invocation (i.e., * {@link #get(Class)} or {@link #getAll()}). *

*/ public void addLater(final LazyObjects c) { synchronized (pending) { pending.add(c); } } // -- Collection methods -- @Override public int size() { return getAll().size(); } @Override public boolean isEmpty() { return getAll().isEmpty(); } @Override public boolean contains(final Object o) { if (!getBaseClass().isAssignableFrom(o.getClass())) return false; @SuppressWarnings("unchecked") final E typedObj = (E) o; final Class type = getType(typedObj); return get(type).contains(o); } @Override public Iterator iterator() { return getAll().iterator(); } @Override public Object[] toArray() { return getAll().toArray(); } @Override public T[] toArray(final T[] a) { return getAll().toArray(a); } @Override public boolean add(final E o) { return add(o, false); } @Override public boolean remove(final Object o) { return remove(o, false); } @Override public boolean containsAll(final Collection c) { return getAll().containsAll(c); } @Override public boolean addAll(final Collection c) { boolean changed = false; for (final E o : c) { final boolean result = add(o, true); if (result) changed = true; } return changed; } @Override public boolean removeAll(final Collection c) { boolean changed = false; for (final Object o : c) { final boolean result = remove(o, true); if (result) changed = true; } return changed; } @Override public boolean retainAll(final Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { hoard.clear(); } // -- Object methods -- @Override public String toString() { final List> classes = new ArrayList<>(hoard.keySet()); Collections.sort(classes, new Comparator>() { @Override public int compare(final Class c1, final Class c2) { return ClassUtils.compare(c1, c2); } }); final String nl = System.getProperty("line.separator"); final StringBuilder sb = new StringBuilder(); for (final Class c : classes) { sb.append(c.getName() + ": {"); final List list = hoard.get(c); boolean first = true; for (final E element : list) { if (first) first = false; else sb.append(", "); sb.append(element); } sb.append("}" + nl); } return sb.toString(); } // -- Internal methods -- /** Adds the object to all compatible type lists. */ protected boolean add(final E o, final boolean batch) { return add(o, getType(o), batch); } /** Return the type by which to index the object. */ protected Class getType(final E o) { return o.getClass(); } /** Removes the object from all compatible type lists. */ protected boolean remove(final Object o, final boolean batch) { if (!getBaseClass().isAssignableFrom(o.getClass())) return false; @SuppressWarnings("unchecked") final E e = (E) o; return remove(o, getType(e), batch); } private Map, List[]> type2Lists = new HashMap<>(); protected synchronized List[] retrieveListsForType(final Class type) { final List[] lists = type2Lists.get(type); if (lists != null) return lists; final ArrayList> listOfLists = new ArrayList<>(); for (final Class c : getTypes(type)) { listOfLists.add(retrieveList(c)); } // convert list of lists to array of lists @SuppressWarnings("rawtypes") final List[] arrayOfRawLists = listOfLists.toArray(new List[listOfLists.size()]); @SuppressWarnings({ "unchecked" }) final List[] arrayOfLists = arrayOfRawLists; type2Lists.put(type, arrayOfLists); return arrayOfLists; } /** Adds an object to type lists beneath the given type hierarchy. */ @SuppressWarnings("unchecked") protected boolean add(final E o, final Class type, final boolean batch) { boolean result = false; for (final List list : retrieveListsForType(type)) { if (addToList(o, (List)list, batch)) result = true; } return result; } /** Removes an object from type lists beneath the given type hierarchy. */ protected boolean remove(final Object o, final Class type, final boolean batch) { boolean result = false; for (final List list : retrieveListsForType(type)) { if (removeFromList(o, list, batch)) result = true; } return result; } protected boolean addToList(final E obj, final List list, @SuppressWarnings("unused") final boolean batch) { return list.add(obj); } protected boolean removeFromList(final Object obj, final List list, @SuppressWarnings("unused") final boolean batch) { return list.remove(obj); } // -- Helper methods -- private static Map, Class[]> typeMap = new HashMap<>(); /** Gets a new set containing the type and all its supertypes. */ protected static synchronized Class[] getTypes(final Class type) { Class[] types = typeMap.get(type); if (types != null) return types; final Set>set = new LinkedHashSet<>(); set.add(All.class); // NB: Always include the "All" class. getTypes(type, set); types = set.toArray(new Class[set.size()]); typeMap.put(type, types); return types; } /** Recursively adds the type and all its supertypes to the given set. */ private static synchronized void getTypes(final Class type, final Set> types) { if (type == null) return; types.add(type); // recursively add to supertypes getTypes(type.getSuperclass(), types); for (final Class iface : type.getInterfaces()) { getTypes(iface, types); } } /** Retrieves the type list for the given type, creating it if necessary. */ protected List retrieveList(final Class type) { List list = hoard.get(type); if (list == null) { list = new ArrayList<>(); hoard.put(type, list); } return list; } private void resolvePending() { synchronized (pending) { while (!pending.isEmpty()) { final LazyObjects c = pending.remove(0); addAll(c.get()); } } } // -- Helper classes -- private static class All { // NB: A special class beneath which *all* elements of the index are listed. } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy