blob: bee85b324485b706ddb41a78fc977e6b6da67492 [file] [log] [blame]
package org.apache.maven.wagon;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.util.StringTokenizer;
/**
* Various path (URL) manipulation routines
*
* @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
*
*/
public final class PathUtils
{
private PathUtils()
{
}
/**
* Returns the directory path portion of a file specification string.
* Matches the equally named unix command.
*
* @return The directory portion excluding the ending file separator.
*/
public static String dirname( final String path )
{
final int i = path.lastIndexOf( "/" );
return ( ( i >= 0 ) ? path.substring( 0, i ) : "" );
}
/**
* Returns the filename portion of a file specification string.
*
* @return The filename string with extension.
*/
public static String filename( final String path )
{
final int i = path.lastIndexOf( "/" );
return ( ( i >= 0 ) ? path.substring( i + 1 ) : path );
}
public static String[] dirnames( final String path )
{
final String dirname = PathUtils.dirname( path );
return split( dirname, "/", -1 );
}
private static String[] split( final String str, final String separator, final int max )
{
final StringTokenizer tok;
if ( separator == null )
{
// Null separator means we're using StringTokenizer's default
// delimiter, which comprises all whitespace characters.
tok = new StringTokenizer( str );
}
else
{
tok = new StringTokenizer( str, separator );
}
int listSize = tok.countTokens();
if ( max > 0 && listSize > max )
{
listSize = max;
}
final String[] list = new String[listSize];
int i = 0;
int lastTokenBegin;
int lastTokenEnd = 0;
while ( tok.hasMoreTokens() )
{
if ( max > 0 && i == listSize - 1 )
{
// In the situation where we hit the max yet have
// tokens left over in our input, the last list
// element gets all remaining text.
final String endToken = tok.nextToken();
lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
list[i] = str.substring( lastTokenBegin );
break;
}
else
{
list[i] = tok.nextToken();
lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
lastTokenEnd = lastTokenBegin + list[i].length();
}
i++;
}
return list;
}
/**
* Return the host name (Removes protocol and path from the URL) E.g: for input
* <code>http://www.codehause.org</code> this method will return <code>www.apache.org</code>
*
* @param url the url
* @return the host name
*/
public static String host( final String url )
{
if ( url == null || url.length() == 0 )
{
return "localhost";
}
String authorization = authorization( url );
int index = authorization.indexOf( '@' );
String host = ( index >= 0 ) ? authorization.substring( index + 1 ) : authorization;
// In case we have IPv6 in the host portion of url
// we have to remove brackets '[' and ']'
return ( ( host.charAt( 0 ) == '[' ) && ( host.charAt( host.length() - 1 ) == ']' ) )
? host.substring( 1, host.length() - 1 )
: host;
}
/**
* This was changed from private to package local so that it can be unit tested.
*/
static String authorization( final String url )
{
if ( url == null )
{
return "localhost";
}
String protocol = PathUtils.protocol( url );
if ( protocol == null || protocol.equalsIgnoreCase( "file" ) )
{
return "localhost";
}
String host = url;
if ( protocol.equalsIgnoreCase( "scm" ) )
{
// skip over type
host = host.substring( host.indexOf( ":", 4 ) + 1 ).trim();
}
protocol = PathUtils.protocol( host );
if ( protocol.equalsIgnoreCase( "file" ) )
{
return "localhost";
}
// skip over protocol
host = host.substring( host.indexOf( ":" ) + 1 ).trim();
if ( host.startsWith( "//" ) )
{
host = host.substring( 2 );
}
int pos = host.indexOf( "/" );
if ( pos > 0 )
{
host = host.substring( 0, pos );
}
pos = host.indexOf( '@' );
pos = ( pos > 0 ) ? endOfHostPosition( host, pos ) : endOfHostPosition( host, 0 );
if ( pos > 0 )
{
host = host.substring( 0, pos );
}
return host;
}
private static int endOfHostPosition( String host, int pos )
{
// if this is IPv6 then it will be in IPv6 Literal Addresses in URL's format
// see: http://www.ietf.org/rfc/rfc2732.txt
int endOfIPv6Pos = host.indexOf( ']', pos );
return ( endOfIPv6Pos > 0 ) ? endOfIPv6Pos + 1 : host.indexOf( ":", pos );
}
/**
* /**
* Return the protocol name.
* <br/>
* E.g: for input
* <code>http://www.codehause.org</code> this method will return <code>http</code>
*
* @param url the url
* @return the host name
*/
public static String protocol( final String url )
{
final int pos = url.indexOf( ":" );
if ( pos == -1 )
{
return "";
}
return url.substring( 0, pos ).trim();
}
/**
* @param url
* @return the port or {@link WagonConstants#UNKNOWN_PORT} if not existent
*/
public static int port( String url )
{
final String protocol = PathUtils.protocol( url );
if ( protocol == null || protocol.equalsIgnoreCase( "file" ) )
{
return WagonConstants.UNKNOWN_PORT;
}
final String authorization = PathUtils.authorization( url );
if ( authorization == null )
{
return WagonConstants.UNKNOWN_PORT;
}
if ( protocol.equalsIgnoreCase( "scm" ) )
{
// skip over type
url = url.substring( url.indexOf( ":", 4 ) + 1 ).trim();
}
if ( url.regionMatches( true, 0, "file:", 0, 5 ) || url.regionMatches( true, 0, "local:", 0, 6 ) )
{
return WagonConstants.UNKNOWN_PORT;
}
// skip over protocol
url = url.substring( url.indexOf( ":" ) + 1 ).trim();
if ( url.startsWith( "//" ) )
{
url = url.substring( 2 );
}
int start = authorization.length();
if ( url.length() > start && url.charAt( start ) == ':' )
{
int end = url.indexOf( '/', start );
if ( end == start + 1 )
{
// it is :/
return WagonConstants.UNKNOWN_PORT;
}
if ( end == -1 )
{
end = url.length();
}
return Integer.parseInt( url.substring( start + 1, end ) );
}
else
{
return WagonConstants.UNKNOWN_PORT;
}
}
/**
* Derive the path portion of the given URL.
*
* @param url the repository URL
* @return the basedir of the repository
* @todo need to URL decode for spaces?
*/
public static String basedir( String url )
{
String protocol = PathUtils.protocol( url );
String retValue = null;
if ( protocol.equalsIgnoreCase( "scm" ) )
{
// skip over SCM bits
if ( url.regionMatches( true, 0, "scm:svn:", 0, 8 ) )
{
url = url.substring( url.indexOf( ":", 4 ) + 1 );
protocol = PathUtils.protocol( url );
}
}
if ( protocol.equalsIgnoreCase( "file" ) )
{
retValue = url.substring( protocol.length() + 1 );
retValue = decode( retValue );
// special case: if omitted // on protocol, keep path as is
if ( retValue.startsWith( "//" ) )
{
retValue = retValue.substring( 2 );
if ( retValue.length() >= 2 && ( retValue.charAt( 1 ) == '|' || retValue.charAt( 1 ) == ':' ) )
{
// special case: if there is a windows drive letter, then keep the original return value
retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
}
else
{
// Now we expect the host
int index = retValue.indexOf( "/" );
if ( index >= 0 )
{
retValue = retValue.substring( index + 1 );
}
// special case: if there is a windows drive letter, then keep the original return value
if ( retValue.length() >= 2 && ( retValue.charAt( 1 ) == '|' || retValue.charAt( 1 ) == ':' ) )
{
retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
}
else if ( index >= 0 )
{
// leading / was previously stripped
retValue = "/" + retValue;
}
}
}
// special case: if there is a windows drive letter using |, switch to :
if ( retValue.length() >= 2 && retValue.charAt( 1 ) == '|' )
{
retValue = retValue.charAt( 0 ) + ":" + retValue.substring( 2 );
}
}
else
{
final String authorization = PathUtils.authorization( url );
final int port = PathUtils.port( url );
int pos = 0;
if ( protocol.equalsIgnoreCase( "scm" ) )
{
pos = url.indexOf( ":", 4 ) + 1;
pos = url.indexOf( ":", pos ) + 1;
}
else
{
int index = url.indexOf( "://" );
if ( index != -1 )
{
pos = index + 3;
}
}
pos += authorization.length();
if ( port != WagonConstants.UNKNOWN_PORT )
{
pos = pos + Integer.toString( port ).length() + 1;
}
if ( url.length() > pos )
{
retValue = url.substring( pos );
if ( retValue.startsWith( ":" ) )
{
// this is for :/ after the host
retValue = retValue.substring( 1 );
}
// one module may be allowed in the path in CVS
retValue = retValue.replace( ':', '/' );
}
}
if ( retValue == null )
{
retValue = "/";
}
return retValue.trim();
}
/**
* Decodes the specified (portion of a) URL. <strong>Note:</strong> This decoder assumes that ISO-8859-1 is used to
* convert URL-encoded octets to characters.
*
* @param url The URL to decode, may be <code>null</code>.
* @return The decoded URL or <code>null</code> if the input was <code>null</code>.
*/
private static String decode( String url )
{
String decoded = url;
if ( url != null )
{
int pos = -1;
while ( ( pos = decoded.indexOf( '%', pos + 1 ) ) >= 0 )
{
if ( pos + 2 < decoded.length() )
{
String hexStr = decoded.substring( pos + 1, pos + 3 );
char ch = (char) Integer.parseInt( hexStr, 16 );
decoded = decoded.substring( 0, pos ) + ch + decoded.substring( pos + 3 );
}
}
}
return decoded;
}
public static String user( String url )
{
String host = authorization( url );
int index = host.indexOf( '@' );
if ( index > 0 )
{
String userInfo = host.substring( 0, index );
index = userInfo.indexOf( ':' );
if ( index > 0 )
{
return userInfo.substring( 0, index );
}
else if ( index < 0 )
{
return userInfo;
}
}
return null;
}
public static String password( String url )
{
String host = authorization( url );
int index = host.indexOf( '@' );
if ( index > 0 )
{
String userInfo = host.substring( 0, index );
index = userInfo.indexOf( ':' );
if ( index >= 0 )
{
return userInfo.substring( index + 1 );
}
}
return null;
}
// TODO: move to plexus-utils or use something appropriate from there
public static String toRelative( File basedir, String absolutePath )
{
String relative;
absolutePath = absolutePath.replace( '\\', '/' );
String basedirPath = basedir.getAbsolutePath().replace( '\\', '/' );
if ( absolutePath.startsWith( basedirPath ) )
{
relative = absolutePath.substring( basedirPath.length() );
if ( relative.startsWith( "/" ) )
{
relative = relative.substring( 1 );
}
if ( relative.length() <= 0 )
{
relative = ".";
}
}
else
{
relative = absolutePath;
}
return relative;
}
}