/*
 * This file is part of ClassGraph.
 *
 * Author: Luke Hutchison
 *
 * Hosted at: https://github.com/classgraph/classgraph
 *
 * --
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Luke Hutchison
 *
 * 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.
 */
package io.github.classgraph.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

/** A simple URL path encoder. */
public class URLPathEncoder {

    private static boolean[] safe = new boolean[256];

    static {
        for (int i = 'a'; i <= 'z'; i++) {
            safe[i] = true;
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            safe[i] = true;
        }
        for (int i = '0'; i <= '9'; i++) {
            safe[i] = true;
        }
        // "safe" rule
        safe['$'] = safe['-'] = safe['_'] = safe['.'] = safe['+'] = true;
        // "extra" rule
        safe['!'] = safe['*'] = safe['\''] = safe['('] = safe[')'] = safe[','] = true;
        // Only include "/" from "fsegment" and "hsegment" rules (exclude ':', '@', '&' and '=' for safety)
        safe['/'] = true;
    }

    private static final char[] hexadecimal = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
            'D', 'E', 'F' };

    /**
     * Encode a URL path using percent-encoding. '/' is not encoded.
     * 
     * @param path
     *            The path to encode.
     * @return The encoded path.
     */
    public static String encodePath(final String path) {
        final StringBuilder encodedPath = new StringBuilder(path.length() * 2);
        final ByteArrayOutputStream utf8bytes = new ByteArrayOutputStream(10);
        OutputStreamWriter writer;
        try {
            writer = new OutputStreamWriter(utf8bytes, "UTF8");
        } catch (final UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        for (int i = 0; i < path.length(); i++) {
            final int c = path.charAt(i);
            if (c < 256 && safe[c]) {
                encodedPath.append((char) c);
            } else {
                try {
                    writer.write(c);
                    writer.flush();
                } catch (final IOException e) {
                    utf8bytes.reset();
                    continue;
                }
                final byte[] charBytes = utf8bytes.toByteArray();
                for (int j = 0; j < charBytes.length; j++) {
                    final byte b = charBytes[j];
                    final int low = (b & 0x0f);
                    final int high = ((b & 0xf0) >> 4);
                    encodedPath.append('%');
                    encodedPath.append(hexadecimal[high]);
                    encodedPath.append(hexadecimal[low]);
                }
                utf8bytes.reset();
            }
        }
        return encodedPath.toString();
    }
}

@@ -1319,140 +1319,36 @@
 ava.
+n
 io.
-ByteArrayOutputStream;%0Aimport java.io.IOException;%0Aimport java.io.OutputStreamWriter;%0Aimport java.io.UnsupportedEncodingException
+charset.StandardCharsets
 ;%0A%0A/
@@ -2174,19 +2174,19 @@
 ', '
-A
+a
 ', '
-B
+b
 ', '
-C
+c
 ',%0A 
@@ -2201,19 +2201,19 @@
    '
-D
+d
 ', '
-E
+e
 ', '
-F
+f
 ' %7D;
@@ -2466,269 +2466,63 @@
     
-final StringBuilder encodedPath = new StringBuilder(path.length() * 2);%0A        final ByteArrayOutputStream utf8bytes = new ByteArrayOutputStream(10);%0A        OutputStreamWriter writer;%0A        try %7B%0A            writer = new OutputStreamWriter(utf8bytes, %22
+byte%5B%5D pathBytes = path.getBytes(StandardCharsets.
 UTF
+_
 8
-%22
 );%0A 
@@ -2532,109 +2532,82 @@
     
-%7D catch (final UnsupportedEncodingException e) %7B%0A            throw new RuntimeException(e);%0A        %7D
+final StringBuilder encodedPath = new StringBuilder(pathBytes.length * 3);
 %0A   
@@ -2639,17 +2639,20 @@
 path
+Bytes
 .length
-()
 ; i+
@@ -2682,26 +2682,31 @@
 int 
-c
+b
  = path
-.charAt(i)
+Bytes%5Bi%5D & 0xff
 ;%0A  
@@ -2719,17 +2719,17 @@
     if (
-c
+b
  %3C 256 &
@@ -2735,17 +2735,17 @@
 && safe%5B
-c
+b
 %5D) %7B%0A   
@@ -2787,131 +2787,12 @@
 ar) 
-c);%0A            %7D else %7B%0A                try %7B%0A                    writer.write(c);%0A                    writer.flush();%0A   
+b);%0A
     
@@ -2803,412 +2803,17 @@
     
- 
 %7D 
-catch (final IOException e) %7B%0A                    utf8bytes.reset();%0A                    continue;%0A                %7D%0A                final byte%5B%5D charBytes = utf8bytes.toByteArray();%0A                for (int j = 0; j %3C charBytes.length; j++) %7B%0A                    final byte b = charBytes%5Bj%5D;%0A                    final int low = (b & 0x0f);%0A                    final int high = ((b & 0xf0) %3E%3E 4);%0A    
+else %7B%0A
     
@@ -2849,20 +2849,16 @@
 d('%25');%0A
-    
         
@@ -2900,20 +2900,27 @@
 mal%5B
-high%5D);%0A    
+(b & 0xf0) %3E%3E 4%5D);%0A
     
@@ -2966,65 +2966,17 @@
 mal%5B
-low%5D);%0A                %7D%0A                utf8bytes.reset(
+b & 0x0f%5D
 );%0A 

Improve robustness of URL encoding of non-safe UTF-8 bytes