| /* |
| * 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. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.xmlgraphics.image.loader.spi; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import org.apache.xmlgraphics.image.loader.ImageFlavor; |
| import org.apache.xmlgraphics.image.loader.ImageInfo; |
| import org.apache.xmlgraphics.util.Service; |
| |
| /** |
| * This class is the registry for all implementations of the various service provider interfaces |
| * for the image package. |
| */ |
| public class ImageImplRegistry { |
| |
| /** logger */ |
| protected static Log log = LogFactory.getLog(ImageImplRegistry.class); |
| |
| /** Holds the list of preloaders */ |
| private List preloaders = new java.util.ArrayList(); |
| //Content: List<ImagePreloader> |
| private int lastPreloaderIdentifier; |
| private int lastPreloaderSort; |
| |
| /** Holds the list of ImageLoaderFactories */ |
| private Map loaders = new java.util.HashMap(); |
| //Content: Map<String,Map<ImageFlavor,ImageLoaderFactory>> |
| |
| /** Holds the list of ImageConverters */ |
| private List converters = new java.util.ArrayList(); |
| //Content: List<ImageConverter> |
| |
| private int converterModifications; |
| |
| /** Singleton instance */ |
| private static ImageImplRegistry defaultInstance; |
| |
| /** |
| * Main constructor. |
| * @see #getDefaultInstance() |
| */ |
| public ImageImplRegistry() { |
| discoverClasspathImplementations(); |
| } |
| |
| /** |
| * Returns the default instance of the Image implementation registry. |
| * @return the default instance |
| */ |
| public static ImageImplRegistry getDefaultInstance() { |
| if (defaultInstance == null) { |
| defaultInstance = new ImageImplRegistry(); |
| } |
| return defaultInstance; |
| } |
| |
| /** |
| * Discovers all implementations in the application's classpath. |
| */ |
| public void discoverClasspathImplementations() { |
| //Dynamic registration of ImagePreloaders |
| Iterator iter = Service.providers(ImagePreloader.class, true); |
| while (iter.hasNext()) { |
| registerPreloader((ImagePreloader)iter.next()); |
| } |
| |
| //Dynamic registration of ImageLoaderFactories |
| iter = Service.providers(ImageLoaderFactory.class, true); |
| while (iter.hasNext()) { |
| registerLoaderFactory((ImageLoaderFactory)iter.next()); |
| } |
| |
| //Dynamic registration of ImageConverters |
| iter = Service.providers(ImageConverter.class, true); |
| while (iter.hasNext()) { |
| registerConverter((ImageConverter)iter.next()); |
| } |
| } |
| |
| /** |
| * Registers a new ImagePreloader. |
| * @param preloader An ImagePreloader instance |
| */ |
| public void registerPreloader(ImagePreloader preloader) { |
| if (log.isDebugEnabled()) { |
| log.debug("Registered " + preloader.getClass().getName() |
| + " with priority " + preloader.getPriority()); |
| } |
| preloaders.add(newPreloaderHolder(preloader)); |
| } |
| |
| private synchronized PreloaderHolder newPreloaderHolder(ImagePreloader preloader) { |
| PreloaderHolder holder = new PreloaderHolder(); |
| holder.preloader = preloader; |
| holder.identifier = ++lastPreloaderIdentifier; |
| return holder; |
| } |
| |
| private class PreloaderHolder { |
| private ImagePreloader preloader; |
| private int identifier; |
| |
| public String toString() { |
| return preloader + " " + identifier; |
| } |
| } |
| |
| private synchronized void sortPreloaders() { |
| if (this.lastPreloaderIdentifier != this.lastPreloaderSort) { |
| Collections.sort(this.preloaders, new Comparator() { |
| |
| public int compare(Object o1, Object o2) { |
| PreloaderHolder h1 = (PreloaderHolder)o1; |
| PreloaderHolder h2 = (PreloaderHolder)o2; |
| int p1 = h1.preloader.getPriority(); |
| int p2 = h2.preloader.getPriority(); |
| int diff = p1 - p2; |
| if (diff != 0) { |
| return diff; |
| } else { |
| diff = h1.identifier - h2.identifier; |
| return diff; |
| } |
| } |
| |
| }); |
| this.lastPreloaderSort = lastPreloaderIdentifier; |
| } |
| } |
| |
| /** |
| * Registers a new ImageLoaderFactory. |
| * @param loaderFactory An ImageLoaderFactory instance |
| */ |
| public void registerLoaderFactory(ImageLoaderFactory loaderFactory) { |
| if (!loaderFactory.isAvailable()) { |
| if (log.isDebugEnabled()) { |
| log.debug("ImageLoaderFactory reports not available: " |
| + loaderFactory.getClass().getName()); |
| } |
| return; |
| } |
| String[] mimes = loaderFactory.getSupportedMIMETypes(); |
| for (int i = 0, ci = mimes.length; i < ci; i++) { |
| String mime = mimes[i]; |
| |
| synchronized (loaders) { |
| Map flavorMap = (Map)loaders.get(mime); |
| if (flavorMap == null) { |
| flavorMap = new java.util.HashMap(); |
| loaders.put(mime, flavorMap); |
| } |
| |
| ImageFlavor[] flavors = loaderFactory.getSupportedFlavors(mime); |
| for (int j = 0, cj = flavors.length; j < cj; j++) { |
| ImageFlavor flavor = flavors[j]; |
| |
| List factoryList = (List)flavorMap.get(flavor); |
| if (factoryList == null) { |
| factoryList = new java.util.ArrayList(); |
| flavorMap.put(flavor, factoryList); |
| } |
| factoryList.add(loaderFactory); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Registered " + loaderFactory.getClass().getName() |
| + ": MIME = " + mime + ", Flavor = " + flavor); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the Collection of registered ImageConverter instances. |
| * @return a Collection<ImageConverter> |
| */ |
| public Collection getImageConverters() { |
| return Collections.unmodifiableList(this.converters); |
| } |
| |
| /** |
| * Returns the number of modifications to the collection of registered ImageConverter instances. |
| * This is used to detect changes in the registry concerning ImageConverters. |
| * @return the number of modifications |
| */ |
| public int getImageConverterModifications() { |
| return this.converterModifications; |
| } |
| |
| /** |
| * Registers a new ImageConverter. |
| * @param converter An ImageConverter instance |
| */ |
| public void registerConverter(ImageConverter converter) { |
| converters.add(converter); |
| converterModifications++; |
| if (log.isDebugEnabled()) { |
| log.debug("Registered: " + converter.getClass().getName()); |
| } |
| } |
| |
| /** |
| * Returns an iterator over all registered ImagePreloader instances. |
| * @return an iterator over ImagePreloader instances. |
| */ |
| public Iterator getPreloaderIterator() { |
| sortPreloaders(); |
| final Iterator iter = this.preloaders.iterator(); |
| //Unpack the holders |
| return new Iterator() { |
| |
| public boolean hasNext() { |
| return iter.hasNext(); |
| } |
| |
| public Object next() { |
| Object obj = iter.next(); |
| if (obj != null) { |
| return ((PreloaderHolder)obj).preloader; |
| } else { |
| return null; |
| } |
| } |
| |
| public void remove() { |
| iter.remove(); |
| } |
| |
| }; |
| } |
| |
| /** |
| * Returns the best ImageLoaderFactory supporting the {@link ImageInfo} and image flavor. |
| * If there are multiple ImageLoaderFactories the one with the least usage penalty is selected. |
| * @param imageInfo the image info object |
| * @param flavor the image flavor. |
| * @return an ImageLoaderFactory instance or null, if no suitable implementation was found |
| */ |
| public ImageLoaderFactory getImageLoaderFactory(ImageInfo imageInfo, ImageFlavor flavor) { |
| String mime = imageInfo.getMimeType(); |
| Map flavorMap = (Map)loaders.get(mime); |
| if (flavorMap != null) { |
| List factoryList = (List)flavorMap.get(flavor); |
| if (factoryList != null && factoryList.size() > 0) { |
| Iterator iter = factoryList.iterator(); |
| int bestPenalty = Integer.MAX_VALUE; |
| ImageLoaderFactory bestFactory = null; |
| while (iter.hasNext()) { |
| ImageLoaderFactory factory = (ImageLoaderFactory)iter.next(); |
| if (!factory.isSupported(imageInfo)) { |
| continue; |
| } |
| int penalty = factory.getUsagePenalty(mime, flavor); |
| if (penalty < bestPenalty) { |
| bestPenalty = penalty; |
| bestFactory = factory; |
| } |
| } |
| return bestFactory; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns an array of {@link ImageLoaderFactory} instances that support the MIME type |
| * indicated by an {@link ImageInfo} object and can generate the given image flavor. |
| * @param imageInfo the image info object |
| * @param flavor the target image flavor |
| * @return the array of image loader factories |
| */ |
| public ImageLoaderFactory[] getImageLoaderFactories(ImageInfo imageInfo, ImageFlavor flavor) { |
| String mime = imageInfo.getMimeType(); |
| Collection matches = new java.util.TreeSet(new ImageLoaderFactoryComparator(mime, flavor)); |
| Map flavorMap = (Map)loaders.get(mime); |
| if (flavorMap != null) { |
| List factoryList = (List)flavorMap.get(flavor); |
| if (factoryList != null && factoryList.size() > 0) { |
| Iterator iter = factoryList.iterator(); |
| while (iter.hasNext()) { |
| ImageLoaderFactory factory = (ImageLoaderFactory)iter.next(); |
| if (factory.isSupported(imageInfo)) { |
| matches.add(factory); |
| } |
| } |
| } |
| } |
| if (matches.size() == 0) { |
| return null; |
| } else { |
| return (ImageLoaderFactory[])matches.toArray(new ImageLoaderFactory[matches.size()]); |
| } |
| } |
| |
| private static class ImageLoaderFactoryComparator implements Comparator { |
| |
| private String mime; |
| private ImageFlavor targetFlavor; |
| |
| public ImageLoaderFactoryComparator(String mime, ImageFlavor targetFlavor) { |
| this.mime = mime; |
| this.targetFlavor = targetFlavor; |
| } |
| |
| public int compare(Object o1, Object o2) { |
| ImageLoaderFactory f1 = (ImageLoaderFactory)o1; |
| ImageLoaderFactory f2 = (ImageLoaderFactory)o2; |
| //Lowest penalty first |
| return f1.getUsagePenalty(mime, targetFlavor) - f2.getUsagePenalty(mime, targetFlavor); |
| } |
| |
| } |
| |
| |
| /** |
| * Returns an array of ImageLoaderFactory instances which support the given MIME type. The |
| * instances are returned in no particular order. |
| * @param mime the MIME type to find ImageLoaderFactories for |
| * @return the array of ImageLoaderFactory instances |
| */ |
| public ImageLoaderFactory[] getImageLoaderFactories(String mime) { |
| Map flavorMap = (Map)loaders.get(mime); |
| if (flavorMap != null) { |
| Set factories = new java.util.HashSet(); |
| Iterator iter = flavorMap.values().iterator(); |
| while (iter.hasNext()) { |
| List factoryList = (List)iter.next(); |
| factories.addAll(factoryList); |
| } |
| int factoryCount = factories.size(); |
| if (factoryCount > 0) { |
| return (ImageLoaderFactory[])factories.toArray( |
| new ImageLoaderFactory[factoryCount]); |
| } |
| } |
| return null; |
| } |
| |
| } |