소스 검색

new: MultiMap#EMPTY
new: URIInput #fromURI, #fromFile
new: URIOutput #fromURI, #fromFile
new: URIReader #fromURI, #fromFile
new: URIWriter #fromURI, #fromFile
new: URIUtils
change: StringUtils - better NULL handling

Ranides Atterwim 10 년 전
부모
커밋
b585cffdf7

+ 37 - 0
assira/src/main/java/net/ranides/assira/collection/maps/MapUtils.java

@@ -8,6 +8,7 @@ package net.ranides.assira.collection.maps;
 
 import java.io.Serializable;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -470,4 +471,40 @@ public final class MapUtils {
         
     }
     
+    static class EmptyMM<K,V> extends AMap<K,V> implements MultiMap<K, V> {
+        
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public Set<Map.Entry<K, V>> entrySet() {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public int size() {
+            return 0;
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return false;
+        }
+
+        @Override
+        public boolean containsValue(Object value) {
+            return false;
+        }
+
+        @Override
+        public V get(Object key) {
+            return null;
+        }
+        
+        @Override
+        public Collection<V> getAll(Object key) {
+            return Collections.emptyList();
+        }
+
+    }
+    
 }

+ 8 - 1
assira/src/main/java/net/ranides/assira/collection/maps/MultiMap.java

@@ -20,6 +20,9 @@ import net.ranides.assira.collection.sets.HashSet;
  */
 public interface MultiMap<K,V> extends Map<K,V> {
     
+    @SuppressWarnings("rawtypes")
+    MultiMap EMPTY = new MapUtils.EmptyMM();
+    
     Collection<V> getAll(Object key);
     
     default V get(Object key, BinaryOperator<V> fold) {
@@ -48,5 +51,9 @@ public interface MultiMap<K,V> extends Map<K,V> {
     default Set<K> uniqueKeySet() {
         return new HashSet<>(keySet());
     }
-    
+
+    @SuppressWarnings("unchecked")
+    static <K,V> MultiMap<K,V> empty() {
+        return EMPTY;
+    }
 }

+ 54 - 35
assira/src/main/java/net/ranides/assira/io/URIInput.java

@@ -1,35 +1,54 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.io;
-
-import java.io.FilterInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URI;
-import java.nio.charset.Charset;
-
-/**
- *
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-public class URIInput extends FilterInputStream {
-    
-    private final URI uri;
-
-    public URIInput(URI uri, InputStream istream) {
-        super(istream);
-        this.uri = uri;
-    }
-
-    public URI uri() {
-        return uri;
-    }
-    
-    public URIReader reader(Charset charset) {
-        return new URIReader(uri, new InputStreamReader(this, charset));
-    }
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.nio.charset.Charset;
+import net.ranides.assira.text.URIUtils;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class URIInput extends FilterInputStream {
+    
+    private final URI uri;
+
+    public URIInput(URI uri, InputStream istream) {
+        super(istream);
+        this.uri = uri;
+    }
+    
+    // @todo (assira #3) URIStreams: URIInput
+    public static URIInput fromURI(URI uri) throws IOException {
+        switch(uri.getScheme()) {
+            case "file:":
+                return fromFile(URIUtils.getFile(uri));
+            default:
+                return new URIInput(uri, uri.toURL().openStream());
+        }
+    }
+
+    
+    public static URIInput fromFile(File file) throws IOException {
+        return new URIInput(file.toURI(), new FileInputStream(file));
+    }
+
+    public URI uri() {
+        return uri;
+    }
+    
+    public URIReader reader(Charset charset) {
+        return new URIReader(uri, new InputStreamReader(this, charset));
+    }
+}

+ 54 - 36
assira/src/main/java/net/ranides/assira/io/URIOutput.java

@@ -1,36 +1,54 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.io;
-
-import java.io.FilterOutputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.net.URI;
-import java.nio.charset.Charset;
-
-/**
- *
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-public class URIOutput extends FilterOutputStream {
-    
-    private final URI uri;
-
-    public URIOutput(URI uri, OutputStream out) {
-        super(out);
-        this.uri = uri;
-    }
-    
-    public URI uri() {
-        return uri;
-    }
-    
-    public URIWriter writer(Charset charset) {
-        return new URIWriter(uri, new OutputStreamWriter(this, charset));
-    }
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.io;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.URI;
+import java.nio.charset.Charset;
+import net.ranides.assira.text.URIUtils;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class URIOutput extends FilterOutputStream {
+    
+    private final URI uri;
+
+    public URIOutput(URI uri, OutputStream out) {
+        super(out);
+        this.uri = uri;
+    }
+    
+    // @todo (assira #3) URIStreams: URIOutput
+    public static URIOutput fromURI(URI uri) throws IOException {
+        switch(uri.getScheme()) {
+            case "file:":
+                return fromFile(URIUtils.getFile(uri));
+            default:
+                throw new UnsupportedOperationException("URI: " + uri);
+        }
+    }
+    
+    public static URIOutput fromFile(File file) throws IOException {
+        return new URIOutput(file.toURI(), new FileOutputStream(file));
+    }
+    
+    public URI uri() {
+        return uri;
+    }
+    
+    public URIWriter writer(Charset charset) {
+        return new URIWriter(uri, new OutputStreamWriter(this, charset));
+    }
+}

+ 126 - 83
assira/src/main/java/net/ranides/assira/io/URIReader.java

@@ -1,83 +1,126 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.io;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.net.URI;
-import java.nio.CharBuffer;
-
-public class URIReader extends Reader {
-    
-    private final URI uri;
-    private final Reader delegate;
-
-    /**
-     * Tworzy nowy obiekt powiązany z podaną nazwą pliku oraz readerem.
-     * @param file
-     * @param reader
-     */
-    public URIReader(URI uri, Reader reader) {
-        this.uri = uri;
-        this.delegate = reader;
-    }
-    
-    public URI uri() {
-        return uri;
-    }
-
-    @Override
-    public int read(CharBuffer target) throws IOException {
-        return delegate.read(target);
-    }
-
-    @Override
-    public int read() throws IOException {
-        return delegate.read();
-    }
-
-    @Override
-    public int read(char[] cbuf) throws IOException {
-        return delegate.read(cbuf);
-    }
-
-    @Override
-    public int read(char[] cbuf, int off, int len) throws IOException {
-        return delegate.read(cbuf, off, len);
-    }
-
-    @Override
-    public long skip(long n) throws IOException {
-        return delegate.skip(n);
-    }
-
-    @Override
-    public boolean ready() throws IOException {
-        return delegate.ready();
-    }
-
-    @Override
-    public boolean markSupported() {
-        return delegate.markSupported();
-    }
-
-    @Override
-    public void mark(int readAheadLimit) throws IOException {
-        delegate.mark(readAheadLimit);
-    }
-
-    @Override
-    public void reset() throws IOException {
-        delegate.reset();
-    }
-
-    @Override
-    public void close() throws IOException {
-        delegate.close();
-    }
-    
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URLConnection;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.ranides.assira.text.Charsets;
+import net.ranides.assira.text.URIUtils;
+
+public class URIReader extends Reader {
+    
+    private static final Pattern RE_CHARSET = Pattern.compile(";[ ]+charset=([^; ]+)");
+    
+    private final URI uri;
+    private final Reader delegate;
+
+    /**
+     * Tworzy nowy obiekt powiązany z podaną nazwą pliku oraz readerem.
+     * @param file
+     * @param reader
+     */
+    public URIReader(URI uri, Reader reader) {
+        this.uri = uri;
+        this.delegate = reader;
+    }
+    
+    // @todo (assira #3) URIStreams: URIReader
+    public static URIReader fromURI(URI uri) throws IOException {
+        switch(uri.getScheme()) {
+            case "file:": {
+                File file = URIUtils.getFile(uri);
+                Charset cs = Charset.forName(URIUtils.getParam(uri, "charset").orElse("UTF-8"));
+                return fromFile(file, cs);
+            }
+            default: {
+                URLConnection c = uri.toURL().openConnection();
+                c.connect();
+                Charset cs = getContentCharset(c.getContentType());
+                return new URIReader(uri, new InputStreamReader(c.getInputStream(), cs));
+            }
+        }
+    }
+    
+    private static Charset getContentCharset(String type) {
+        if(null == type) {
+            return Charsets.UTF8;
+        }
+        Matcher hit = RE_CHARSET.matcher(type);
+        if(!hit.find()) {
+            return Charsets.UTF8;
+        }
+        return Charset.forName(hit.group(1));
+    }
+    
+    public static URIReader fromFile(File file, Charset cs) throws IOException {
+        return new URIReader(file.toURI(), new InputStreamReader(new FileInputStream(file),cs));
+    }
+    
+    public URI uri() {
+        return uri;
+    }
+
+    @Override
+    public int read(CharBuffer target) throws IOException {
+        return delegate.read(target);
+    }
+
+    @Override
+    public int read() throws IOException {
+        return delegate.read();
+    }
+
+    @Override
+    public int read(char[] cbuf) throws IOException {
+        return delegate.read(cbuf);
+    }
+
+    @Override
+    public int read(char[] cbuf, int off, int len) throws IOException {
+        return delegate.read(cbuf, off, len);
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        return delegate.skip(n);
+    }
+
+    @Override
+    public boolean ready() throws IOException {
+        return delegate.ready();
+    }
+
+    @Override
+    public boolean markSupported() {
+        return delegate.markSupported();
+    }
+
+    @Override
+    public void mark(int readAheadLimit) throws IOException {
+        delegate.mark(readAheadLimit);
+    }
+
+    @Override
+    public void reset() throws IOException {
+        delegate.reset();
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+    
+}

+ 105 - 81
assira/src/main/java/net/ranides/assira/io/URIWriter.java

@@ -1,81 +1,105 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.io;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.net.URI;
-
-
-
-public class URIWriter extends Writer {
-    
-    private final URI uri;
-    private final Writer delegate;
-
-    protected URIWriter(URI uri, Writer delegate) {
-        this.uri = uri;
-        this.delegate = delegate;
-    }
-    
-    public URI uri() {
-        return uri;
-    }
-
-    @Override
-    public void write(int c) throws IOException {
-        delegate.write(c);
-    }
-
-    @Override
-    public void write(char[] cbuf) throws IOException {
-        delegate.write(cbuf);
-    }
-
-    @Override
-    public void write(char[] cbuf, int off, int len) throws IOException {
-        delegate.write(cbuf, off, len);
-    }
-
-    @Override
-    public void write(String str) throws IOException {
-        delegate.write(str);
-    }
-
-    @Override
-    public void write(String str, int off, int len) throws IOException {
-        delegate.write(str, off, len);
-    }
-
-    @Override
-    public Writer append(CharSequence csq) throws IOException {
-        return delegate.append(csq);
-    }
-
-    @Override
-    public Writer append(CharSequence csq, int start, int end) throws IOException {
-        return delegate.append(csq, start, end);
-    }
-
-    @Override
-    public Writer append(char c) throws IOException {
-        return delegate.append(c);
-    }
-
-    @Override
-    public void flush() throws IOException {
-        delegate.flush();
-    }
-
-    @Override
-    public void close() throws IOException {
-        delegate.close();
-    }
-    
-    
-    
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.charset.Charset;
+import net.ranides.assira.text.URIUtils;
+
+
+
+public class URIWriter extends Writer {
+    
+    private final URI uri;
+    private final Writer delegate;
+
+    public URIWriter(URI uri, Writer delegate) {
+        this.uri = uri;
+        this.delegate = delegate;
+    }
+    
+    // @todo (assira #3) URIStreams: URIWriter
+    public static URIWriter fromURI(URI uri) throws IOException {
+        switch(uri.getScheme()) {
+            case "file:":
+                File file = URIUtils.getFile(uri);
+                Charset cs = Charset.forName(URIUtils.getParam(uri, "charset").orElse("UTF-8"));
+                return fromFile(file, cs);
+            default:
+                throw new UnsupportedOperationException("URI: " + uri);
+        }
+    }
+
+    
+    public static URIWriter fromFile(File file, Charset cs) throws IOException {
+        return new URIWriter(file.toURI(), new OutputStreamWriter(new FileOutputStream(file),cs));
+    }
+    
+    public URI uri() {
+        return uri;
+    }
+
+    @Override
+    public void write(int c) throws IOException {
+        delegate.write(c);
+    }
+
+    @Override
+    public void write(char[] cbuf) throws IOException {
+        delegate.write(cbuf);
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        delegate.write(cbuf, off, len);
+    }
+
+    @Override
+    public void write(String str) throws IOException {
+        delegate.write(str);
+    }
+
+    @Override
+    public void write(String str, int off, int len) throws IOException {
+        delegate.write(str, off, len);
+    }
+
+    @Override
+    public Writer append(CharSequence csq) throws IOException {
+        return delegate.append(csq);
+    }
+
+    @Override
+    public Writer append(CharSequence csq, int start, int end) throws IOException {
+        return delegate.append(csq, start, end);
+    }
+
+    @Override
+    public Writer append(char c) throws IOException {
+        return delegate.append(c);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+    
+    
+    
+}

+ 2 - 1
assira/src/main/java/net/ranides/assira/reflection/util/PackageScanner.java

@@ -26,6 +26,7 @@ import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 import net.ranides.assira.collection.iterators.ForwardSpliterator;
 import net.ranides.assira.text.StringUtils;
+import net.ranides.assira.text.URIUtils;
 import net.ranides.assira.trace.ExceptionUtils;
 
 
@@ -92,7 +93,7 @@ public final class PackageScanner {
                 return false;
             }
             String str = resources.nextElement().getPath();
-            String uri = StringUtils.decodeURL(str, Charset.defaultCharset());
+            String uri = URIUtils.decodeURL(str, Charset.defaultCharset());
             File file = new File(uri);
             current = file.isDirectory() ? forDirectory(file.toPath()) : forJAR(uri);
             return current.tryAdvance(action);

+ 5 - 5
assira/src/main/java/net/ranides/assira/text/StringTraits.java

@@ -17,15 +17,15 @@ public final class StringTraits {
         /* utility class */
     }
     
-    public static boolean isEmpty(String value) {
-		return null == value || value.isEmpty();
+    public static boolean isEmpty(CharSequence value) {
+		return null == value || 0 == value.length();
 	}
 	
-	public static boolean isBlank(String value) {
-		return isEmpty(value) || value.trim().isEmpty();
+	public static boolean isBlank(CharSequence value) {
+		return isEmpty(value) || value.toString().trim().isEmpty();
 	}
 	
-    public static boolean isNumber(String text) {
+    public static boolean isNumber(CharSequence text) {
         if(isEmpty(text)) {
             return false;
         }

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1027 - 956
assira/src/main/java/net/ranides/assira/text/StringUtils.java


+ 97 - 0
assira/src/main/java/net/ranides/assira/text/URIUtils.java

@@ -0,0 +1,97 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.text;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.stream.Stream;
+import net.ranides.assira.collection.arrays.ArraySearch;
+import net.ranides.assira.collection.arrays.ArrayUtils;
+import net.ranides.assira.collection.maps.HashMultiMap;
+import net.ranides.assira.collection.maps.MapCollectors;
+import net.ranides.assira.collection.maps.MapUtils;
+import net.ranides.assira.collection.maps.MultiMap;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class URIUtils {
+
+    public static String decodeURL(String input) {
+        return decodeURL(input, Charsets.UTF8);
+    }
+	
+	public static String decodeURL(String input, Charset charset) {
+        try {
+            return URLDecoder.decode(input, charset.name());
+        } catch(UnsupportedEncodingException cause) {
+			throw new AssertionError("Unsupported charset: " + charset, cause);
+        }
+    }
+    
+    public static String encodeURL(String input) {
+        return encodeURL(input, Charsets.UTF8);
+    }
+    
+    public static String encodeURL(String input, Charset charset) {
+        try {
+            return URLEncoder.encode(input, charset.name());
+        } catch(UnsupportedEncodingException cause) {
+			throw new AssertionError("Unsupported charset: " + charset, cause);
+        }
+    }
+    
+    public static File getFile(URI uri) {
+        String p = uri.getSchemeSpecificPart();
+        String q = uri.getQuery();
+        int qs = (null==q ? 0 : q.length()+1);
+        return new File(p.substring(2, p.length() - qs));
+    }
+    
+    public static MultiMap<String,String> getParams(URI uri) {
+        String query = uri.getQuery();
+        if(StringTraits.isEmpty(query)) {
+            return MultiMap.empty();
+        }
+        return Arrays
+            .stream(query.split("&"))
+            .map(v -> v.split("="))
+            .collect(MapCollectors.multimap(p -> paramName(p), p -> paramValue(p)));
+    }
+    
+    public static Optional<String> getParam(URI uri, String param) {
+        String query = uri.getQuery();
+        if(StringTraits.isEmpty(query)) {
+            return Optional.empty();
+        }
+        return Arrays
+            .stream(query.split("&"))
+            .map(v -> v.split("="))
+            .filter(p -> param.equals(paramName(p)))
+            .map(p -> paramValue(p))
+            .findFirst();
+    }
+    
+    private static final String paramName(String[] array) {
+        return array.length > 0 ? array[0] : "";
+    }
+    
+    private static final String paramValue(String[] array) {
+        return array.length > 1 ? array[1] : "";
+    }
+    
+}

+ 41 - 0
assira/src/test/java/net/ranides/assira/io/URIReaderTest.java

@@ -0,0 +1,41 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.io;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import net.ranides.assira.text.StringTraits;
+import net.ranides.assira.text.StringUtils;
+import org.junit.Test;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class URIReaderTest {
+    
+    public URIReaderTest() {
+    }
+
+    @Test
+    public void testSomeMethod() throws URISyntaxException {
+        new URI("file://c:/data");
+        
+        System.out.printf("%s%n", getFile(new URI("file://c:/windows/system/file.txt")));
+        System.out.printf("%s%n", getFile(new URI("file://c:/windows/system/")));
+        System.out.printf("%s%n", getFile(new URI("file://c:/windows/system/file.txt?")));
+        System.out.printf("%s%n", getFile(new URI("file://c:/windows/system/file.txt?charset=UTF-8&endl=unix")));
+    }
+    
+    private static final String getFile(URI uri) {
+        String p = uri.getSchemeSpecificPart();
+        String q = uri.getQuery();
+        int qs = (null==q ? 0 : q.length()+1);
+        return p.substring(2, p.length() - qs);
+    }
+    
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 577 - 620
assira/src/test/java/net/ranides/assira/text/StringUtilsTest.java


+ 89 - 0
assira/src/test/java/net/ranides/assira/text/URIUtilsTest.java

@@ -0,0 +1,89 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.text;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import static net.ranides.assira.junit.NewAssert.assertThrows;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class URIUtilsTest {
+    
+    @Test
+    public void testEncodeURL() {
+        assertEquals("hello%3Aworld%3Fparam%3D5", URIUtils.encodeURL("hello:world?param=5"));
+        
+        assertEquals("hello%3Aworld%3Fparam%3D5", URIUtils.encodeURL("hello:world?param=5", new FCharset("UTF-8")));
+        
+        assertThrows(AssertionError.class, ()->{
+            URIUtils.encodeURL("hello:world?param=5", new FCharset("UFC888"));
+        });
+        
+    }
+    
+    @Test
+    public void testDecodeURL() {
+        assertEquals("hello:world?param=5", URIUtils.decodeURL("hello%3Aworld%3Fparam%3D5"));
+        
+        assertEquals("hello:world?param=5", URIUtils.decodeURL("hello%3Aworld%3Fparam%3D5", new FCharset("UTF-8")));
+        
+        assertThrows(AssertionError.class, ()->{
+            URIUtils.decodeURL("hello%3Aworld%3Fparam%3D5", new FCharset("UFC888"));
+        });
+    }
+    
+    @Test
+    public void testGetQueryParams() throws URISyntaxException {
+        URI u1 = new URI("file://root?charset=UTF-8&endl=unix&follow&fs=ntfs");
+        URI u2 = new URI("file://root?charset=UTF-8");
+        URI u3 = new URI("file://root?charset");
+        URI u4 = new URI("file://root?");
+        URI u5 = new URI("file://root");
+        
+        
+        System.out.printf("%s%n", URIUtils.getParams(u1));
+        System.out.printf("%s%n", URIUtils.getParams(u2));
+        System.out.printf("%s%n", URIUtils.getParams(u3));
+        System.out.printf("%s%n", URIUtils.getParams(u4));
+        System.out.printf("%s%n", URIUtils.getParams(u5));
+    }
+    
+    private static final class FCharset extends Charset {
+
+        public FCharset(String name) {
+            super(name, new String[0]);
+        }
+
+        @Override
+        public boolean contains(Charset cs) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public CharsetDecoder newDecoder() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public CharsetEncoder newEncoder() {
+            throw new UnsupportedOperationException();
+        }
+        
+    }
+	
+}