/*
 * This file is part of the DITA Open Toolkit project hosted on
 * Sourceforge.net. See the accompanying license.txt file for
 * applicable licenses.
 */

/*
 * (c) Copyright IBM Corp. 2010 All Rights Reserved.
 */
package org.dita.dost.util;

/**
 * Used to escape a file path to a URI.
 */
public final class FilePathToURI {
    // Which ASCII characters need to be escaped
    private static final boolean gNeedEscaping[] = new boolean[128];
    // The first hex character if a character needs to be escaped
    private static final char gAfterEscaping1[] = new char[128];
    // The second hex character if a character needs to be escaped
    private static final char gAfterEscaping2[] = new char[128];
    private static final char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    // Initialize the above 3 arrays
    static {
        for (int i = 0; i <= 0x1f; i++) {
            gNeedEscaping[i] = true;
            gAfterEscaping1[i] = gHexChs[i >> 4];
            gAfterEscaping2[i] = gHexChs[i & 0xf];
        }
        gNeedEscaping[0x7f] = true;
        gAfterEscaping1[0x7f] = '7';
        gAfterEscaping2[0x7f] = 'F';
        final char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', '?',
                '|', '\\', '^', '~', '[', ']', '`', '\'', '&'};
        final int len = escChs.length;
        char ch;
        for (int i = 0; i < len; i++) {
            ch = escChs[i];
            gNeedEscaping[ch] = true;
            gAfterEscaping1[ch] = gHexChs[ch >> 4];
            gAfterEscaping2[ch] = gHexChs[ch & 0xf];
        }
    }

    /** To escape a file path to a URI, by using %HH to represent special ASCII characters:
     * 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%' and '"' and non-ASCII characters
     * (whose value >= 128).
     * 
     * '\' character will also be escaped.
     * 
     * @param path The path to be escaped.
     * @return The escaped URI.
     */
    public static String filepath2URI(String path) {
        // Return null if path is null.
        if (path == null) {
            return null;
        }
        path = escapeSpecialAsciiAndNonAscii(path);

        return path;
    }

    /**
     * To escape a file path to a URI, by using %HH to represent
     * special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%'
     * and '"' and non-ASCII characters (whose value >= 128).
     * 
     * @param path The path to be escaped.
     * @return The escaped path.
     */
    public static String escapeSpecialAsciiAndNonAscii(final String path) {
        int len = path.length(), ch;
        final StringBuffer buffer = new StringBuffer(len*3);
        // Change C:/something to /C:/something
        if (len >= 2 && path.charAt(1) == ':') {
            ch = Character.toUpperCase(path.charAt(0));
            if (ch >= 'A' && ch <= 'Z') {
                buffer.append('/');
            }
        }

        // For each character in the path
        int i = 0;
        for (; i < len; i++) {
            ch = path.charAt(i);
            // If it's not an ASCII character, break here, and use UTF-8 encoding
            if (ch >= 128) {
                break;
            }
            if (gNeedEscaping[ch]) {
                buffer.append('%');
                buffer.append(gAfterEscaping1[ch]);
                buffer.append(gAfterEscaping2[ch]);
                // Record the fact that it's escaped
            }
            else {
                buffer.append((char)ch);
            }
        }

        // We saw some non-ASCII character
        if (i < len) {
            // Get UTF-8 bytes for the remaining sub-string
            byte[] bytes;
            byte b;
            try {
                bytes = path.substring(i).getBytes("UTF-8");
            } catch (final java.io.UnsupportedEncodingException e) {
                // Should never happen
                return path;
            }
            len = bytes.length;

            // For each byte
            for (i = 0; i < len; i++) {
                b = bytes[i];
                // For non-ASCII character: make it positive, then escape
                if (b < 0) {
                    ch = b + 256;
                    buffer.append('%');
                    buffer.append(gHexChs[ch >> 4]);
                    buffer.append(gHexChs[ch & 0xf]);
                }
                else if (gNeedEscaping[b]) {
                    buffer.append('%');
                    buffer.append(gAfterEscaping1[b]);
                    buffer.append(gAfterEscaping2[b]);
                }
                else {
                    buffer.append((char)b);
                }
            }
        }
        return buffer.toString();
    }
}