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    }