/* * Copyright (C) 2011 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.common.filefixture; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.novelang.testing.DirectoryFixture; /** * Performs filesystem-oriented operations on {@link Resource} and {@link Directory}. * * @author Laurent Caillette */ /*package*/ abstract class AbstractResourceInstaller { public abstract File getTargetDirectory() ; /** * Copies the given {@code directory} into the {@code physicalTargetDirectory}. * * @param directory a non-null object. * @return created directory. * @see #copyTo(Directory, java.io.File) */ public File copy( final Directory directory ) { final File target = createPhysicalDirectory( getTargetDirectory(), directory.getName() ) ; try { copyTo( directory, target ) ; } catch( IOException e ) { throw new RuntimeException( e ) ; } return target ; } /** * Copies the given {@code singleResource} into the {@code physicalTargetDirectory}. * * @param singleResource a non-null object. * @return created file referencing {@code singleResource}. */ public File copy( final Resource singleResource ) { try { return copyTo( singleResource, getTargetDirectory() ) ; } catch( IOException e ) { throw new RuntimeException( e ) ; } } /** * Copies the content of the given {@code directory} into the {@code physicalTargetDirectory}. * * @param directory a non-null object. * @see #copy(Directory) */ public void copyContent( final Directory directory ) { try { copyTo( directory, getTargetDirectory() ) ; } catch( IOException e ) { throw new RuntimeException( e ) ; } } /** * Copies the content of given {@code directory} into the {@code physicalTargetDirectory}. * @param directory a non-null object. * @param physicalTargetDirectory a non-null object representing an existing directory. */ private static void copyTo( final Directory directory, final File physicalTargetDirectory ) throws IOException { for( final Directory subdirectory : directory.getSubdirectories() ) { final File physicalDirectory = createPhysicalDirectory( physicalTargetDirectory, subdirectory.getName() ) ; copyTo( subdirectory, physicalDirectory ) ; } for( final Resource resource : directory.getResources() ) { copyTo( resource, physicalTargetDirectory ) ; } } public File createFileObject( final Directory scope, final SchemaNode node ) { Preconditions.checkArgument( ResourceSchema.isParentOfOrSameAs( scope, node ), "Directory '%s' expected to be parent of '%s'", scope.getAbsoluteResourceName(), node.getAbsoluteResourceName() ) ; final List< Directory > reverseParentHierarchy = Lists.newArrayList() ; if ( node != scope ) { Directory parent = node.getParent() ; while( true ) { if( null == parent ) { break ; } else { if( scope == parent ) { // reverseParentHierarchy.add( parent ) ; break ; } else { reverseParentHierarchy.add( parent ) ; parent = parent.getParent() ; } } } } File result = getTargetDirectory(); final Iterable< Directory > parentHierarchy = Iterables.reverse( reverseParentHierarchy ) ; for( final Directory directory : parentHierarchy ) { result = new File( result, directory.getName() ) ; } result = new File( result, node.getName() ) ; return result ; } /** * Creates a {@code File} object corresponding to a resource with a relocated path. * * @param node a non-null object. * @return a non-null object. */ public File createFileObject( final SchemaNode node ) { final List< Directory > reverseParentHierarchy = Lists.newArrayList() ; Directory parent = node.getParent() ; while( true ) { if( null == parent ) { break ; } else { reverseParentHierarchy.add( parent ) ; parent = parent.getParent() ; } } File result = getTargetDirectory(); final Iterable< Directory > parentHierarchy = Iterables.reverse( reverseParentHierarchy ) ; for( final Directory directory : parentHierarchy ) { result = new File( result, directory.getName() ) ; } result = new File( result, node.getName() ) ; if( ! result.exists() ) { throw new AssertionError( "Should exist: '" + result.getPath() + "'" ) ; } return result ; } /** * Copies given resource to some target directory, retaining directory hierarchy above, up to the * {@param scope} directory (not included). * <pre> copyScoped( ResourceTree.D0.dir, ResourceTree.D0_1_0.dir ) + tree --> + somewhere + d0 < scope + d0.1 + d0.0 + d0.1.0 r0.0.0.txt result > r0.1.0.0.txt + d0.1 r0.1.0.txt + d0.1.0 r0.1.0.0.txt < target </pre> * @param scope a non-null object. * @param resource a non-null resource under {@code scope}. * @return a {@code File} object referencing the node to copy. */ public File copyScoped( final Directory scope, final Resource resource ) { Preconditions.checkArgument( ResourceSchema.isParentOf( scope, resource ) ) ; final List< Directory > reverseParentHierarchy = Lists.newArrayList() ; Directory parent = resource.getParent() ; while( true ) { if( scope == parent ) { break ; } else { reverseParentHierarchy.add( parent ) ; parent = parent.getParent() ; } } final Iterable< Directory > parentHierarchy = Iterables.reverse( reverseParentHierarchy ) ; File result = null ; File target = getTargetDirectory(); for( final Directory directory : parentHierarchy ) { target = createPhysicalDirectory( target, directory.getName() ) ; } try { result = copyTo( resource, target ) ; } catch( IOException e ) { throw new RuntimeException( e ) ; } return result ; } /** * Does the same as {@link #copyWithPath(Directory, Resource)} with a path being * the parent's {@code Directory} of the resource. */ public File copyWithPath( final Resource resource ) { final Directory root = resource.getRoot() ; if( root == null ) { return copy( resource ) ; } else { return copyWithPath( root, resource ) ; } } /** * Copies given resource to some target directory, retaining directory hierarchy above, up to the * {@param scope} directory (not included). * <pre> copyScoped( ResourceTree.D0.dir, ResourceTree.D0_1_0.dir ) + tree --> + somewhere + d0 < scope + d0 + d0.0 + d0.1 r0.0.0.txt + d0.1.0 + d0.1 result > r0.1.0.0.txt r0.1.0.txt + d0.1.0 r0.1.0.0.txt < target </pre> * @param scope a non-null object. * @param resource a non-null resource under {@code scope}. * @return a {@code File} object referencing the node to copy. */ public File copyWithPath( final Directory scope, final Resource resource ) { Preconditions.checkArgument( ResourceSchema.isParentOf( scope, resource ) ) ; final List< Directory > reverseParentHierarchy = Lists.newArrayList() ; Directory parent = resource.getParent() ; Directory oldParent = null ; while( true ) { if( scope == oldParent ) { break ; } oldParent = parent ; reverseParentHierarchy.add( parent ) ; parent = parent.getParent() ; } final Iterable< Directory > parentHierarchy = Iterables.reverse( reverseParentHierarchy ) ; File result = null ; File target = getTargetDirectory(); for( final Directory directory : parentHierarchy ) { target = createPhysicalDirectory( target, directory.getName() ) ; } try { result = copyTo( resource, target ) ; } catch( IOException e ) { throw new RuntimeException( e ) ; } return result ; } /** * Copies given directory to some target directory, retaining directory hierarchy above, up to the * {@param scope} directory (included). * <pre> copyScoped( ResourceTree.D0_1.dir, ResourceTree.D0_1_0.dir ) + tree --> + somewhere + d0 + d0.1 + d0.0 < scope result > + d0.1.0 r0.0.0.txt r0.1.0.0.txt + d0.1 r0.1.0.txt + d0.1.0 < target r0.1.0.0.txt </pre> * @param scope a non-null object. * @param origin a non-null object. * @return a {@code File} object referencing the node to copy. */ public File copyScoped( final Directory scope, final Directory origin ) { Preconditions.checkArgument( ResourceSchema.isParentOf( scope, origin ) ) ; final List< Directory > reverseParentHierarchy = Lists.newArrayList() ; reverseParentHierarchy.add( origin ) ; Directory parent = origin.getParent() ; while( true ) { if( scope == parent ) { break ; } else { reverseParentHierarchy.add( parent ) ; parent = parent.getParent() ; } } final Iterable< Directory > parentHierarchy = Iterables.reverse( reverseParentHierarchy ) ; File result = null ; File target = getTargetDirectory() ; for( final Directory directory : parentHierarchy ) { target = createPhysicalDirectory( target, directory.getName() ) ; if( origin == directory ) { result = target ; } } try { copyTo( origin, target ) ; } catch( IOException e ) { throw new RuntimeException( e ) ; } if( null == result ) { throw new IllegalStateException( "Ooops!" ) ; } return result ; } // ================ // Internal cooking // ================ private static File copyTo( final Resource resource, final File physicalTargetDirectory ) throws IOException { final File physicalFile = new File( physicalTargetDirectory, resource.getName() ) ; final FileOutputStream fileOutputStream = new FileOutputStream( physicalFile ) ; IOUtils.copy( resource.getInputStream(), fileOutputStream ) ; fileOutputStream.flush() ; fileOutputStream.close() ; return physicalFile ; } private static File createPhysicalDirectory( final File physicalParentDirectory, final String name ) { final File physicalDirectory = new File( physicalParentDirectory, name ) ; if( ! physicalDirectory.mkdir() ) { if( ! FileUtils.waitFor( physicalDirectory, DirectoryFixture.TIMEOUT_SECONDS ) ) { throw new AssertionError( "Failed to create file " + physicalDirectory.getAbsolutePath() ) ; } } return physicalDirectory ; } }