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 }