package org.codehaus.mojo.unix;
/*
* The MIT License
*
* Copyright 2009 The Codehaus.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import static fj.Bottom.*;
import fj.*;
import fj.Function;
import static fj.Function.*;
import static fj.P.*;
import fj.data.*;
import static fj.data.Either.*;
import static fj.data.Option.*;
import static fj.data.Tree.*;
import static fj.data.TreeZipper.*;
import fj.pre.*;
import static fj.pre.Ord.*;
import org.codehaus.mojo.unix.UnixFsObject.*;
import org.codehaus.mojo.unix.util.*;
import static org.codehaus.mojo.unix.util.fj.FunctionF.flip2;
import org.codehaus.mojo.unix.util.fj.*;
/**
* @author <a href="mailto:trygvis@codehaus.org">Trygve Laugstøl</a>
* @version $Id$
*/
public class PackageFileSystem<A>
{
private final Fs fs = new Fs();
private final TreeZipper<PackageFileSystemObject<A>> root;
private final PackageFileSystemObject<A> defaultDirectory;
public static <A> PackageFileSystem<A> create( PackageFileSystemObject<A> root,
PackageFileSystemObject<A> defaultDirectory )
{
return new UglyPackageFileSystem<A>( fromTree( leaf( root ) ), defaultDirectory );
}
private PackageFileSystem( TreeZipper<PackageFileSystemObject<A>> root, PackageFileSystemObject<A> defaultDirectory )
{
Validate.validateNotNull( root, defaultDirectory );
this.root = root;
this.defaultDirectory = defaultDirectory;
}
private static class UglyPackageFileSystem<A>
extends PackageFileSystem<A>
{
public UglyPackageFileSystem( TreeZipper<PackageFileSystemObject<A>> root, PackageFileSystemObject<A> defaultDirectory )
{
super( root, defaultDirectory );
}
}
private class PrettyPackageFileSystem
extends PackageFileSystem<A>
{
public PrettyPackageFileSystem()
{
super( fromTree( prettyTree( root.toTree() ) ), defaultDirectory );
}
public PackageFileSystem<A> prettify()
{
return this;
}
}
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
public boolean hasPath( final RelativePath path )
{
List<String> paths = path.toList();
return paths.isEmpty() || find( root, paths ).isRight();
}
public Option<PackageFileSystemObject<A>> getObject( final RelativePath path )
{
if ( path.isBase() )
{
return some( root.getLabel() );
}
return find( root, path.toList() ).right().
map( TreeZipperF.<PackageFileSystemObject<A>>getLabel_() ).right().toOption();
}
public PackageFileSystem<A> addDirectory( PackageFileSystemObject<A> object )
{
List<String> names = object.getUnixFsObject().path.toList();
// String name = names.isEmpty() ? root.getLabel().name : names.reverse().head();
// PackageFileSystemObject<A> settings = p( (UnixFsObject) directory, a );
Tree<PackageFileSystemObject<A>> newChild = leaf( object );
Either<TreeZipper<PackageFileSystemObject<A>>, TreeZipper<PackageFileSystemObject<A>>> either = findAndCreateParentsFor( names );
return either.
either( compose( fs.navigateToRootAndCreatePackageFileSystem, curry( flip2( fs.addChild ), newChild ) ),
compose( fs.navigateToRootAndCreatePackageFileSystem, curry( fs.mutateExisting, object ) ) );
}
public PackageFileSystem<A> addFile( PackageFileSystemObject<A> file )
{
if ( file.getUnixFsObject().path.isBase() )
{
throw error( "addFile on base path." );
}
List<String> names = file.getUnixFsObject().path.toList();
Tree<PackageFileSystemObject<A>> newChild = leaf( file );
return findAndCreateParentsFor( names ).
either( compose( fs.navigateToRootAndCreatePackageFileSystem, curry( flip2( fs.addChild ), newChild ) ),
compose( fs.navigateToRootAndCreatePackageFileSystem, curry( fs.mutateExisting, file ) ) );
}
public PackageFileSystem<A> addSymlink( PackageFileSystemObject<A> symlink )
{
if ( symlink.getUnixFsObject().path.isBase() )
{
throw error( "addSymlink on base path." );
}
List<String> names = symlink.getUnixFsObject().path.toList();
Tree<PackageFileSystemObject<A>> newChild = leaf( symlink );
return findAndCreateParentsFor( names ).
either( compose( fs.navigateToRootAndCreatePackageFileSystem, curry( flip2( fs.addChild ), newChild ) ),
compose( fs.navigateToRootAndCreatePackageFileSystem, curry( fs.mutateExisting, symlink ) ) );
}
/**
* Applies the <code>f</code> to all objects in this filesystem.
*
* TODO: Shouldn't it just return a new UnixFsObject?
*/
public PackageFileSystem<A> apply( final F2<UnixFsObject, FileAttributes, FileAttributes> f )
{
TreeZipper<PackageFileSystemObject<A>> root = this.root.map( new F<PackageFileSystemObject<A>, PackageFileSystemObject<A>>()
{
public PackageFileSystemObject<A> f( PackageFileSystemObject<A> node )
{
final FileAttributes fileAttributes = f.f( node.getUnixFsObject(), node.getUnixFsObject().getFileAttributes() );
// TODO: check if the attributes was modified
return node.setFileAttributes( fileAttributes );
}
} );
return new UglyPackageFileSystem<A>( root, defaultDirectory );
}
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
// Applies the function to each path, and selects the last one that was some()
F2<P2<RelativePath, FileAttributes>, F<RelativePath, Option<FileAttributes>>, P2<RelativePath, FileAttributes>>
fileAttributeFolder =
new F2<P2<RelativePath, FileAttributes>, F<RelativePath, Option<FileAttributes>>, P2<RelativePath, FileAttributes>>()
{
public P2<RelativePath, FileAttributes> f( final P2<RelativePath, FileAttributes> previous,
final F<RelativePath, Option<FileAttributes>> transformer )
{
return previous.map2( new F<FileAttributes, FileAttributes>()
{
public FileAttributes f( FileAttributes fileAttributes )
{
return transformer.f( previous._1() ).orSome( previous._2() );
}
} );
}
};
public List<PackageFileSystemObject<A>> toList()
{
return root.toTree().flatten();
}
Tree<PackageFileSystemObject<A>> getTree()
{
return root.toTree();
}
public PackageFileSystem<A> prettify()
{
return new PrettyPackageFileSystem();
}
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
/**
* Returns right with the node if it was just created, or left with the parent and the remaining path
*/
private Either<TreeZipper<PackageFileSystemObject<A>>, TreeZipper<PackageFileSystemObject<A>>> findAndCreateParentsFor( List<String> paths )
{
// If paths is empty, then we're looking for the root
if ( paths.isEmpty() )
{
return right( root );
}
return find( root, paths ).left().
map( fs.createParentsFor );
}
private TreeZipper<PackageFileSystemObject<A>> createParentsFor( P2<TreeZipper<PackageFileSystemObject<A>>, List<String>> p2 )
{
TreeZipper<PackageFileSystemObject<A>> zipper = p2._1();
List<String> paths = p2._2();
RelativePath path = zipper.getLabel().getUnixFsObject().path;
// Create all but the last path element
while ( paths.isNotEmpty() && paths.tail().isNotEmpty() )
{
path = path.add( paths.head() );
zipper = addChild( zipper, leaf( defaultDirectory.setPath( path ) ) );
paths = paths.tail();
}
return zipper;
}
/**
* @param parent A zipper focused on the closest, existing parent
* @param node The node to insert
*/
public TreeZipper<PackageFileSystemObject<A>> addChild( TreeZipper<PackageFileSystemObject<A>> parent, Tree<PackageFileSystemObject<A>> node )
{
// System.out.println( "Adding to " + parent.getLabel().object.path + ": " + node.root().object.path + " which is a " + node.root().object.getClass() );
if ( parent.getLabel().getUnixFsObject() instanceof Directory )
{
return parent.insertDownFirst( node );
}
throw error( "Parent has to be a directory, parent: " + parent.getLabel().getUnixFsObject().path );
}
/**
* Returns right with the node, or left with the closest parent and the remaining path.
*/
private Either<P2<TreeZipper<PackageFileSystemObject<A>>, List<String>>, TreeZipper<PackageFileSystemObject<A>>> find( final TreeZipper<PackageFileSystemObject<A>> parent,
final List<String> paths )
{
String head = paths.head();
List<String> tail = paths.tail();
Option<TreeZipper<PackageFileSystemObject<A>>> option = parent.firstChild();
while ( option.isSome() )
{
TreeZipper<PackageFileSystemObject<A>> zipper = option.some();
if ( zipper.getLabel().getUnixFsObject().path.name().equals( head ) )
{
if ( tail.isEmpty() )
{
// We found the parent
// TODO: or did this actually find the child?
return right( zipper );
}
else
{
return find( zipper, tail );
}
}
option = zipper.right();
}
return left( p( parent, paths ) );
}
public static <A> Ordering compareTreeNodes( Tree<PackageFileSystemObject<A>> a, Tree<PackageFileSystemObject<A>> b )
{
return RelativePath.ord.compare( a.root().getUnixFsObject().path, b.root().getUnixFsObject().path );
}
private static <A> Tree<PackageFileSystemObject<A>> prettyTree( Tree<PackageFileSystemObject<A>> root )
{
List<Tree<PackageFileSystemObject<A>>> forest = root.subForest().
sort( PackageFileSystem.<A>treeOrd() ).
map( new F<Tree<PackageFileSystemObject<A>>, Tree<PackageFileSystemObject<A>>>()
{
public Tree<PackageFileSystemObject<A>> f( Tree<PackageFileSystemObject<A>> child )
{
return prettyTree( child );
}
} );
return Tree.node( root.root(), forest );
}
public PackageFileSystem<A> navigateToRootAndCreatePackageFileSystem( TreeZipper<PackageFileSystemObject<A>> zipper )
{
return new UglyPackageFileSystem<A>( zipper.root(), defaultDirectory );
}
private static <A> Ord<Tree<PackageFileSystemObject<A>>> treeOrd()
{
return ord( curry( PackageFileSystem.<A>compareTreeNodes() ) );
}
static <A> F2<Tree<PackageFileSystemObject<A>>, Tree<PackageFileSystemObject<A>>, Ordering> compareTreeNodes()
{
return new F2<Tree<PackageFileSystemObject<A>>, Tree<PackageFileSystemObject<A>>, Ordering>()
{
public Ordering f( Tree<PackageFileSystemObject<A>> a, Tree<PackageFileSystemObject<A>> b )
{
return compareTreeNodes( a, b );
}
};
}
/**
* First-order versions
*/
private class Fs
{
F2<String, String, String> last = new F2<String, String, String>()
{
public String f( String s, String s1 )
{
return s1;
}
};
F<P2<TreeZipper<PackageFileSystemObject<A>>, List<String>>, TreeZipper<PackageFileSystemObject<A>>> createParentsFor =
new F<P2<TreeZipper<PackageFileSystemObject<A>>, List<String>>, TreeZipper<PackageFileSystemObject<A>>>()
{
public TreeZipper<PackageFileSystemObject<A>> f( P2<TreeZipper<PackageFileSystemObject<A>>, List<String>> p2 )
{
return createParentsFor( p2 );
}
};
F<TreeZipper<PackageFileSystemObject<A>>, PackageFileSystem<A>> navigateToRootAndCreatePackageFileSystem =
new F<TreeZipper<PackageFileSystemObject<A>>, PackageFileSystem<A>>()
{
public PackageFileSystem<A> f( TreeZipper<PackageFileSystemObject<A>> treeZipper )
{
return navigateToRootAndCreatePackageFileSystem( treeZipper );
}
};
F2<TreeZipper<PackageFileSystemObject<A>>, Tree<PackageFileSystemObject<A>>, TreeZipper<PackageFileSystemObject<A>>> addChild =
new F2<TreeZipper<PackageFileSystemObject<A>>, Tree<PackageFileSystemObject<A>>, TreeZipper<PackageFileSystemObject<A>>>()
{
public TreeZipper<PackageFileSystemObject<A>> f( TreeZipper<PackageFileSystemObject<A>> parent, Tree<PackageFileSystemObject<A>> node )
{
return addChild( parent, node );
}
};
// public F2<PackageFileSystemObject<A>, PackageFileSystemObject<A>, PackageFileSystemObject<A>> apply_()
// {
// return new F2<PackageFileSystemObject<A>, PackageFileSystemObject<A>, PackageFileSystemObject<A>>()
// {
// public PackageFileSystemObject<A> f( PackageFileSystemObject<A> node, PackageFileSystemObject<A> p2 )
// {
// return node.apply( p2 );
// }
// };
// }
// public F<PackageFileSystemObject<A>, PackageFileSystemObject<A>> toP2_()
// {
// return new F<PackageFileSystemObject<A>, PackageFileSystemObject<A>>()
// {
// public PackageFileSystemObject<A> f( PackageFileSystemObject<A> node )
// {
// return node.toP2();
// }
// };
// }
F2<PackageFileSystemObject<A>, TreeZipper<PackageFileSystemObject<A>>, TreeZipper<PackageFileSystemObject<A>>> mutateExisting =
new F2<PackageFileSystemObject<A>, TreeZipper<PackageFileSystemObject<A>>, TreeZipper<PackageFileSystemObject<A>>>()
{
public TreeZipper<PackageFileSystemObject<A>> f( PackageFileSystemObject<A> newSettings,
TreeZipper<PackageFileSystemObject<A>> nodeTreeZipper )
{
return nodeTreeZipper.modifyLabel(
Function.<PackageFileSystemObject<A>, PackageFileSystemObject<A>>constant( newSettings ) );
}
};
}
}