// Path: library/src/main/java/org/mariotaku/restfu/http/mime/Body.java
// public interface Body extends Closeable {
//     ContentType contentType();
// 
//     String contentEncoding();
// 
//     long length() throws IOException;
// 
//     long writeTo(final OutputStream os) throws IOException;
// 
//     InputStream stream() throws IOException;
// 
//     void close() throws IOException;
// 
// }
// 
// Path: library/src/main/java/org/mariotaku/restfu/http/mime/UrlSerialization.java
// public abstract class UrlSerialization {
// 
//     static final char[] HEX_CHAR_TABLE = {
//             '0', '1', '2', '3',
//             '4', '5', '6', '7',
//             '8', '9', 'A', 'B',
//             'C', 'D', 'E', 'F'
//     };
// 
//     public static UrlSerialization PATH = new UrlSerialization() {
//         final BitSet allowedSet;
// 
//         {
//             allowedSet = new BitSet(0xFF);
// 
//             allowedSet.set('_', true);
//             allowedSet.set('-', true);
//             allowedSet.set('!', true);
//             allowedSet.set('.', true);
//             allowedSet.set('~', true);
//             allowedSet.set('\'', true);
//             allowedSet.set('(', true);
//             allowedSet.set(')', true);
//             allowedSet.set('*', true);
//             for (int i = '0'; i <= '9'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'A'; i <= 'Z'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'a'; i <= 'z'; i++) {
//                 allowedSet.set(i, true);
//             }
//         }
// 
//         @Override
//         protected void appendEscape(int codePoint, Charset charset, StringBuilder target) {
//             if (codePoint <= 0xFF && allowedSet.get(codePoint)) {
//                 target.appendCodePoint(codePoint);
//             } else {
//                 percentEncode(codePoint, charset, target);
//             }
//         }
//     };
// 
//     /**
//      * https://url.spec.whatwg.org/#concept-urlsearchparams-list
//      */
//     public static UrlSerialization QUERY = new UrlSerialization() {
//         final BitSet allowedSet;
// 
//         {
//             allowedSet = new BitSet(0xFF);
// //            allowedSet.set('*', true);
//             allowedSet.set('-', true);
//             allowedSet.set('.', true);
//             allowedSet.set('_', true);
//             for (int i = '0'; i <= '9'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'A'; i <= 'Z'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'a'; i <= 'z'; i++) {
//                 allowedSet.set(i, true);
//             }
//         }
// 
//         @Override
//         protected void appendEscape(int codePoint, Charset charset, StringBuilder target) {
//             if (codePoint == 0x20) {
//                 target.append('+');
//             } else if (codePoint <= 0xFF && allowedSet.get(codePoint)) {
//                 target.appendCodePoint(codePoint);
//             } else {
//                 percentEncode(codePoint, charset, target);
//             }
//         }
//     };
// 
// 
//     protected abstract void appendEscape(int codePoint, Charset charset, StringBuilder target);
// 
//     protected final void percentEncode(int codePoint, Charset charset, StringBuilder target) {
//         CharBuffer cb = CharBuffer.wrap(Character.toChars(codePoint));
//         ByteBuffer encoded = charset.encode(cb);
//         for (int i = 0, j = encoded.limit(); i < j; i++) {
//             target.append('%');
//             byte v = encoded.get(i);
//             target.append(HEX_CHAR_TABLE[(v & 0xF0) >>> 4]);
//             target.append(HEX_CHAR_TABLE[v & 0xF]);
//         }
//     }
// 
//     public final String serialize(String str, Charset charset) {
//         final StringBuilder sb = new StringBuilder();
//         serialize(str, charset, sb);
//         return sb.toString();
//     }
// 
//     public final void serialize(String str, Charset charset, StringBuilder target) {
//         final int length = str.length();
//         for (int offset = 0; offset < length; ) {
//             final int codePoint = str.codePointAt(offset);
//             appendEscape(codePoint, charset, target);
//             offset += Character.charCount(codePoint);
//         }
//     }
// 
// }


import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mariotaku.commons.collection.Pair;
import org.mariotaku.restfu.annotation.HttpMethod;
import org.mariotaku.restfu.annotation.param.*;
import org.mariotaku.restfu.http.*;
import org.mariotaku.restfu.http.mime.Body;
import org.mariotaku.restfu.http.mime.UrlSerialization;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
                        map.put(key, valueMap.get(key));
                    }
                }
            } else if (value != null) {
                for (String key : extra.value()) {
                    map.put(key, value);
                }
            }
        }
        return extrasCache = map;
    }

    @NotNull
    public RestRequest toRestRequest(@NotNull RestConverter.Factory<E> factory, @Nullable final ValueMap valuesPool)
            throws RestConverter.ConvertException, IOException, E {
        final HttpMethod method = getMethod();
        final MultiValueMap<Body> params = getParams(factory, valuesPool);
        final RawValue fileValue = getRawValue();
        checkMethod(method, fileValue);
        return new RestRequest(method.value(), method.allowBody(), getPath(), getHeaders(valuesPool),
                getQueries(valuesPool), params, fileValue, getBodyType(), getExtras());
    }

    private String findPathReplacement(String key) {
        final Charset charset = Charset.forName("UTF-8");
        for (Pair<Path, Object> entry : paths) {
            if (key.equals(entry.first.value())) {
                if (entry.first.encoded()) {
                    return String.valueOf(entry.second);
                } else {
// Path: library/src/main/java/org/mariotaku/restfu/http/mime/Body.java
// public interface Body extends Closeable {
//     ContentType contentType();
// 
//     String contentEncoding();
// 
//     long length() throws IOException;
// 
//     long writeTo(final OutputStream os) throws IOException;
// 
//     InputStream stream() throws IOException;
// 
//     void close() throws IOException;
// 
// }
// 
// Path: library/src/main/java/org/mariotaku/restfu/http/mime/UrlSerialization.java
// public abstract class UrlSerialization {
// 
//     static final char[] HEX_CHAR_TABLE = {
//             '0', '1', '2', '3',
//             '4', '5', '6', '7',
//             '8', '9', 'A', 'B',
//             'C', 'D', 'E', 'F'
//     };
// 
//     public static UrlSerialization PATH = new UrlSerialization() {
//         final BitSet allowedSet;
// 
//         {
//             allowedSet = new BitSet(0xFF);
// 
//             allowedSet.set('_', true);
//             allowedSet.set('-', true);
//             allowedSet.set('!', true);
//             allowedSet.set('.', true);
//             allowedSet.set('~', true);
//             allowedSet.set('\'', true);
//             allowedSet.set('(', true);
//             allowedSet.set(')', true);
//             allowedSet.set('*', true);
//             for (int i = '0'; i <= '9'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'A'; i <= 'Z'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'a'; i <= 'z'; i++) {
//                 allowedSet.set(i, true);
//             }
//         }
// 
//         @Override
//         protected void appendEscape(int codePoint, Charset charset, StringBuilder target) {
//             if (codePoint <= 0xFF && allowedSet.get(codePoint)) {
//                 target.appendCodePoint(codePoint);
//             } else {
//                 percentEncode(codePoint, charset, target);
//             }
//         }
//     };
// 
//     /**
//      * https://url.spec.whatwg.org/#concept-urlsearchparams-list
//      */
//     public static UrlSerialization QUERY = new UrlSerialization() {
//         final BitSet allowedSet;
// 
//         {
//             allowedSet = new BitSet(0xFF);
// //            allowedSet.set('*', true);
//             allowedSet.set('-', true);
//             allowedSet.set('.', true);
//             allowedSet.set('_', true);
//             for (int i = '0'; i <= '9'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'A'; i <= 'Z'; i++) {
//                 allowedSet.set(i, true);
//             }
//             for (int i = 'a'; i <= 'z'; i++) {
//                 allowedSet.set(i, true);
//             }
//         }
// 
//         @Override
//         protected void appendEscape(int codePoint, Charset charset, StringBuilder target) {
//             if (codePoint == 0x20) {
//                 target.append('+');
//             } else if (codePoint <= 0xFF && allowedSet.get(codePoint)) {
//                 target.appendCodePoint(codePoint);
//             } else {
//                 percentEncode(codePoint, charset, target);
//             }
//         }
//     };
// 
// 
//     protected abstract void appendEscape(int codePoint, Charset charset, StringBuilder target);
// 
//     protected final void percentEncode(int codePoint, Charset charset, StringBuilder target) {
//         CharBuffer cb = CharBuffer.wrap(Character.toChars(codePoint));
//         ByteBuffer encoded = charset.encode(cb);
//         for (int i = 0, j = encoded.limit(); i < j; i++) {
//             target.append('%');
//             byte v = encoded.get(i);
//             target.append(HEX_CHAR_TABLE[(v & 0xF0) >>> 4]);
//             target.append(HEX_CHAR_TABLE[v & 0xF]);
//         }
//     }
// 
//     public final String serialize(String str, Charset charset) {
//         final StringBuilder sb = new StringBuilder();
//         serialize(str, charset, sb);
//         return sb.toString();
//     }
// 
//     public final void serialize(String str, Charset charset, StringBuilder target) {
//         final int length = str.length();
//         for (int offset = 0; offset < length; ) {
//             final int codePoint = str.codePointAt(offset);
//             appendEscape(codePoint, charset, target);
//             offset += Character.charCount(codePoint);
//         }
//     }
// 
// }


// Path: library/src/main/java/org/mariotaku/restfu/RestMethod.java
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mariotaku.commons.collection.Pair;
import org.mariotaku.restfu.annotation.HttpMethod;
import org.mariotaku.restfu.annotation.param.*;
import org.mariotaku.restfu.http.*;
import org.mariotaku.restfu.http.mime.Body;
import org.mariotaku.restfu.http.mime.UrlSerialization;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;

                        map.put(key, valueMap.get(key));
                    }
                }
            } else if (value != null) {
                for (String key : extra.value()) {
                    map.put(key, value);
                }
            }
        }
        return extrasCache = map;
    }

    @NotNull
    public RestRequest toRestRequest(@NotNull RestConverter.Factory<E> factory, @Nullable final ValueMap valuesPool)
            throws RestConverter.ConvertException, IOException, E {
        final HttpMethod method = getMethod();
        final MultiValueMap<Body> params = getParams(factory, valuesPool);
        final RawValue fileValue = getRawValue();
        checkMethod(method, fileValue);
        return new RestRequest(method.value(), method.allowBody(), getPath(), getHeaders(valuesPool),
                getQueries(valuesPool), params, fileValue, getBodyType(), getExtras());
    }

    private String findPathReplacement(String key) {
        final Charset charset = Charset.forName("UTF-8");
        for (Pair<Path, Object> entry : paths) {
            if (key.equals(entry.first.value())) {
                if (entry.first.encoded()) {
                    return String.valueOf(entry.second);
                } else {
                    return UrlSerialization.PATH.serialize(String.valueOf(entry.second), charset);