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