268 lines
8.6 KiB
Java
268 lines
8.6 KiB
Java
/*
|
|
* This file is part of Applied Energistics 2.
|
|
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
|
|
*
|
|
* Applied Energistics 2 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.
|
|
*
|
|
* Applied Energistics 2 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
|
*/
|
|
|
|
package appeng.recipes.loader;
|
|
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
import java.net.URL;
|
|
import java.net.URLDecoder;
|
|
import java.util.Collection;
|
|
import java.util.Enumeration;
|
|
import java.util.HashSet;
|
|
import java.util.jar.JarEntry;
|
|
import java.util.jar.JarFile;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
import com.google.common.base.Preconditions;
|
|
|
|
import org.apache.commons.io.FileUtils;
|
|
|
|
|
|
/**
|
|
* copies recipes in jars onto file system includes the readme, needs to be modified if other files needs to be handled
|
|
*
|
|
* @author thatsIch
|
|
* @version rv3 - 11.05.2015
|
|
* @since rv3 11.05.2015
|
|
*/
|
|
public class RecipeResourceCopier
|
|
{
|
|
/**
|
|
* Most expected size of recipes found
|
|
*/
|
|
private static final int INITIAL_RESOURCE_CAPACITY = 20;
|
|
private static final Pattern DOT_COMPILE_PATTERN = Pattern.compile( ".", Pattern.LITERAL );
|
|
private static final String FILE_PROTOCOL = "file";
|
|
private static final String CLASS_EXTENSION = ".class";
|
|
private static final String JAR_PROTOCOL = "jar";
|
|
private static final String UTF_8_ENCODING = "UTF-8";
|
|
|
|
/**
|
|
* copy source in the jar
|
|
*/
|
|
private final String root;
|
|
|
|
/**
|
|
* @param root source root folder of the recipes inside the jar.
|
|
*
|
|
* @throws NullPointerException if root is <tt>null</tt>
|
|
*/
|
|
public RecipeResourceCopier( @Nonnull final String root )
|
|
{
|
|
Preconditions.checkNotNull( root );
|
|
|
|
this.root = root;
|
|
}
|
|
|
|
/**
|
|
* copies recipes found in the root to destination.
|
|
*
|
|
* @param identifier only copy files which end with the identifier
|
|
* @param destination destination folder to which the recipes are copied to
|
|
*
|
|
* @throws URISyntaxException {@see #getResourceListing}
|
|
* @throws IOException {@see #getResourceListing} and if copying the detected resource to file is not possible
|
|
* @throws NullPointerException if either parameter is <tt>null</tt>
|
|
* @throws IllegalArgumentException if destination is not a directory
|
|
*/
|
|
public void copyTo( @Nonnull final String identifier, @Nonnull final File destination ) throws URISyntaxException, IOException
|
|
{
|
|
Preconditions.checkNotNull( destination );
|
|
Preconditions.checkArgument( destination.isDirectory() );
|
|
|
|
this.copyTo( identifier, destination, this.root );
|
|
}
|
|
|
|
/**
|
|
* @param destination destination folder to which the recipes are copied to
|
|
* @param directory the folder to copy.
|
|
*
|
|
* @throws URISyntaxException {@see #getResourceListing}
|
|
* @throws IOException {@see #getResourceListing} and if copying the detected resource to file is not possible
|
|
* @see {RecipeResourceCopier#copyTo(File)}
|
|
*/
|
|
private void copyTo( @Nonnull final String identifier, @Nonnull final File destination, @Nonnull final String directory ) throws URISyntaxException, IOException
|
|
{
|
|
assert identifier != null;
|
|
assert destination != null;
|
|
assert directory != null;
|
|
|
|
final Class<? extends RecipeResourceCopier> copierClass = this.getClass();
|
|
final String[] listing = this.getResourceListing( copierClass, directory );
|
|
for( final String list : listing )
|
|
{
|
|
if( list.endsWith( identifier ) )
|
|
{
|
|
// generate folder before the file is copied so no empty folders will be generated
|
|
FileUtils.forceMkdir( destination );
|
|
|
|
this.copyFile( destination, directory, list );
|
|
}
|
|
else if( !list.contains( "." ) )
|
|
{
|
|
final File subDirectory = new File( destination, list );
|
|
|
|
this.copyTo( identifier, subDirectory, directory + list + "/" );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copies a single file inside a folder to the destination.
|
|
*
|
|
* @param destination folder to which the file is copied to
|
|
* @param directory the directory containing the file
|
|
* @param fileName the file to copy
|
|
*
|
|
* @throws IOException if copying the file is not possible
|
|
*/
|
|
private void copyFile( @Nonnull final File destination, @Nonnull final String directory, @Nonnull final String fileName ) throws IOException
|
|
{
|
|
assert destination != null;
|
|
assert directory != null;
|
|
assert fileName != null;
|
|
|
|
final Class<? extends RecipeResourceCopier> copierClass = this.getClass();
|
|
final InputStream inStream = copierClass.getResourceAsStream( '/' + directory + fileName );
|
|
final File outFile = new File( destination, fileName );
|
|
|
|
if( !outFile.exists() && inStream != null )
|
|
{
|
|
FileUtils.copyInputStreamToFile( inStream, outFile );
|
|
inStream.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List directory contents for a resource folder. Not recursive. This is basically a brute-force implementation.
|
|
* Works for regular files and also JARs.
|
|
*
|
|
* @param clazz Any java class that lives in the same place as the resources you want.
|
|
* @param path Should end with "/", but not start with one.
|
|
*
|
|
* @return Just the name of each member item, not the full paths.
|
|
*
|
|
* @throws URISyntaxException if it is a file path and the URL can not be converted to URI
|
|
* @throws IOException if jar path can not be decoded
|
|
* @throws UnsupportedOperationException if it is neither in jar nor in file path
|
|
*/
|
|
@Nonnull
|
|
private String[] getResourceListing( @Nonnull final Class<?> clazz, @Nonnull final String path ) throws URISyntaxException, IOException
|
|
{
|
|
assert clazz != null;
|
|
assert path != null;
|
|
|
|
final ClassLoader classLoader = clazz.getClassLoader();
|
|
if( classLoader == null )
|
|
{
|
|
throw new IllegalStateException( "ClassLoader was not found. It was probably loaded at a inappropriate time" );
|
|
}
|
|
|
|
URL dirURL = classLoader.getResource( path );
|
|
if( dirURL != null )
|
|
{
|
|
final String protocol = dirURL.getProtocol();
|
|
if( protocol.equals( FILE_PROTOCOL ) )
|
|
{
|
|
// A file path: easy enough
|
|
|
|
final URI uriOfURL = dirURL.toURI();
|
|
final File fileOfURI = new File( uriOfURL );
|
|
final String[] filesAndDirectoriesOfURI = fileOfURI.list();
|
|
|
|
if( filesAndDirectoriesOfURI == null )
|
|
{
|
|
throw new IllegalStateException( "Files and Directories were illegal. Either an abstract pathname does not denote a directory, or an I/O error occured." );
|
|
}
|
|
else
|
|
{
|
|
return filesAndDirectoriesOfURI;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( dirURL == null )
|
|
{
|
|
|
|
/*
|
|
* In case of a jar file, we can't actually find a directory.
|
|
* Have to assume the same jar as clazz.
|
|
*/
|
|
final String className = clazz.getName();
|
|
final Matcher matcher = DOT_COMPILE_PATTERN.matcher( className );
|
|
final String me = matcher.replaceAll( "/" ) + CLASS_EXTENSION;
|
|
dirURL = classLoader.getResource( me );
|
|
}
|
|
|
|
if( dirURL != null )
|
|
{
|
|
|
|
final String protocol = dirURL.getProtocol();
|
|
if( protocol.equals( JAR_PROTOCOL ) )
|
|
{
|
|
/* A JAR path */
|
|
final String dirPath = dirURL.getPath();
|
|
final String jarPath = dirPath.substring( 5, dirPath.indexOf( '!' ) ); // strip out only
|
|
// the JAR file
|
|
final JarFile jar = new JarFile( URLDecoder.decode( jarPath, UTF_8_ENCODING ) );
|
|
try
|
|
{
|
|
final Enumeration<JarEntry> entries = jar.entries(); // gives ALL entries in jar
|
|
final Collection<String> result = new HashSet<>( INITIAL_RESOURCE_CAPACITY ); // avoid
|
|
// duplicates
|
|
|
|
// in case it is a
|
|
// subdirectory
|
|
while( entries.hasMoreElements() )
|
|
{
|
|
final JarEntry entry = entries.nextElement();
|
|
final String entryFullName = entry.getName();
|
|
if( entryFullName.startsWith( path ) )
|
|
{ // filter according to the path
|
|
String entryName = entryFullName.substring( path.length() );
|
|
final int checkSubDir = entryName.indexOf( '/' );
|
|
if( checkSubDir >= 0 )
|
|
{
|
|
// if it is a subdirectory, we just return the directory name
|
|
entryName = entryName.substring( 0, checkSubDir );
|
|
}
|
|
result.add( entryName );
|
|
}
|
|
}
|
|
|
|
return result.toArray( new String[result.size()] );
|
|
}
|
|
finally
|
|
{
|
|
jar.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new UnsupportedOperationException( "Cannot list files for URL " + dirURL );
|
|
}
|
|
}
|