1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.net;
29
30 import java.nio.ByteBuffer;
31 import java.nio.CharBuffer;
32 import java.nio.charset.Charset;
33 import java.nio.charset.StandardCharsets;
34 import java.util.BitSet;
35
36
37
38
39
40
41 public class PercentCodec {
42
43 static final BitSet GEN_DELIMS = new BitSet(256);
44 static final BitSet SUB_DELIMS = new BitSet(256);
45 static final BitSet UNRESERVED = new BitSet(256);
46 static final BitSet URIC = new BitSet(256);
47
48 static {
49 GEN_DELIMS.set(':');
50 GEN_DELIMS.set('/');
51 GEN_DELIMS.set('?');
52 GEN_DELIMS.set('#');
53 GEN_DELIMS.set('[');
54 GEN_DELIMS.set(']');
55 GEN_DELIMS.set('@');
56
57 SUB_DELIMS.set('!');
58 SUB_DELIMS.set('$');
59 SUB_DELIMS.set('&');
60 SUB_DELIMS.set('\'');
61 SUB_DELIMS.set('(');
62 SUB_DELIMS.set(')');
63 SUB_DELIMS.set('*');
64 SUB_DELIMS.set('+');
65 SUB_DELIMS.set(',');
66 SUB_DELIMS.set(';');
67 SUB_DELIMS.set('=');
68
69 for (int i = 'a'; i <= 'z'; i++) {
70 UNRESERVED.set(i);
71 }
72 for (int i = 'A'; i <= 'Z'; i++) {
73 UNRESERVED.set(i);
74 }
75
76 for (int i = '0'; i <= '9'; i++) {
77 UNRESERVED.set(i);
78 }
79 UNRESERVED.set('-');
80 UNRESERVED.set('.');
81 UNRESERVED.set('_');
82 UNRESERVED.set('~');
83 URIC.or(SUB_DELIMS);
84 URIC.or(UNRESERVED);
85 }
86
87 private static final int RADIX = 16;
88
89 static void encode(final StringBuilder buf, final CharSequence content, final Charset charset,
90 final BitSet safechars, final boolean blankAsPlus) {
91 if (content == null) {
92 return;
93 }
94 final CharBuffer cb = CharBuffer.wrap(content);
95 final ByteBuffer bb = (charset != null ? charset : StandardCharsets.UTF_8).encode(cb);
96 while (bb.hasRemaining()) {
97 final int b = bb.get() & 0xff;
98 if (safechars.get(b)) {
99 buf.append((char) b);
100 } else if (blankAsPlus && b == ' ') {
101 buf.append("+");
102 } else {
103 buf.append("%");
104 final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
105 final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
106 buf.append(hex1);
107 buf.append(hex2);
108 }
109 }
110 }
111
112 static void encode(final StringBuilder buf, final CharSequence content, final Charset charset, final boolean blankAsPlus) {
113 encode(buf, content, charset, UNRESERVED, blankAsPlus);
114 }
115
116 public static void encode(final StringBuilder buf, final CharSequence content, final Charset charset) {
117 encode(buf, content, charset, UNRESERVED, false);
118 }
119
120 public static String encode(final CharSequence content, final Charset charset) {
121 if (content == null) {
122 return null;
123 }
124 final StringBuilder buf = new StringBuilder();
125 encode(buf, content, charset, UNRESERVED, false);
126 return buf.toString();
127 }
128
129 static String decode(final CharSequence content, final Charset charset, final boolean plusAsBlank) {
130 if (content == null) {
131 return null;
132 }
133 final ByteBuffer bb = ByteBuffer.allocate(content.length());
134 final CharBuffer cb = CharBuffer.wrap(content);
135 while (cb.hasRemaining()) {
136 final char c = cb.get();
137 if (c == '%' && cb.remaining() >= 2) {
138 final char uc = cb.get();
139 final char lc = cb.get();
140 final int u = Character.digit(uc, RADIX);
141 final int l = Character.digit(lc, RADIX);
142 if (u != -1 && l != -1) {
143 bb.put((byte) ((u << 4) + l));
144 } else {
145 bb.put((byte) '%');
146 bb.put((byte) uc);
147 bb.put((byte) lc);
148 }
149 } else if (plusAsBlank && c == '+') {
150 bb.put((byte) ' ');
151 } else {
152 bb.put((byte) c);
153 }
154 }
155 bb.flip();
156 return (charset != null ? charset : StandardCharsets.UTF_8).decode(bb).toString();
157 }
158
159 public static String decode(final CharSequence content, final Charset charset) {
160 return decode(content, charset, false);
161 }
162
163 }