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.rebind;
020    
021    import java.math.BigDecimal;
022    import java.math.BigInteger;
023    import java.util.Date;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    import com.google.gwt.core.ext.GeneratorContext;
029    import com.google.gwt.core.ext.TreeLogger;
030    import com.google.gwt.core.ext.UnableToCompleteException;
031    import com.google.gwt.core.ext.typeinfo.JClassType;
032    import com.google.gwt.core.ext.typeinfo.JParameterizedType;
033    import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
034    import com.google.gwt.core.ext.typeinfo.JType;
035    import com.google.gwt.json.client.JSONValue;
036    import com.google.gwt.xml.client.Document;
037    import org.fusesource.restygwt.client.AbstractJsonEncoderDecoder;
038    import org.fusesource.restygwt.client.Json;
039    import org.fusesource.restygwt.client.Json.Style;
040    import static org.fusesource.restygwt.rebind.BaseSourceCreator.*;
041    
042    /**
043     *
044     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
045     */
046    public class JsonEncoderDecoderInstanceLocator {
047    
048        public static final String JSON_ENCODER_DECODER_CLASS = AbstractJsonEncoderDecoder.class.getName();
049        public static final String JSON_CLASS = Json.class.getName();
050    
051        public final JClassType STRING_TYPE;
052        public final JClassType JSON_VALUE_TYPE;
053        public final JClassType DOCUMENT_TYPE;
054        public final JClassType MAP_TYPE;
055        public final JClassType SET_TYPE;
056        public final JClassType LIST_TYPE;
057    
058        public final HashMap<JType, String> builtInEncoderDecoders = new HashMap<JType, String>();
059    
060        public final GeneratorContext context;
061        public final TreeLogger logger;
062    
063        public JsonEncoderDecoderInstanceLocator(GeneratorContext context, TreeLogger logger) throws UnableToCompleteException {
064            this.context = context;
065            this.logger = logger;
066    
067            this.STRING_TYPE = find(String.class);
068            this.JSON_VALUE_TYPE = find(JSONValue.class);
069            this.DOCUMENT_TYPE = find(Document.class);
070            this.MAP_TYPE = find(Map.class);
071            this.SET_TYPE = find(Set.class);
072            this.LIST_TYPE = find(List.class);
073    
074            builtInEncoderDecoders.put(JPrimitiveType.BOOLEAN, JSON_ENCODER_DECODER_CLASS + ".BOOLEAN");
075            builtInEncoderDecoders.put(JPrimitiveType.BYTE, JSON_ENCODER_DECODER_CLASS + ".BYTE");
076            builtInEncoderDecoders.put(JPrimitiveType.CHAR, JSON_ENCODER_DECODER_CLASS + ".CHAR");
077            builtInEncoderDecoders.put(JPrimitiveType.SHORT, JSON_ENCODER_DECODER_CLASS + ".SHORT");
078            builtInEncoderDecoders.put(JPrimitiveType.INT, JSON_ENCODER_DECODER_CLASS + ".INT");
079            builtInEncoderDecoders.put(JPrimitiveType.LONG, JSON_ENCODER_DECODER_CLASS + ".LONG");
080            builtInEncoderDecoders.put(JPrimitiveType.FLOAT, JSON_ENCODER_DECODER_CLASS + ".FLOAT");
081            builtInEncoderDecoders.put(JPrimitiveType.DOUBLE, JSON_ENCODER_DECODER_CLASS + ".DOUBLE");
082            builtInEncoderDecoders.put(find(Boolean.class), JSON_ENCODER_DECODER_CLASS + ".BOOLEAN");
083            builtInEncoderDecoders.put(find(Byte.class), JSON_ENCODER_DECODER_CLASS + ".BYTE");
084            builtInEncoderDecoders.put(find(Character.class), JSON_ENCODER_DECODER_CLASS + ".CHAR");
085            builtInEncoderDecoders.put(find(Short.class), JSON_ENCODER_DECODER_CLASS + ".SHORT");
086            builtInEncoderDecoders.put(find(Integer.class), JSON_ENCODER_DECODER_CLASS + ".INT");
087            builtInEncoderDecoders.put(find(Long.class), JSON_ENCODER_DECODER_CLASS + ".LONG");
088            builtInEncoderDecoders.put(find(Float.class), JSON_ENCODER_DECODER_CLASS + ".FLOAT");
089            builtInEncoderDecoders.put(find(Double.class), JSON_ENCODER_DECODER_CLASS + ".DOUBLE");
090            builtInEncoderDecoders.put(find(BigDecimal.class), JSON_ENCODER_DECODER_CLASS + ".BIG_DECIMAL");
091            builtInEncoderDecoders.put(find(BigInteger.class), JSON_ENCODER_DECODER_CLASS + ".BIG_INTEGER");
092    
093            builtInEncoderDecoders.put(STRING_TYPE, JSON_ENCODER_DECODER_CLASS + ".STRING");
094            builtInEncoderDecoders.put(DOCUMENT_TYPE, JSON_ENCODER_DECODER_CLASS + ".DOCUMENT");
095            builtInEncoderDecoders.put(JSON_VALUE_TYPE, JSON_ENCODER_DECODER_CLASS + ".JSON_VALUE");
096    
097            builtInEncoderDecoders.put(find(Date.class), JSON_ENCODER_DECODER_CLASS + ".DATE");
098    
099        }
100    
101        private JClassType find(Class<?> type) throws UnableToCompleteException {
102            return find(type.getName());
103        }
104    
105        private JClassType find(String type) throws UnableToCompleteException {
106            return RestServiceGenerator.find(logger, context, type);
107        }
108    
109        private String getEncoderDecoder(JType type, TreeLogger logger) throws UnableToCompleteException {
110            String rc = builtInEncoderDecoders.get(type);
111            if (rc == null) {
112                JClassType ct = type.isClass();
113                if (ct != null && !isCollectionType(ct)) {
114                    JsonEncoderDecoderClassCreator generator = new JsonEncoderDecoderClassCreator(logger, context, ct);
115                    return generator.create() + ".INSTANCE";
116                }
117            }
118            return rc;
119        }
120    
121        public String encodeExpression(JType type, String expression, Style style) throws UnableToCompleteException {
122            return encodeDecodeExpression(type, expression, style, "encode", JSON_ENCODER_DECODER_CLASS + ".toJSON", JSON_ENCODER_DECODER_CLASS + ".toJSON", JSON_ENCODER_DECODER_CLASS
123                    + ".toJSON");
124        }
125    
126        public String decodeExpression(JType type, String expression, Style style) throws UnableToCompleteException {
127            return encodeDecodeExpression(type, expression, style, "decode", JSON_ENCODER_DECODER_CLASS + ".toMap", JSON_ENCODER_DECODER_CLASS + ".toSet", JSON_ENCODER_DECODER_CLASS
128                    + ".toList");
129        }
130    
131        private String encodeDecodeExpression(JType type, String expression, Style style, String encoderMethod, String mapMethod, String setMethod, String listMethod)
132                throws UnableToCompleteException {
133    
134            if (null != type.isEnum()) {
135                if (encoderMethod.equals("encode")) {
136                    return encodeDecodeExpression(STRING_TYPE, expression + ".name()", style, encoderMethod, mapMethod, setMethod, listMethod);
137                } else {
138                    return type.getQualifiedSourceName() + ".valueOf(" + encodeDecodeExpression(STRING_TYPE, expression, style, encoderMethod, mapMethod, setMethod, listMethod) + ")";
139                }
140            }
141    
142            String encoderDecoder = getEncoderDecoder(type, logger);
143            if (encoderDecoder != null) {
144                return encoderDecoder + "." + encoderMethod + "(" + expression + ")";
145            }
146    
147            JClassType clazz = type.isClassOrInterface();
148    
149            if (isCollectionType(clazz)) {
150                JParameterizedType parameterizedType = type.isParameterized();
151                if (parameterizedType == null || parameterizedType.getTypeArgs() == null) {
152                    error("Collection types must be parameterized.");
153                }
154                JClassType[] types = parameterizedType.getTypeArgs();
155    
156                if (clazz.isAssignableTo(MAP_TYPE)) {
157                    if (types.length != 2) {
158                        error("Map must define two and only two type parameters");
159                    }
160                    if (types[0] != STRING_TYPE) {
161                        error("Map's first type parameter must be of type String");
162                    }
163                    encoderDecoder = getEncoderDecoder(types[1], logger);
164                    if (encoderDecoder != null) {
165                        return mapMethod + "(" + expression + ", " + encoderDecoder + ", " + JSON_CLASS + ".Style." + style.name() + ")";
166                    }
167                } else if (clazz.isAssignableTo(SET_TYPE)) {
168                    if (types.length != 1) {
169                        error("Set must define one and only one type parameter");
170                    }
171                    encoderDecoder = getEncoderDecoder(types[0], logger);
172                    if (encoderDecoder != null) {
173                        return setMethod + "(" + expression + ", " + encoderDecoder + ")";
174                    }
175                } else if (clazz.isAssignableTo(LIST_TYPE)) {
176                    if (types.length != 1) {
177                        error("List must define one and only one type parameter");
178                    }
179                    encoderDecoder = getEncoderDecoder(types[0], logger);
180                    info("type encoder for: " + types[0] + " is " + encoderDecoder);
181                    if (encoderDecoder != null) {
182                        return listMethod + "(" + expression + ", " + encoderDecoder + ")";
183                    }
184                }
185            }
186    
187            error("Do not know how to encode/decode " + type);
188            return null;
189        }
190    
191        boolean isCollectionType(JClassType clazz) {
192            return clazz != null && (clazz.isAssignableTo(SET_TYPE) || clazz.isAssignableTo(LIST_TYPE) || clazz.isAssignableTo(MAP_TYPE));
193        }
194    
195        protected void error(String msg) throws UnableToCompleteException {
196            logger.log(ERROR, msg);
197            throw new UnableToCompleteException();
198        }
199    
200        protected void warn(String msg) throws UnableToCompleteException {
201            logger.log(WARN, msg);
202            throw new UnableToCompleteException();
203        }
204    
205        protected void info(String msg) throws UnableToCompleteException {
206            logger.log(INFO, msg);
207        }
208    
209        protected void debug(String msg) throws UnableToCompleteException {
210            logger.log(DEBUG, msg);
211        }
212    
213        protected void trace(String msg) throws UnableToCompleteException {
214            logger.log(TRACE, msg);
215        }
216    
217    }