001 /**
002 * Copyright (C) 2009-2011 the original author or authors.
003 * See the notice.md file distributed with this work for additional
004 * information regarding copyright ownership.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.fusesource.restygwt.client;
020
021 import com.google.gwt.i18n.client.DateTimeFormat;
022 import com.google.gwt.json.client.JSONArray;
023 import com.google.gwt.json.client.JSONBoolean;
024 import com.google.gwt.json.client.JSONNull;
025 import com.google.gwt.json.client.JSONNumber;
026 import com.google.gwt.json.client.JSONObject;
027 import com.google.gwt.json.client.JSONString;
028 import com.google.gwt.json.client.JSONValue;
029 import com.google.gwt.xml.client.Document;
030 import com.google.gwt.xml.client.XMLParser;
031
032 import org.fusesource.restygwt.client.Json.Style;
033
034 import java.math.BigDecimal;
035 import java.math.BigInteger;
036 import java.util.ArrayList;
037 import java.util.Collection;
038 import java.util.Date;
039 import java.util.HashMap;
040 import java.util.HashSet;
041 import java.util.List;
042 import java.util.Map;
043 import java.util.Map.Entry;
044 import java.util.Set;
045
046 /**
047 *
048 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
049 * @author <a href="http://www.acuedo.com">Dave Finch</a>
050 */
051 abstract public class AbstractJsonEncoderDecoder<T> implements JsonEncoderDecoder<T> {
052
053 // /////////////////////////////////////////////////////////////////
054 // Built in encoders for the native types.
055 // /////////////////////////////////////////////////////////////////
056 public static final AbstractJsonEncoderDecoder<Boolean> BOOLEAN = new AbstractJsonEncoderDecoder<Boolean>() {
057
058 public Boolean decode(JSONValue value) throws DecodingException {
059 if (value == null || value.isNull() != null) {
060 return null;
061 }
062 JSONBoolean bool = value.isBoolean();
063 if (bool == null) {
064 throw new DecodingException("Expected a json boolean, but was given: " + value);
065 }
066 return bool.booleanValue();
067 }
068
069 public JSONValue encode(Boolean value) throws EncodingException {
070 return (value == null) ? getNullType() : JSONBoolean.getInstance(value);
071 }
072 };
073
074 public static final AbstractJsonEncoderDecoder<Character> CHAR = new AbstractJsonEncoderDecoder<Character>() {
075
076 public Character decode(JSONValue value) throws DecodingException {
077 if (value == null || value.isNull() != null) {
078 return null;
079 }
080 return (char) toDouble(value);
081 }
082
083 public JSONValue encode(Character value) throws EncodingException {
084 return (value == null) ? getNullType() : new JSONNumber(value);
085 }
086 };
087
088 public static final AbstractJsonEncoderDecoder<Byte> BYTE = new AbstractJsonEncoderDecoder<Byte>() {
089
090 public Byte decode(JSONValue value) throws DecodingException {
091 if (value == null || value.isNull() != null) {
092 return null;
093 }
094 return (byte) toDouble(value);
095
096 }
097
098 public JSONValue encode(Byte value) throws EncodingException {
099 if (value == null) {
100 return null;
101 }
102 return new JSONNumber(value);
103 }
104 };
105
106 public static final AbstractJsonEncoderDecoder<Short> SHORT = new AbstractJsonEncoderDecoder<Short>() {
107
108 public Short decode(JSONValue value) throws DecodingException {
109 if (value == null || value.isNull() != null) {
110 return null;
111 }
112 return (short) toDouble(value);
113 }
114
115 public JSONValue encode(Short value) throws EncodingException {
116 return (value == null) ? getNullType() : new JSONNumber(value);
117 }
118 };
119
120 public static final AbstractJsonEncoderDecoder<Integer> INT = new AbstractJsonEncoderDecoder<Integer>() {
121
122 public Integer decode(JSONValue value) throws DecodingException {
123 if (value == null || value.isNull() != null) {
124 return null;
125 }
126 return (int) toDouble(value);
127 }
128
129 public JSONValue encode(Integer value) throws EncodingException {
130 return (value == null) ? getNullType() : new JSONNumber(value);
131 }
132 };
133
134 public static final AbstractJsonEncoderDecoder<Long> LONG = new AbstractJsonEncoderDecoder<Long>() {
135
136 public Long decode(JSONValue value) throws DecodingException {
137 if (value == null || value.isNull() != null) {
138 return null;
139 }
140 return (long) toDouble(value);
141 }
142
143 public JSONValue encode(Long value) throws EncodingException {
144 return (value == null) ? getNullType() : new JSONNumber(value);
145 }
146 };
147
148 public static final AbstractJsonEncoderDecoder<Float> FLOAT = new AbstractJsonEncoderDecoder<Float>() {
149
150 public Float decode(JSONValue value) throws DecodingException {
151 if (value == null || value.isNull() != null) {
152 return null;
153 }
154 return (float) toDouble(value);
155 }
156
157 public JSONValue encode(Float value) throws EncodingException {
158 return (value == null) ? getNullType() : new JSONNumber(value);
159 }
160 };
161
162 public static final AbstractJsonEncoderDecoder<Double> DOUBLE = new AbstractJsonEncoderDecoder<Double>() {
163
164 public Double decode(JSONValue value) throws DecodingException {
165 if (value == null || value.isNull() != null) {
166 return null;
167 }
168 return toDouble(value);
169 }
170
171 public JSONValue encode(Double value) throws EncodingException {
172 return (value == null) ? getNullType() : new JSONNumber(value);
173 }
174 };
175
176 public static final AbstractJsonEncoderDecoder<String> STRING = new AbstractJsonEncoderDecoder<String>() {
177
178 public String decode(JSONValue value) throws DecodingException {
179 if (value == null || value.isNull() != null) {
180 return null;
181 }
182 JSONString str = value.isString();
183 if (str == null) {
184 throw new DecodingException("Expected a json string, but was given: " + value);
185 }
186 return str.stringValue();
187 }
188
189 public JSONValue encode(String value) throws EncodingException {
190 return (value == null) ? getNullType() : new JSONString(value);
191 }
192 };
193
194 public static final AbstractJsonEncoderDecoder<BigDecimal> BIG_DECIMAL = new AbstractJsonEncoderDecoder<BigDecimal>() {
195
196 public BigDecimal decode(JSONValue value) throws DecodingException {
197 if (value == null || value.isNull() != null) {
198 return null;
199 }
200 return toBigDecimal(value);
201 }
202
203 public JSONValue encode(BigDecimal value) throws EncodingException {
204 return (value == null) ? getNullType() : new JSONString(value.toString());
205 }
206 };
207
208 public static final AbstractJsonEncoderDecoder<BigInteger> BIG_INTEGER = new AbstractJsonEncoderDecoder<BigInteger>() {
209
210 public BigInteger decode(JSONValue value) throws DecodingException {
211 if (value == null || value.isNull() != null) {
212 return null;
213 }
214 JSONNumber number = value.isNumber();
215 if (number == null) {
216 JSONString str = value.isString();
217 if (str == null) {
218 throw new DecodingException("Expected a json number r string, but was given: " + value);
219 }
220
221 // Doing a straight conversion from string to BigInteger will not work for large values
222 // So we convert to BigDecimal first and then convert it to BigInteger.
223 return new BigDecimal(str.stringValue()).toBigInteger();
224 }
225
226 // Doing a straight conversion from string to BigInteger will not work for large values
227 // So we convert to BigDecimal first and then convert it to BigInteger.
228 return new BigDecimal(value.toString()).toBigInteger();
229 }
230
231 public JSONValue encode(BigInteger value) throws EncodingException {
232 return (value == null) ? getNullType() : new JSONString(value.toString());
233 }
234 };
235
236 public static final AbstractJsonEncoderDecoder<Document> DOCUMENT = new AbstractJsonEncoderDecoder<Document>() {
237
238 public Document decode(JSONValue value) throws DecodingException {
239 if (value == null || value.isNull() != null) {
240 return null;
241 }
242 JSONString str = value.isString();
243 if (str == null) {
244 throw new DecodingException("Expected a json string, but was given: " + value);
245 }
246 return XMLParser.parse(str.stringValue());
247 }
248
249 public JSONValue encode(Document value) throws EncodingException {
250 return (value == null) ? getNullType() : new JSONString(value.toString());
251 }
252 };
253
254 public static final AbstractJsonEncoderDecoder<JSONValue> JSON_VALUE = new AbstractJsonEncoderDecoder<JSONValue>() {
255
256 public JSONValue decode(JSONValue value) throws DecodingException {
257 return value;
258 }
259
260 public JSONValue encode(JSONValue value) throws EncodingException {
261 return value;
262 }
263 };
264
265 public static final AbstractJsonEncoderDecoder<Date> DATE = new AbstractJsonEncoderDecoder<Date>() {
266
267 public Date decode(JSONValue value) throws DecodingException {
268 if (value == null || value.isNull() != null) {
269 return null;
270 }
271 String format = Defaults.getDateFormat();
272 if (format == null) {
273 JSONNumber num = value.isNumber();
274 if (num == null) {
275 throw new DecodingException("Expected a json number, but was given: " + value);
276 }
277 return new Date((long)num.doubleValue());
278 } else {
279 JSONString str = value.isString();
280 if (str == null) {
281 throw new DecodingException("Expected a json string, but was given: " + value);
282 }
283 return DateTimeFormat.getFormat(format).parse(str.stringValue());
284 }
285 }
286
287 public JSONValue encode(Date value) throws EncodingException {
288 if (value == null) {
289 return getNullType();
290 }
291 String format = Defaults.getDateFormat();
292 if (format == null) {
293 return new JSONNumber(value.getTime());
294 } else {
295 return new JSONString(DateTimeFormat.getFormat(format).format(value));
296 }
297 }
298 };
299
300 // /////////////////////////////////////////////////////////////////
301 // Helper Methods.
302 // /////////////////////////////////////////////////////////////////
303
304 static public BigDecimal toBigDecimal(JSONValue value) {
305 JSONNumber number = value.isNumber();
306 if (number == null) {
307 throw new DecodingException("Expected a json number, but was given: " + value);
308 }
309 return new BigDecimal(value.toString());
310 }
311
312 static public double toDouble(JSONValue value) {
313 JSONNumber number = value.isNumber();
314 if (number == null) {
315 throw new DecodingException("Expected a json number, but was given: " + value);
316 }
317 return number.doubleValue();
318 }
319
320
321 static public JSONObject toObject(JSONValue value) {
322 JSONObject object = value.isObject();
323 if (object == null) {
324 throw new DecodingException("Expected a json object, but was given: " + object);
325 }
326 return object;
327 }
328
329 static public JSONObject toObjectFromWrapper(JSONValue value, String name) {
330 JSONObject object = value.isObject();
331 if (object == null) {
332 throw new DecodingException("Expected a json object, but was given: " + object);
333 }
334 JSONValue result = object.get(name);
335 if (result == null) {
336 throw new DecodingException("No wrapper with name '" + name + "' found in given: " + object);
337 }
338 return toObject(result);
339 }
340
341 static public <Type> List<Type> toList(JSONValue value, AbstractJsonEncoderDecoder<Type> encoder) {
342 if (value == null || value.isNull() != null) {
343 return null;
344 }
345 JSONArray array = value.isArray();
346 if (array == null) {
347 throw new DecodingException("Expected a json array, but was given: " + value);
348 }
349
350 ArrayList<Type> rc = new ArrayList<Type>(array.size());
351 int size = array.size();
352 for (int i = 0; i < size; i++) {
353 rc.add(encoder.decode(array.get(i)));
354 }
355 return rc;
356 }
357
358 static public <Type> Set<Type> toSet(JSONValue value, AbstractJsonEncoderDecoder<Type> encoder) {
359 if (value == null || value.isNull() != null) {
360 return null;
361 }
362 JSONArray array = value.isArray();
363 if (array == null) {
364 throw new DecodingException("Expected a json array, but was given: " + value);
365 }
366
367 HashSet<Type> rc = new HashSet<Type>(array.size() * 2);
368 int size = array.size();
369 for (int i = 0; i < size; i++) {
370 rc.add(encoder.decode(array.get(i)));
371 }
372 return rc;
373 }
374
375 static public <Type> Map<String, Type> toMap(JSONValue value, AbstractJsonEncoderDecoder<Type> encoder, Style style) {
376 if (value == null || value.isNull() != null) {
377 return null;
378 }
379
380 switch (style) {
381 case DEFAULT:
382 case SIMPLE: {
383 JSONObject object = value.isObject();
384 if (object == null) {
385 throw new DecodingException("Expected a json object, but was given: " + value);
386 }
387
388 HashMap<String, Type> rc = new HashMap<String, Type>(object.size() * 2);
389 for (String key : object.keySet()) {
390 rc.put(key, encoder.decode(object.get(key)));
391 }
392 return rc;
393 }
394 case JETTISON_NATURAL: {
395 JSONObject object = value.isObject();
396 if (object == null) {
397 throw new DecodingException("Expected a json object, but was given: " + value);
398 }
399 value = object.get("entry");
400 if (value == null) {
401 throw new DecodingException("Expected an entry array not found");
402 }
403 JSONArray entries = value.isArray();
404 if (entries == null) {
405 throw new DecodingException("Expected an entry array, but was given: " + value);
406 }
407
408 HashMap<String, Type> rc = new HashMap<String, Type>(object.size() * 2);
409 for (int i = 0; i < entries.size(); i++) {
410 JSONObject entry = entries.get(i).isObject();
411 if (entry == null)
412 throw new DecodingException("Expected an entry object, but was given: " + value);
413 JSONValue key = entry.get("key");
414 if (key == null)
415 throw new DecodingException("Expected an entry key field not found");
416 JSONString k = key.isString();
417 if (k == null)
418 throw new DecodingException("Expected an entry key to be a string, but was given: " + value);
419
420 rc.put(k.stringValue(), encoder.decode(entry.get("value")));
421 }
422 return rc;
423 }
424 default:
425 throw new UnsupportedOperationException("The encoding style is not yet suppored: " + style.name());
426 }
427 }
428
429 static public <Type> JSONValue toJSON(Map<String, Type> value, AbstractJsonEncoderDecoder<Type> encoder, Style style) {
430 if (value == null) {
431 return JSONNull.getInstance();
432 }
433
434 switch (style) {
435 case DEFAULT:
436 case SIMPLE: {
437 JSONObject rc = new JSONObject();
438 for (Entry<String, Type> t : value.entrySet()) {
439 rc.put(t.getKey(), encoder.encode(t.getValue()));
440 }
441 return rc;
442 }
443 case JETTISON_NATURAL: {
444 JSONObject rc = new JSONObject();
445 JSONArray entries = new JSONArray();
446 int i = 0;
447 for (Entry<String, Type> t : value.entrySet()) {
448 JSONObject entry = new JSONObject();
449 entry.put("key", new JSONString(t.getKey()));
450 entry.put("value", encoder.encode(t.getValue()));
451 entries.set(i++, entry);
452 }
453 rc.put("entry", entries);
454 return rc;
455 }
456 default:
457 throw new UnsupportedOperationException("The encoding style is not yet suppored: " + style.name());
458 }
459 }
460
461 static public <Type> JSONValue toJSON(Collection<Type> value, AbstractJsonEncoderDecoder<Type> encoder) {
462 if (value == null) {
463 return JSONNull.getInstance();
464 }
465 JSONArray rc = new JSONArray();
466 int i = 0;
467 for (Type t : value) {
468 rc.set(i++, encoder.encode(t));
469 }
470 return rc;
471 }
472
473 static private JSONNull getNullType() {
474 return (Defaults.doesIgnoreJsonNulls()) ? null : JSONNull.getInstance();
475 }
476 }