1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Rustem V. Rafikov
19 * @version $Revision: 1.3 $
20 */
21
22package javax.imageio.spi;
23
24import java.util.*;
25import java.util.Map.Entry;
26
27/**
28 * The ServiceRegistry class provides ability to register, deregister, look up
29 * and obtain service provider instances (SPIs). A service means a set of
30 * interfaces and classes, and a service provider is an implementation of a
31 * service. Service providers can be associated with one or more categories.
32 * Each category is defined by a class or interface. Only a single instance of a
33 * each class is allowed to be registered as a category.
34 *
35 * @since Android 1.0
36 */
37public class ServiceRegistry {
38
39    /**
40     * The categories.
41     */
42    CategoriesMap categories = new CategoriesMap(this);
43
44    /**
45     * Instantiates a new ServiceRegistry with the specified categories.
46     *
47     * @param categoriesIterator
48     *            an Iterator of Class objects for defining of categories.
49     */
50    public ServiceRegistry(Iterator<Class<?>> categoriesIterator) {
51        if (null == categoriesIterator) {
52            throw new IllegalArgumentException("categories iterator should not be NULL");
53        }
54        while (categoriesIterator.hasNext()) {
55            Class<?> c = categoriesIterator.next();
56            categories.addCategory(c);
57        }
58    }
59
60    /**
61     * Looks up and instantiates the available providers of this service using
62     * the specified class loader.
63     *
64     * @param providerClass
65     *            the Class object of the provider to be looked up.
66     * @param loader
67     *            the class loader to be used.
68     * @return the iterator of providers objects for this service.
69     */
70    public static <T> Iterator<T> lookupProviders(Class<T> providerClass, ClassLoader loader) {
71        throw new UnsupportedOperationException("Not supported yet");
72    }
73
74    /**
75     * Looks up and instantiates the available providers of this service using
76     * the context class loader.
77     *
78     * @param providerClass
79     *            the Class object of the provider to be looked up.
80     * @return the iterator of providers objects for this service.
81     */
82    public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
83        return lookupProviders(providerClass, Thread.currentThread().getContextClassLoader());
84    }
85
86    /**
87     * Registers the specified service provider object in the specified
88     * categories.
89     *
90     * @param provider
91     *            the specified provider to be registered.
92     * @param category
93     *            the category.
94     * @return true, if no provider of the same class is registered in this
95     *         category, false otherwise.
96     */
97    public <T> boolean registerServiceProvider(T provider, Class<T> category) {
98        return categories.addProvider(provider, category);
99    }
100
101    /**
102     * Registers a list of service providers.
103     *
104     * @param providers
105     *            the list of service providers.
106     */
107    public void registerServiceProviders(Iterator<?> providers) {
108        for (Iterator<?> iterator = providers; iterator.hasNext();) {
109            categories.addProvider(iterator.next(), null);
110        }
111    }
112
113    /**
114     * Registers the specified service provider object in all categories.
115     *
116     * @param provider
117     *            the service provider.
118     */
119    public void registerServiceProvider(Object provider) {
120        categories.addProvider(provider, null);
121    }
122
123    /**
124     * Deregisters the specifies service provider from the specified category.
125     *
126     * @param provider
127     *            the service provider to be deregistered.
128     * @param category
129     *            the specified category.
130     * @return true, if the provider was already registered in the specified
131     *         category, false otherwise.
132     */
133    public <T> boolean deregisterServiceProvider(T provider, Class<T> category) {
134        throw new UnsupportedOperationException("Not supported yet");
135    }
136
137    /**
138     * Deregisters the specified service provider from all categories.
139     *
140     * @param provider
141     *            the specified service provider.
142     */
143    public void deregisterServiceProvider(Object provider) {
144        throw new UnsupportedOperationException("Not supported yet");
145    }
146
147    /**
148     * Gets an Iterator of registered service providers in the specified
149     * category which satisfy the specified Filter. The useOrdering parameter
150     * indicates whether the iterator will return all of the server provider
151     * objects in a set order.
152     *
153     * @param category
154     *            the specified category.
155     * @param filter
156     *            the specified filter.
157     * @param useOrdering
158     *            the flag indicating that providers are ordered in the returned
159     *            Iterator.
160     * @return the iterator of registered service providers.
161     */
162    @SuppressWarnings("unchecked")
163    public <T> Iterator<T> getServiceProviders(Class<T> category, Filter filter, boolean useOrdering) {
164        return new FilteredIterator<T>(filter, (Iterator<T>)categories.getProviders(category,
165                useOrdering));
166    }
167
168    /**
169     * Gets an Iterator of all registered service providers in the specified
170     * category. The useOrdering parameter indicates whether the iterator will
171     * return all of the server provider objects in a set order.
172     *
173     * @param category
174     *            the specified category.
175     * @param useOrdering
176     *            the flag indicating that providers are ordered in the returned
177     *            Iterator.
178     * @return the Iterator of service providers.
179     */
180    @SuppressWarnings("unchecked")
181    public <T> Iterator<T> getServiceProviders(Class<T> category, boolean useOrdering) {
182        return (Iterator<T>)categories.getProviders(category, useOrdering);
183    }
184
185    /**
186     * Gets the registered service provider object that has the specified class
187     * type.
188     *
189     * @param providerClass
190     *            the specified provider class.
191     * @return the service provider object.
192     */
193    public <T> T getServiceProviderByClass(Class<T> providerClass) {
194        throw new UnsupportedOperationException("Not supported yet");
195    }
196
197    /**
198     * Sets an ordering between two service provider objects within the
199     * specified category.
200     *
201     * @param category
202     *            the specified category.
203     * @param firstProvider
204     *            the first provider.
205     * @param secondProvider
206     *            the second provider.
207     * @return true, if a previously unset order was set.
208     */
209    public <T> boolean setOrdering(Class<T> category, T firstProvider, T secondProvider) {
210        throw new UnsupportedOperationException("Not supported yet");
211    }
212
213    /**
214     * Unsets an ordering between two service provider objects within the
215     * specified category.
216     *
217     * @param category
218     *            the specified category.
219     * @param firstProvider
220     *            the first provider.
221     * @param secondProvider
222     *            the second provider.
223     * @return true, if a previously unset order was removed.
224     */
225    public <T> boolean unsetOrdering(Class<T> category, T firstProvider, T secondProvider) {
226        throw new UnsupportedOperationException("Not supported yet");
227    }
228
229    /**
230     * Deregisters all providers from the specified category.
231     *
232     * @param category
233     *            the specified category.
234     */
235    public void deregisterAll(Class<?> category) {
236        throw new UnsupportedOperationException("Not supported yet");
237    }
238
239    /**
240     * Deregister all providers from all categories.
241     */
242    public void deregisterAll() {
243        throw new UnsupportedOperationException("Not supported yet");
244    }
245
246    /**
247     * Finalizes this object.
248     *
249     * @throws Throwable
250     *             if an error occurs during finalization.
251     */
252    @Override
253    public void finalize() throws Throwable {
254        // TODO uncomment when deregisterAll is implemented
255        // deregisterAll();
256    }
257
258    /**
259     * Checks whether the specified provider has been already registered.
260     *
261     * @param provider
262     *            the provider to be checked.
263     * @return true, if the specified provider has been already registered,
264     *         false otherwise.
265     */
266    public boolean contains(Object provider) {
267        throw new UnsupportedOperationException("Not supported yet");
268    }
269
270    /**
271     * Gets an iterator of Class objects representing the current categories.
272     *
273     * @return the Iterator of Class objects.
274     */
275    public Iterator<Class<?>> getCategories() {
276        return categories.list();
277    }
278
279    /**
280     * The ServiceRegistry.Filter interface is used by
281     * ServiceRegistry.getServiceProviders to filter providers according to the
282     * specified criterion.
283     *
284     * @since Android 1.0
285     */
286    public static interface Filter {
287
288        /**
289         * Returns true if the specified provider satisfies the criterion of
290         * this Filter.
291         *
292         * @param provider
293         *            the provider.
294         * @return true, if the specified provider satisfies the criterion of
295         *         this Filter, false otherwise.
296         */
297        boolean filter(Object provider);
298    }
299
300    /**
301     * The Class CategoriesMap.
302     */
303    private static class CategoriesMap {
304
305        /**
306         * The categories.
307         */
308        Map<Class<?>, ProvidersMap> categories = new HashMap<Class<?>, ProvidersMap>();
309
310        /**
311         * The registry.
312         */
313        ServiceRegistry registry;
314
315        /**
316         * Instantiates a new categories map.
317         *
318         * @param registry
319         *            the registry.
320         */
321        public CategoriesMap(ServiceRegistry registry) {
322            this.registry = registry;
323        }
324
325        // -- TODO: useOrdering
326        /**
327         * Gets the providers.
328         *
329         * @param category
330         *            the category.
331         * @param useOrdering
332         *            the use ordering.
333         * @return the providers.
334         */
335        Iterator<?> getProviders(Class<?> category, boolean useOrdering) {
336            ProvidersMap providers = categories.get(category);
337            if (null == providers) {
338                throw new IllegalArgumentException("Unknown category: " + category);
339            }
340            return providers.getProviders(useOrdering);
341        }
342
343        /**
344         * List.
345         *
346         * @return the iterator< class<?>>.
347         */
348        Iterator<Class<?>> list() {
349            return categories.keySet().iterator();
350        }
351
352        /**
353         * Adds the category.
354         *
355         * @param category
356         *            the category.
357         */
358        void addCategory(Class<?> category) {
359            categories.put(category, new ProvidersMap());
360        }
361
362        /**
363         * Adds a provider to the category. If <code>category</code> is
364         * <code>null</code> then the provider will be added to all categories
365         * which the provider is assignable from.
366         *
367         * @param provider
368         *            provider to add.
369         * @param category
370         *            category to add provider to.
371         * @return true, if there were such provider in some category.
372         */
373        boolean addProvider(Object provider, Class<?> category) {
374            if (provider == null) {
375                throw new IllegalArgumentException("provider should be != NULL");
376            }
377
378            boolean rt;
379            if (category == null) {
380                rt = findAndAdd(provider);
381            } else {
382                rt = addToNamed(provider, category);
383            }
384
385            if (provider instanceof RegisterableService) {
386                ((RegisterableService)provider).onRegistration(registry, category);
387            }
388
389            return rt;
390        }
391
392        /**
393         * Adds the to named.
394         *
395         * @param provider
396         *            the provider.
397         * @param category
398         *            the category.
399         * @return true, if successful.
400         */
401        private boolean addToNamed(Object provider, Class<?> category) {
402            Object obj = categories.get(category);
403
404            if (null == obj) {
405                throw new IllegalArgumentException("Unknown category: " + category);
406            }
407
408            return ((ProvidersMap)obj).addProvider(provider);
409        }
410
411        /**
412         * Find and add.
413         *
414         * @param provider
415         *            the provider.
416         * @return true, if successful.
417         */
418        private boolean findAndAdd(Object provider) {
419            boolean rt = false;
420            for (Entry<Class<?>, ProvidersMap> e : categories.entrySet()) {
421                if (e.getKey().isAssignableFrom(provider.getClass())) {
422                    rt |= e.getValue().addProvider(provider);
423                }
424            }
425            return rt;
426        }
427    }
428
429    /**
430     * The Class ProvidersMap.
431     */
432    private static class ProvidersMap {
433        // -- TODO: providers ordering support
434
435        /**
436         * The providers.
437         */
438        Map<Class<?>, Object> providers = new HashMap<Class<?>, Object>();
439
440        /**
441         * Adds the provider.
442         *
443         * @param provider
444         *            the provider.
445         * @return true, if successful.
446         */
447        boolean addProvider(Object provider) {
448            return providers.put(provider.getClass(), provider) != null;
449        }
450
451        /**
452         * Gets the provider classes.
453         *
454         * @return the provider classes.
455         */
456        Iterator<Class<?>> getProviderClasses() {
457            return providers.keySet().iterator();
458        }
459
460        // -- TODO ordering
461        /**
462         * Gets the providers.
463         *
464         * @param userOrdering
465         *            the user ordering.
466         * @return the providers.
467         */
468        Iterator<?> getProviders(boolean userOrdering) {
469            return providers.values().iterator();
470        }
471    }
472
473    /**
474     * The Class FilteredIterator.
475     */
476    private static class FilteredIterator<E> implements Iterator<E> {
477
478        /**
479         * The filter.
480         */
481        private Filter filter;
482
483        /**
484         * The backend.
485         */
486        private Iterator<E> backend;
487
488        /**
489         * The next obj.
490         */
491        private E nextObj;
492
493        /**
494         * Instantiates a new filtered iterator.
495         *
496         * @param filter
497         *            the filter.
498         * @param backend
499         *            the backend.
500         */
501        public FilteredIterator(Filter filter, Iterator<E> backend) {
502            this.filter = filter;
503            this.backend = backend;
504            findNext();
505        }
506
507        /**
508         * Next.
509         *
510         * @return the e.
511         */
512        public E next() {
513            if (nextObj == null) {
514                throw new NoSuchElementException();
515            }
516            E tmp = nextObj;
517            findNext();
518            return tmp;
519        }
520
521        /**
522         * Checks for next.
523         *
524         * @return true, if successful.
525         */
526        public boolean hasNext() {
527            return nextObj != null;
528        }
529
530        /**
531         * Removes the.
532         */
533        public void remove() {
534            throw new UnsupportedOperationException();
535        }
536
537        /**
538         * Sets nextObj to a next provider matching the criterion given by the
539         * filter.
540         */
541        private void findNext() {
542            nextObj = null;
543            while (backend.hasNext()) {
544                E o = backend.next();
545                if (filter.filter(o)) {
546                    nextObj = o;
547                    return;
548                }
549            }
550        }
551    }
552}
553