/* * Copyright (C) 2008 Laurent Caillette * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.novelang.outfit.loader; import java.io.File; import java.io.InputStream; import java.net.MalformedURLException; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * A {@link ResourceLoader} made of multiple {@link AbstractResourceLoader}s. * It tries all of them in sequence and throws a {@link ResourceNotFoundException} * referencing every place it tried. * <p> * The {@link org.novelang.outfit.loader.ClasspathResourceLoader}s are always tried last. * * @author Laurent Caillette */ public class CompositeResourceLoader implements ResourceLoader { private final ImmutableList< AbstractResourceLoader > preferredResourceLoaders; private final ImmutableList< ClasspathResourceLoader > classpathResourceLoaders ; public CompositeResourceLoader( final AbstractResourceLoader... resourceLoaders ) { this( ImmutableList.< AbstractResourceLoader >builder().add( resourceLoaders ).build() ) ; } /** * Trying to load from {@link AbstractResourceLoader}s in the order of the list. * * @param resourceLoaders a non-null, non-empty {@code List}. */ public CompositeResourceLoader( final ImmutableList< AbstractResourceLoader > resourceLoaders ) { checkArgument( ! resourceLoaders.isEmpty() ) ; final ImmutableList.Builder< AbstractResourceLoader > preferredResourceLoaderBuilder = ImmutableList.builder() ; final ImmutableList.Builder< ClasspathResourceLoader > classpathResourceLoaderBuilder = ImmutableList.builder() ; for( final AbstractResourceLoader resourceLoader : resourceLoaders ) { if( resourceLoader instanceof ClasspathResourceLoader ) { classpathResourceLoaderBuilder.add( ( ClasspathResourceLoader ) resourceLoader ) ; } else { preferredResourceLoaderBuilder.add( resourceLoader ) ; } } preferredResourceLoaders = preferredResourceLoaderBuilder.build() ; classpathResourceLoaders = classpathResourceLoaderBuilder.build() ; } /*package*/ ImmutableList< AbstractResourceLoader > getAll() { return new ImmutableList.Builder< AbstractResourceLoader >() .addAll( preferredResourceLoaders ) .addAll( classpathResourceLoaders ) .build() ; } /** * First {@link AbstractResourceLoader}s tried first. */ public CompositeResourceLoader( final ResourceLoader first, final ResourceLoader second ) { this( ImmutableList.< AbstractResourceLoader >builder() .addAll( explode( first ) ) .addAll( explode( second ) ) .build() ) ; } private static ImmutableList< AbstractResourceLoader > explode( final ResourceLoader resourceLoader ) { checkNotNull( resourceLoader ) ; if( resourceLoader instanceof CompositeResourceLoader ) { return ( ( CompositeResourceLoader ) resourceLoader ).getAll() ; } else if( resourceLoader instanceof AbstractResourceLoader ) { return ImmutableList.of( ( AbstractResourceLoader ) resourceLoader ) ; } else { throw new IllegalArgumentException( "Unsupported: " + resourceLoader.getClass().getName() ) ; } } @Override public InputStream getInputStream( final ResourceName resourceName ) throws ResourceNotFoundException { final ImmutableList.Builder< String > missesBuilder = ImmutableList.builder() ; InputStream inputStream ; inputStream = tryResourceLoaders( preferredResourceLoaders, missesBuilder, resourceName ) ; if( inputStream != null ) { return inputStream ; } inputStream = tryResourceLoaders( classpathResourceLoaders, missesBuilder, resourceName ) ; if( inputStream != null ) { return inputStream ; } final ImmutableList< String > misses = missesBuilder.build() ; throw new ResourceNotFoundException( resourceName, Joiner.on( "\n" ).join( misses ) ) ; } private static InputStream tryResourceLoaders( final ImmutableList< ? extends AbstractResourceLoader > resourceLoaders, final ImmutableList.Builder< String > missesBuilder, final ResourceName resourceName ) { for( final AbstractResourceLoader resourceLoader : resourceLoaders ) { final InputStream inputStream = resourceLoader.maybeGetInputStream( resourceName ) ; if( inputStream == null ) { missesBuilder.add( resourceLoader.toString() ) ; } else { return inputStream ; } } return null ; } @Override public String toString() { return getClass().getSimpleName() ; } public String getMultilineDescription() { final StringBuilder stringBuilder = new StringBuilder( this.toString() + ", searching (in order):" ) ; for( final AbstractResourceLoader resourceLoader : getAll() ) { stringBuilder.append( "\n " ) ; // stringBuilder.append( resourceLoader.toString() ) ; stringBuilder.append( resourceLoader.getMultilineDescription() ) ; } return stringBuilder.toString() ; } public static CompositeResourceLoader create( final String resourcePath, final File... directories ) { final ImmutableList.Builder< UrlResourceLoader > urlResourceLoaders = ImmutableList.builder() ; for( final File directory : directories ) { try { urlResourceLoaders.add( new UrlResourceLoader( directory.toURI().toURL() ) ) ; } catch( MalformedURLException e ) { throw new RuntimeException( e ) ; } } return create( resourcePath, urlResourceLoaders.build() ) ; } public static CompositeResourceLoader create( final String resourcePath, final ImmutableList< UrlResourceLoader > urlResourceLoaders ) { final ImmutableList.Builder< AbstractResourceLoader > resourceLoaders = ImmutableList.builder() ; resourceLoaders.addAll( urlResourceLoaders ) ; resourceLoaders.add( new ClasspathResourceLoader( resourcePath ) ) ; return new CompositeResourceLoader( resourceLoaders.build() ) ; } }