jpayne@68
|
1 package server;
|
jpayne@68
|
2
|
jpayne@68
|
3 import java.util.BitSet;
|
jpayne@68
|
4 import java.util.HashMap;
|
jpayne@68
|
5
|
jpayne@68
|
6 import structures.ByteBuilder;
|
jpayne@68
|
7
|
jpayne@68
|
8 public class PercentEncoding {
|
jpayne@68
|
9
|
jpayne@68
|
10 public static boolean containsSpecialSymbol(String s){
|
jpayne@68
|
11 if(s==null){return false;}
|
jpayne@68
|
12 for(int i=0, max=s.length(); i<max; i++){
|
jpayne@68
|
13 char c=s.charAt(i);
|
jpayne@68
|
14 if(isSpecial.get(c)){
|
jpayne@68
|
15 // System.err.print("b");
|
jpayne@68
|
16 return true;}
|
jpayne@68
|
17 }
|
jpayne@68
|
18 // System.err.print("c");
|
jpayne@68
|
19 return false;
|
jpayne@68
|
20 }
|
jpayne@68
|
21
|
jpayne@68
|
22 public static boolean containsCommonSymbol(String s){
|
jpayne@68
|
23 if(s==null){return false;}
|
jpayne@68
|
24 for(int i=0, max=s.length(); i<max; i++){
|
jpayne@68
|
25 char c=s.charAt(i);
|
jpayne@68
|
26 if(isCommon.get(c)){return true;}
|
jpayne@68
|
27 }
|
jpayne@68
|
28 return false;
|
jpayne@68
|
29 }
|
jpayne@68
|
30
|
jpayne@68
|
31 public static String symbolToCode(String s){
|
jpayne@68
|
32 // System.err.print("a");
|
jpayne@68
|
33 if(!containsSpecialSymbol(s)){return s;}
|
jpayne@68
|
34 // System.err.print("d");
|
jpayne@68
|
35 ByteBuilder bb=new ByteBuilder();
|
jpayne@68
|
36 for(int i=0, max=s.length(); i<max; i++){
|
jpayne@68
|
37 char c=s.charAt(i);
|
jpayne@68
|
38 String code=symbolToCodeArray[c];
|
jpayne@68
|
39 if(code!=null){
|
jpayne@68
|
40 // System.err.print("e("+code+")");
|
jpayne@68
|
41 bb.append(code);
|
jpayne@68
|
42 }else{
|
jpayne@68
|
43 // System.err.print("f");
|
jpayne@68
|
44 bb.append(c);
|
jpayne@68
|
45 }
|
jpayne@68
|
46 }
|
jpayne@68
|
47 // System.err.println("g");
|
jpayne@68
|
48 // System.err.println(bb);
|
jpayne@68
|
49 return bb.toString();
|
jpayne@68
|
50 }
|
jpayne@68
|
51
|
jpayne@68
|
52 public static String commonSymbolToCode(String s){
|
jpayne@68
|
53 if(!containsCommonSymbol(s)){return s;}
|
jpayne@68
|
54 ByteBuilder bb=new ByteBuilder();
|
jpayne@68
|
55 for(int i=0, max=s.length(); i<max; i++){
|
jpayne@68
|
56 char c=s.charAt(i);
|
jpayne@68
|
57 if(isCommon.get(c)){
|
jpayne@68
|
58 String code=symbolToCodeArray[c];
|
jpayne@68
|
59 assert(code!=null);
|
jpayne@68
|
60 bb.append(code);
|
jpayne@68
|
61 }else{
|
jpayne@68
|
62 bb.append(c);
|
jpayne@68
|
63 }
|
jpayne@68
|
64 }
|
jpayne@68
|
65 return bb.toString();
|
jpayne@68
|
66 }
|
jpayne@68
|
67
|
jpayne@68
|
68 private static int parseCode(String s, int start){
|
jpayne@68
|
69 if(s==null || start+2>=s.length()){return -1;}
|
jpayne@68
|
70 assert(s.charAt(start)=='%');
|
jpayne@68
|
71 int sum=0;
|
jpayne@68
|
72 for(int i=start+1; i<=start+2; i++){
|
jpayne@68
|
73 sum=sum<<4;
|
jpayne@68
|
74 final char c=s.charAt(i);
|
jpayne@68
|
75 if(c>='0' && c<='9'){
|
jpayne@68
|
76 sum=sum+(c-'0');
|
jpayne@68
|
77 }else if(c>='A' && c<'F'){
|
jpayne@68
|
78 sum=sum+(10+c-'A');
|
jpayne@68
|
79 }else{
|
jpayne@68
|
80 return -1;
|
jpayne@68
|
81 }
|
jpayne@68
|
82 }
|
jpayne@68
|
83 return sum;
|
jpayne@68
|
84 }
|
jpayne@68
|
85
|
jpayne@68
|
86 public static String codeToSymbol(String s){
|
jpayne@68
|
87 int idx=s.indexOf('%');
|
jpayne@68
|
88 if(idx<0){return s;}
|
jpayne@68
|
89
|
jpayne@68
|
90 ByteBuilder bb=new ByteBuilder(s.length());
|
jpayne@68
|
91 for(int i=0; i<s.length(); i++){
|
jpayne@68
|
92 char c=s.charAt(i);
|
jpayne@68
|
93 if(c=='%'){
|
jpayne@68
|
94 int sym=parseCode(s, i);
|
jpayne@68
|
95 if(sym<0){bb.append(c);}
|
jpayne@68
|
96 else{
|
jpayne@68
|
97 bb.append((char)sym);
|
jpayne@68
|
98 i+=2;//Skip next 2 characters
|
jpayne@68
|
99 }
|
jpayne@68
|
100 }else{bb.append(c);}
|
jpayne@68
|
101 }
|
jpayne@68
|
102 return (bb.length()==s.length() ? s : bb.toString());
|
jpayne@68
|
103 }
|
jpayne@68
|
104
|
jpayne@68
|
105 private static HashMap<String, String> makeCodeToSymbolMap() {
|
jpayne@68
|
106 HashMap<String, String> map=new HashMap<String, String>(129);
|
jpayne@68
|
107 assert(reservedSymbol.length==reservedCode.length);
|
jpayne@68
|
108 assert(commonSymbol.length==commonCode.length);
|
jpayne@68
|
109 for(int i=0; i<reservedSymbol.length; i++){
|
jpayne@68
|
110 map.put(reservedCode[i], reservedSymbol[i]);
|
jpayne@68
|
111 }
|
jpayne@68
|
112 for(int i=0; i<commonSymbol.length; i++){
|
jpayne@68
|
113 map.put(commonCode[i], commonSymbol[i]);
|
jpayne@68
|
114 }
|
jpayne@68
|
115 return map;
|
jpayne@68
|
116 }
|
jpayne@68
|
117
|
jpayne@68
|
118 private static HashMap<String, String> makeSymbolToCodeMap() {
|
jpayne@68
|
119 HashMap<String, String> map=new HashMap<String, String>(257);
|
jpayne@68
|
120 assert(reservedSymbol.length==reservedCode.length);
|
jpayne@68
|
121 assert(commonSymbol.length==commonCode.length);
|
jpayne@68
|
122 for(int i=0; i<reservedSymbol.length; i++){
|
jpayne@68
|
123 map.put(reservedSymbol[i], reservedCode[i]);
|
jpayne@68
|
124 }
|
jpayne@68
|
125 for(int i=0; i<commonSymbol.length; i++){
|
jpayne@68
|
126 map.put(commonSymbol[i], commonCode[i]);
|
jpayne@68
|
127 }
|
jpayne@68
|
128 return map;
|
jpayne@68
|
129 }
|
jpayne@68
|
130
|
jpayne@68
|
131 private static String[] makeSymbolToCodeArray() {
|
jpayne@68
|
132 final String[] array=new String[128];
|
jpayne@68
|
133 for(int i=0; i<reservedSymbol.length; i++){
|
jpayne@68
|
134 String s=reservedSymbol[i];
|
jpayne@68
|
135 String c=reservedCode[i];
|
jpayne@68
|
136 array[s.charAt(0)]=c;
|
jpayne@68
|
137 }
|
jpayne@68
|
138 for(int i=0; i<commonSymbol.length; i++){
|
jpayne@68
|
139 String s=commonSymbol[i];
|
jpayne@68
|
140 String c=commonCode[i];
|
jpayne@68
|
141 array[s.charAt(0)]=c;
|
jpayne@68
|
142 }
|
jpayne@68
|
143 return array;
|
jpayne@68
|
144 }
|
jpayne@68
|
145
|
jpayne@68
|
146 private static final BitSet makeBitSet(String[]...matrix){
|
jpayne@68
|
147 BitSet bs=new BitSet(128);
|
jpayne@68
|
148 for(String[] array : matrix){
|
jpayne@68
|
149 for(String s : array){
|
jpayne@68
|
150 char c=s.charAt(0);
|
jpayne@68
|
151 bs.set(c);
|
jpayne@68
|
152 }
|
jpayne@68
|
153 }
|
jpayne@68
|
154 return bs;
|
jpayne@68
|
155 }
|
jpayne@68
|
156
|
jpayne@68
|
157 //See https://en.wikipedia.org/wiki/Percent-encoding
|
jpayne@68
|
158 public static final String[] reservedSymbol=new String[] {
|
jpayne@68
|
159 "!", "#", "$", "&", "'", "(", ")", "*", "+", ",", "/", ":", ";", "=", "?", "@", "[", "]"
|
jpayne@68
|
160 };
|
jpayne@68
|
161
|
jpayne@68
|
162 public static final String[] reservedCode=new String[] {
|
jpayne@68
|
163 "%21", "%23", "%24", "%26", "%27", "%28", "%29", "%2A", "%2B", "%2C", "%2F", "%3A", "%3B", "%3D", "%3F", "%40", "%5B", "%5D"
|
jpayne@68
|
164 };
|
jpayne@68
|
165
|
jpayne@68
|
166 public static final String[] commonSymbol=new String[] {
|
jpayne@68
|
167 "\n", " ", "\"", "%", "<", ">", "\\", "|",
|
jpayne@68
|
168 };
|
jpayne@68
|
169
|
jpayne@68
|
170 public static final String[] commonCode=new String[] {
|
jpayne@68
|
171 "%0A", "%20", "%22", "%25", "%3C", "%3E", "%5C", "%7C"
|
jpayne@68
|
172 };
|
jpayne@68
|
173
|
jpayne@68
|
174 private static final BitSet isSpecial=makeBitSet(reservedSymbol, commonSymbol);
|
jpayne@68
|
175 private static final BitSet isCommon=makeBitSet(commonSymbol);
|
jpayne@68
|
176
|
jpayne@68
|
177 // public static final HashMap<String, String> codeToSymbolMap=makeCodeToSymbolMap();
|
jpayne@68
|
178 // public static final HashMap<String, String> symbolToCodeMap=makeSymbolToCodeMap();
|
jpayne@68
|
179 public static final String[] symbolToCodeArray=makeSymbolToCodeArray();
|
jpayne@68
|
180
|
jpayne@68
|
181 /** Don't print caught exceptions */
|
jpayne@68
|
182 public static boolean suppressErrors=false;
|
jpayne@68
|
183
|
jpayne@68
|
184 }
|