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

asset.pipeline.AssetSpecLoader.groovy Maven / Gradle / Ivy

package asset.pipeline

import groovy.util.logging.Slf4j

/*
 * Copyright 2014 original authors
 *
 * Licensed 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.
 */

/**
 * Loads asset specification. The asset-pipeline determines what processors and file types are processible via addon
 * plugins by scanning the classpath for "META-INF/asset-pipeline/asset.specs" files and "META-INF/asset-pipeline/processor.specs"
 * files. The 'asset.specs' file is a line by line list of classes that implement the {@link AssetFile} interface.
 * These are automatically added to the list of known specs and are what facilitate clean extensibility of the asset-pipeline.
 *
 * The 'processor.specs' file allows for adding additional {@link Processor} implementations to existing {@link AssetFile} classes.
 * It is a properties file with the key being the full class name of the {@link Processor} implementation and the value being
 * a comma delimited list of full class names of {@link AssetFile} implementations to be appended to
 *
 * @author Graeme Rocher
 */
@Slf4j
class AssetSpecLoader {

    static final String FACTORIES_RESOURCE_LOCATION = "META-INF/asset-pipeline/asset.specs"
    static final String PROCESSORS_RESOURCE_LOCATION = "META-INF/asset-pipeline/processor.specs"

    private static List> specifications = null
    /**
     * Load the specifications and return a list of specification classes
     *
     * @param classLoader The classloader
     *
     * @return A list of specification classes
     */
    static List> loadSpecifications(ClassLoader classLoader = Thread.currentThread().contextClassLoader) {

        if(specifications == null) {
            def resources = classLoader.getResources(FACTORIES_RESOURCE_LOCATION)
            specifications = []

            resources.each { URL res ->
                def classNames = res.getText('UTF-8').split(/\r?\n/).collect()  { String str -> str.trim() }

                for(className in classNames) {
                    try {
                        def cls = classLoader.loadClass(className)
                        if(AssetFile.isAssignableFrom(cls) ) {
                            if(!specifications.contains(cls))
                                specifications << (Class) cls
                        }
                        else {
                            log.warn("Asset specification $className not registered because it does not implement the AssetFile interface")
                        }
                    } catch (Throwable e) {
                        log.error("Error loading asset specification $className: $e.message", e)
                    }
                }
            }
            applyProcessors(classLoader)
        }
        return specifications
    }

    /**
     * Loads processor.specs files and appends {@link Processor} implementations to matching {@link AssetFile} implementations.
     *
     * @param classLoader The classloader
     */
    static void applyProcessors(ClassLoader classLoader = Thread.currentThread().contextClassLoader) {
        def resources = classLoader.getResources(PROCESSORS_RESOURCE_LOCATION)
        resources.each { URL res ->
            Properties prop = new Properties()
            prop.load(res.openStream())
            prop.keySet().each { processorName ->
                def processorClass = classLoader.loadClass(processorName)
                def specs = prop.getProperty(processorName).split(',')
                specs.each { specName ->
                    try {
                        def specCls = classLoader.loadClass(specName)
                        if(!specCls.processors.contains(processorClass)) {
                            specCls.processors << processorClass
                        }
                    } catch(e) {
                        //Spec doesnt exist this could be normal if it is for example supporting coffee too
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy