001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.discovery.tools;
018    
019    import org.apache.commons.discovery.ResourceClass;
020    import org.apache.commons.discovery.ResourceClassIterator;
021    import org.apache.commons.discovery.resource.classes.DiscoverClasses;
022    import org.apache.commons.discovery.resource.ClassLoaders;
023    
024    /**
025     * Holder for a default class.
026     *
027     * Class may be specified by name (String) or class (Class).
028     * Using the holder complicates the users job, but minimized # of API's.
029     */
030    public class DefaultClassHolder<T> {
031    
032        private Class<? extends T> defaultClass;
033    
034        private final String defaultName;
035    
036        /**
037         * Creates a new holder implementation given
038         * the input SPI implementation/extension class.
039         *
040         * @param <S> Any type extends the SPI type
041         * @param defaultClass The hold class
042         */
043        public <S extends T> DefaultClassHolder(Class<S> defaultClass) {
044            this.defaultClass = defaultClass;
045            this.defaultName = defaultClass.getName();
046        }
047    
048        /**
049         * Creates a new holder implementation given
050         * the input SPI implementation/extension class name.
051         *
052         * @param defaultName The hold class name
053         */
054        public DefaultClassHolder(String defaultName) {
055            this.defaultClass = null;
056            this.defaultName = defaultName;
057        }
058    
059        /**
060         * Returns the default class, loading it if necessary
061         * and verifying that it implements the SPI
062         * (this forces the check, no way out..).
063         *
064         * @param <S>  Any type extends the SPI type
065         * @param spi non-null SPI
066         * @param loaders Used only if class needs to be loaded.
067         * @return The default Class.
068         */
069        public <S extends T> Class<S> getDefaultClass(SPInterface<T> spi, ClassLoaders loaders) {
070            if (defaultClass == null) {
071                DiscoverClasses<T> classDiscovery = new DiscoverClasses<T>(loaders);
072                ResourceClassIterator<T> classes = classDiscovery.findResourceClasses(getDefaultName());
073                if (classes.hasNext()) {
074                    ResourceClass<T> info = classes.nextResourceClass();
075                    try {
076                        defaultClass = info.loadClass();
077                    } catch (Exception e) {
078                        // ignore
079                    }
080                }
081            }
082    
083            if (defaultClass != null) {
084                spi.verifyAncestory(defaultClass);
085            }
086    
087            @SuppressWarnings("unchecked") // the SPInterface.verifyAncestory already asserted
088            Class<S> returned = (Class<S>) defaultClass;
089            return returned;
090        }
091    
092        /**
093         * Returns the hold class name.
094         *
095         * @return The hold class name
096         */
097        public String getDefaultName() {
098            return defaultName;
099        }
100    
101    }