1 package net.minidev.json;
2
3 /*
4 * Copyright 2011 JSON-SMART authors
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 import java.io.IOException;
19
20 /**
21 * protected class used to stored Internal methods
22 *
23 * @author Uriel Chemouni <uchemouni@gmail.com>
24 */
25 class JStylerObj {
26
27 public final static MPSimple MP_SIMPLE = new MPSimple();
28 public final static MPTrue MP_TRUE = new MPTrue();
29 public final static MPAgressive MP_AGGRESIVE = new MPAgressive();
30
31 public final static EscapeLT ESCAPE_LT = new EscapeLT();
32 public final static Escape4Web ESCAPE4Web = new Escape4Web();
33
34 public static interface MustProtect {
35 public boolean mustBeProtect(String s);
36 }
37
38 private static class MPTrue implements MustProtect {
39 public boolean mustBeProtect(String s) {
40 return true;
41 }
42 }
43
44 private static class MPSimple implements MustProtect {
45 /**
46 * can a String can be store without enclosing quotes. ie: should not
47 * contain any special json char
48 *
49 * @param s
50 * @return
51 */
52 public boolean mustBeProtect(final String s) {
53 if (s == null)
54 return false;
55 int len = s.length();
56 if (len == 0)
57 return true;
58 if (s.trim() != s)
59 return true;
60
61 char ch = s.charAt(0);
62 if (ch >= '0' && ch <= '9' || ch == '-')
63 return true;
64
65 for (int i = 0; i < len; i++) {
66 ch = s.charAt(i);
67 if (isSpace(ch))
68 return true;
69 if (isSpecial(ch))
70 return true;
71 if (isSpecialChar(ch))
72 return true;
73 if (isUnicode(ch))
74 return true;
75 }
76 // keyword check
77 if (isKeyword(s))
78 return true;
79 return false;
80 }
81 }
82
83 private static class MPAgressive implements MustProtect {
84 public boolean mustBeProtect(final String s) {
85 if (s == null)
86 return false;
87 int len = s.length();
88 // protect empty String
89 if (len == 0)
90 return true;
91
92 // protect trimable String
93 if (s.trim() != s)
94 return true;
95
96 // json special char
97 char ch = s.charAt(0);
98 if (isSpecial(ch) || isUnicode(ch))
99 return true;
100
101 for (int i = 1; i < len; i++) {
102 ch = s.charAt(i);
103 if (isSpecialClose(ch) || isUnicode(ch))
104 return true;
105 }
106 // keyWord must be protect
107 if (isKeyword(s))
108 return true;
109 // Digit like text must be protect
110 ch = s.charAt(0);
111 // only test String if First Ch is a digit
112 if (ch >= '0' && ch <= '9' || ch == '-') {
113 int p = 1;
114 // skip first digits
115 for (; p < len; p++) {
116 ch = s.charAt(p);
117 if (ch < '0' || ch > '9')
118 break;
119 }
120 // int/long
121 if (p == len)
122 return true;
123 // Floating point
124 if (ch == '.') {
125 p++;
126 }
127 // Skip digits
128 for (; p < len; p++) {
129 ch = s.charAt(p);
130 if (ch < '0' || ch > '9')
131 break;
132 }
133 if (p == len)
134 return true; // can be read as an floating number
135 // Double
136 if (ch == 'E' || ch == 'e') {
137 p++;
138 if (p == len) // no power data not a digits
139 return false;
140 ch = s.charAt(p);
141 if (ch == '+' || ch == '-') {
142 p++;
143 ch = s.charAt(p);
144 }
145 }
146 if (p == len) // no power data => not a digit
147 return false;
148
149 for (; p < len; p++) {
150 ch = s.charAt(p);
151 if (ch < '0' || ch > '9')
152 break;
153 }
154 // floating point With power of data.
155 if (p == len)
156 return true;
157 return false;
158 }
159 return false;
160 }
161 }
162
163 public static boolean isSpace(char c) {
164 return (c == '\r' || c == '\n' || c == '\t' || c == ' ');
165 }
166
167 public static boolean isSpecialChar(char c) {
168 return (c == '\b' || c == '\f' || c == '\n');
169 }
170
171 public static boolean isSpecialOpen(char c) {
172 return (c == '{' || c == '[' || c == ',' || c == ':');
173 }
174
175 public static boolean isSpecialClose(char c) {
176 return (c == '}' || c == ']' || c == ',' || c == ':');
177 }
178
179 public static boolean isSpecial(char c) {
180 return (c == '{' || c == '[' || c == ',' || c == '}' || c == ']' || c == ':' || c == '\'' || c == '"');
181 }
182
183 public static boolean isUnicode(char c) {
184 // ANSI controle char
185 return ((c >= '\u0000' && c <= '\u001F') ||
186 // DEL or unicode ctrl
187 (c >= '\u007F' && c <= '\u009F') ||
188 // '\u00A0' No-breakable space ?
189 // En Quad .. more
190 (c >= '\u2000' && c <= '\u20FF'));
191 }
192
193 public static boolean isKeyword(String s) {
194 if (s.length() < 3)
195 return false;
196 char c = s.charAt(0);
197 if (c == 'n')
198 return s.equals("null");
199 if (c == 't')
200 return s.equals("true");
201 if (c == 'f')
202 return s.equals("false");
203 if (c == 'N')
204 return s.equals("NaN");
205 return false;
206 }
207
208 public static interface StringProtector {
209 public void escape(String s, Appendable out);
210 }
211
212 private static class EscapeLT implements StringProtector {
213 /**
214 * Escape special chars form String except /
215 *
216 * @param s
217 * - Must not be null.
218 * @param out
219 */
220 public void escape(String s, Appendable out) {
221 try {
222 int len = s.length();
223 for (int i = 0; i < len; i++) {
224 char ch = s.charAt(i);
225 switch (ch) {
226 case '"':
227 out.append("\\\"");
228 break;
229 case '\\':
230 out.append("\\\\");
231 break;
232 case '\b':
233 out.append("\\b");
234 break;
235 case '\f':
236 out.append("\\f");
237 break;
238 case '\n':
239 out.append("\\n");
240 break;
241 case '\r':
242 out.append("\\r");
243 break;
244 case '\t':
245 out.append("\\t");
246 break;
247 default:
248 // Reference:
249 // http://www.unicode.org/versions/Unicode5.1.0/
250 if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F')
251 || (ch >= '\u2000' && ch <= '\u20FF')) {
252 out.append("\\u");
253 String hex = "0123456789ABCDEF";
254 out.append(hex.charAt(ch >> 12 & 0x000F));
255 out.append(hex.charAt(ch >> 8 & 0x000F));
256 out.append(hex.charAt(ch >> 4 & 0x000F));
257 out.append(hex.charAt(ch >> 0 & 0x000F));
258 } else {
259 out.append(ch);
260 }
261 }
262 }
263 } catch (IOException e) {
264 throw new RuntimeException("Impossible Exeption");
265 }
266 }
267 }
268
269 private static class Escape4Web implements StringProtector {
270
271 /**
272 * Escape special chars form String including /
273 *
274 * @param s
275 * - Must not be null.
276 * @param sb
277 */
278 public void escape(String s, Appendable sb) {
279 try {
280 int len = s.length();
281 for (int i = 0; i < len; i++) {
282 char ch = s.charAt(i);
283 switch (ch) {
284 case '"':
285 sb.append("\\\"");
286 break;
287 case '\\':
288 sb.append("\\\\");
289 break;
290 case '\b':
291 sb.append("\\b");
292 break;
293 case '\f':
294 sb.append("\\f");
295 break;
296 case '\n':
297 sb.append("\\n");
298 break;
299 case '\r':
300 sb.append("\\r");
301 break;
302 case '\t':
303 sb.append("\\t");
304 break;
305 case '/':
306 sb.append("\\/");
307 break;
308 default:
309 // Reference:
310 // http://www.unicode.org/versions/Unicode5.1.0/
311 if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F')
312 || (ch >= '\u2000' && ch <= '\u20FF')) {
313 sb.append("\\u");
314 String hex = "0123456789ABCDEF";
315 sb.append(hex.charAt(ch >> 12 & 0x0F));
316 sb.append(hex.charAt(ch >> 8 & 0x0F));
317 sb.append(hex.charAt(ch >> 4 & 0x0F));
318 sb.append(hex.charAt(ch >> 0 & 0x0F));
319 } else {
320 sb.append(ch);
321 }
322 }
323 }
324 } catch (IOException e) {
325 throw new RuntimeException("Impossible Error");
326 }
327 }
328 }
329 }
330