package org.codehaus.mojo.unix.util; /* * 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 fj.*; import fj.data.*; import static fj.data.List.*; import static fj.data.Option.*; import fj.pre.*; /** * @author <a href="mailto:trygvis@codehaus.org">Trygve Laugstøl</a> * @version $Id$ */ public class RelativePath implements Comparable<RelativePath> { public final String string; public final static RelativePath BASE = new RelativePath( "." ) { public RelativePath add( String string ) { String cleaned = clean( string ); if ( cleaned == null ) { return this; } return new RelativePath( cleaned ); } public boolean isBelowOrSame( RelativePath other ) { return other.isBase(); } public Option<RelativePath> subtract( RelativePath parent ) { if ( parent.isBase() ) { return some( BASE ); } return none(); } public List<String> toList() { return nil(); } public String asAbsolutePath( String basePath ) { return basePath; } public boolean isBase() { return true; } }; // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- public static final Ord<RelativePath> ord = Ord.ord( new F<RelativePath, F<RelativePath, Ordering>>() { public F<RelativePath, Ordering> f( final RelativePath a ) { return new F<RelativePath, Ordering>() { public Ordering f( RelativePath b ) { return Ord.stringOrd.compare( a.string, b.string ); } }; } } ); // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- private RelativePath( String string ) { this.string = string; } public RelativePath add( String string ) { string = clean( string ); if ( string == null ) { return this; } return new RelativePath( this.string + "/" + string ); } public String asAbsolutePath( String basePath ) { return basePath + ( basePath.endsWith( "/" ) ? "" : "/" ) + string; } public boolean isBase() { return false; } public String name() { int i = string.lastIndexOf( '/' ); if ( i == -1 ) { return string; } return string.substring( i + 1 ); } /** * Returns true if <code>other</code> is further down the path than this path. * <p/> * <ul> * <li>".".isBelowOrSame(..) -> true. Everything is below the base path</li> * <li>"..".isBelowOrSame(".") -> true. Everything is below the base path</li> * <li>"/foo".isBelowOrSame( "/foo") -> true</li> * <li>"/foo/bar".isBelowOrSame( "/foo") -> true</li> * <li>"/foo".isBelowOrSame( "/foo/bar") -> false</li> * </ul> */ public boolean isBelowOrSame( RelativePath parent ) { return parent.isBase() || // Everything is below or equal to the base path string.startsWith( parent.string ); } public Option<RelativePath> subtract( RelativePath parent ) { if ( isBelowOrSame( parent ) ) { if ( parent.isBase() || this.string.equals( parent.string ) ) { return some( this ); } return some( new RelativePath( this.string.substring( parent.string.length() + 1 ) ) ); } return none(); } public List<String> toList() { int i = string.lastIndexOf( '/' ); if ( i == -1 ) { return List.single( string ); } List<String> list = List.single( string.substring( i + 1 ) ); String s = string.substring( 0, i ); do { i = s.lastIndexOf( '/' ); if ( i == -1 ) { return list.cons( s ); } list = list.cons( s.substring( i + 1 ) ); s = s.substring( 0, i ); } while ( true ); } // ----------------------------------------------------------------------- // Static // ----------------------------------------------------------------------- public static RelativePath relativePath( String string ) { string = string == null ? "/" : string.trim(); String s = clean( string ); if ( s == null ) { return BASE; } return new RelativePath( s ); } static String clean( final String string ) { String s = removeDuplicateSlashes( string ); if ( isRoot( s ) ) { return null; } if ( s.startsWith( "./" ) ) { s = s.substring( 2 ); } else if ( s.startsWith( "/" ) ) { s = s.substring( 1 ); } if ( s.endsWith( "/." ) ) { s = s.substring( 0, s.length() - 2 ); } else if ( s.endsWith( "/" ) ) { s = s.substring( 0, s.length() - 1 ); } if ( isRoot( s ) ) { return null; } return s; } private static String removeDuplicateSlashes( String string ) { StringBuffer buffer = new StringBuffer(); boolean lastWasSlash = false; for ( int i = 0; i < string.length(); i++ ) { char c = string.charAt( i ); if ( c == '/' ) { if ( !lastWasSlash ) { buffer.append( c ); lastWasSlash = true; } } else { buffer.append( c ); lastWasSlash = false; } } return buffer.toString(); } private static boolean isRoot( String s ) { return s.length() == 0 || s.equals( "/" ) || s.equals( "." ); } // ----------------------------------------------------------------------- // Object Overrides // ----------------------------------------------------------------------- public boolean equals( Object o ) { if ( this == o ) { return true; } if ( !( o instanceof RelativePath ) ) { return false; } RelativePath path = (RelativePath) o; return string.equals( path.string ); } public int hashCode() { return string.hashCode(); } public String toString() { return string; } // ----------------------------------------------------------------------- // Comparable // ----------------------------------------------------------------------- public int compareTo( RelativePath other ) { return string.compareTo( other.string ); } }