001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.repository;
020
021import java.io.File;
022import java.net.URI;
023
024/**
025 * URL handling for file URLs. Based on org.apache.maven.wagon.PathUtils.
026 *
027 * @since 2.0.0
028 */
029public final class RepositoryUriUtils {
030
031    private RepositoryUriUtils() {}
032
033    public static URI toUri(String repositoryUrl) {
034        final String protocol = protocol(repositoryUrl);
035        if ("file".equals(protocol)
036                || protocol.isEmpty()
037                || protocol.length() == 1
038                        && (Character.isLetter(protocol.charAt(0)) && Character.isUpperCase(protocol.charAt(0)))) {
039            return new File(basedir(repositoryUrl)).toURI();
040        } else {
041            return URI.create(repositoryUrl);
042        }
043    }
044
045    /**
046     * Return the protocol name.
047     *
048     * @param url the url
049     * @return the protocol or empty string.
050     */
051    private static String protocol(final String url) {
052        final int pos = url.indexOf(":");
053
054        if (pos == -1) {
055            return "";
056        }
057        return url.substring(0, pos).trim();
058    }
059
060    /**
061     * Derive the path portion of the given URL.
062     *
063     * @param url the file-repository URL
064     * @return the basedir of the repository
065     */
066    private static String basedir(String url) {
067        String protocol = protocol(url);
068
069        String retValue;
070
071        if (!protocol.isEmpty()) {
072            retValue = url.substring(protocol.length() + 1);
073        } else {
074            retValue = url;
075        }
076        retValue = decode(retValue);
077        // special case: if omitted // on protocol, keep path as is
078        if (retValue.startsWith("//")) {
079            retValue = retValue.substring(2);
080
081            if (retValue.length() >= 2 && (retValue.charAt(1) == '|' || retValue.charAt(1) == ':')) {
082                // special case: if there is a windows drive letter, then keep the original return value
083                retValue = retValue.charAt(0) + ":" + retValue.substring(2);
084            } else {
085                // Now we expect the host
086                int index = retValue.indexOf("/");
087                if (index >= 0) {
088                    retValue = retValue.substring(index + 1);
089                }
090
091                // special case: if there is a windows drive letter, then keep the original return value
092                if (retValue.length() >= 2 && (retValue.charAt(1) == '|' || retValue.charAt(1) == ':')) {
093                    retValue = retValue.charAt(0) + ":" + retValue.substring(2);
094                } else if (index >= 0) {
095                    // leading / was previously stripped
096                    retValue = "/" + retValue;
097                }
098            }
099        }
100
101        // special case: if there is a windows drive letter using |, switch to :
102        if (retValue.length() >= 2 && retValue.charAt(1) == '|') {
103            retValue = retValue.charAt(0) + ":" + retValue.substring(2);
104        }
105
106        return retValue.trim();
107    }
108
109    /**
110     * Decodes the specified (portion of a) URL. <strong>Note:</strong> This decoder assumes that ISO-8859-1 is used to
111     * convert URL-encoded octets to characters.
112     *
113     * @param url The URL to decode, may be <code>null</code>.
114     * @return The decoded URL or <code>null</code> if the input was <code>null</code>.
115     */
116    private static String decode(String url) {
117        String decoded = url;
118        if (url != null) {
119            int pos = -1;
120            while ((pos = decoded.indexOf('%', pos + 1)) >= 0) {
121                if (pos + 2 < decoded.length()) {
122                    String hexStr = decoded.substring(pos + 1, pos + 3);
123                    char ch = (char) Integer.parseInt(hexStr, 16);
124                    decoded = decoded.substring(0, pos) + ch + decoded.substring(pos + 3);
125                }
126            }
127        }
128        return decoded;
129    }
130}