| /* |
| * 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. |
| */ |
| package org.apache.maven.wagon; |
| |
| 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; |
| } |
| } |