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