001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.generator.as3;
022
023 import java.io.ByteArrayOutputStream;
024 import java.io.File;
025 import java.io.IOException;
026 import java.io.OutputStream;
027 import java.io.OutputStreamWriter;
028 import java.io.PrintWriter;
029 import java.lang.reflect.ParameterizedType;
030 import java.lang.reflect.Type;
031 import java.net.URL;
032 import java.text.DateFormat;
033 import java.util.ArrayList;
034 import java.util.Date;
035 import java.util.HashMap;
036 import java.util.HashSet;
037 import java.util.List;
038 import java.util.Map;
039 import java.util.Set;
040
041 import org.granite.generator.Generator;
042 import org.granite.generator.Input;
043 import org.granite.generator.Listener;
044 import org.granite.generator.TemplateUri;
045 import org.granite.generator.as3.reflect.ClientImport;
046 import org.granite.generator.as3.reflect.JavaAbstractType.GenerationType;
047 import org.granite.generator.as3.reflect.JavaEnum;
048 import org.granite.generator.as3.reflect.JavaFieldProperty;
049 import org.granite.generator.as3.reflect.JavaImport;
050 import org.granite.generator.as3.reflect.JavaInterface;
051 import org.granite.generator.as3.reflect.JavaProperty;
052 import org.granite.generator.as3.reflect.JavaRemoteDestination;
053 import org.granite.generator.as3.reflect.JavaType;
054 import org.granite.generator.as3.reflect.JavaType.Kind;
055 import org.granite.generator.as3.reflect.JavaTypeFactory;
056 import org.granite.generator.exception.TemplateException;
057 import org.granite.generator.exception.TemplateUriException;
058 import org.granite.generator.gsp.AbstractGroovyTransformer;
059 import org.granite.generator.gsp.GroovyTemplate;
060 import org.granite.util.ClassUtil;
061
062 /**
063 * @author Franck WOLFF
064 */
065 public abstract class JavaClientGroovyTransformer
066 extends AbstractGroovyTransformer<JavaAs3Input, JavaAs3Output, JavaAs3GroovyConfiguration>
067 implements JavaTypeFactory {
068
069 private static final String GENERATED_BASE_SUFFIX = "Base";
070
071 protected final Map<Class<?>, JavaType> javaTypes = new HashMap<Class<?>, JavaType>();
072 protected final Map<String, JavaImport> javaImports = new HashMap<String, JavaImport>();
073 protected final Map<String, JavaImport> javaPropertyImports = new HashMap<String, JavaImport>();
074
075 public JavaClientGroovyTransformer() {
076 }
077
078 public JavaClientGroovyTransformer(JavaAs3GroovyConfiguration config, Listener listener) {
079 super(config, listener);
080 }
081
082 @Override
083 public boolean accept(Input<?> input) {
084 return (input instanceof JavaAs3Input);
085 }
086
087 @Override
088 protected JavaAs3Output[] getOutputs(JavaAs3Input input) throws IOException, TemplateUriException {
089 JavaType javaType = getJavaType(input.getType());
090 input.setJavaType(javaType);
091 TemplateUri[] templateUris = getTemplateUris(javaType);
092 boolean hasBaseTemplate = templateUris.length > 1;
093
094 JavaAs3Output[] outputs = new JavaAs3Output[templateUris.length];
095
096 for (int i = 0; i < templateUris.length; i++) {
097 GroovyTemplate template = getTemplate(templateUris[i]);
098 File dir = getOutputDir(input, template);
099 File file = getOutputFile(input, template, dir);
100 boolean outdated = isOutdated(input, template, file, hasBaseTemplate);
101 String status = getOutputStatus(input, template, file, hasBaseTemplate);
102
103 outputs[i] = new JavaAs3Output(
104 javaType,
105 template,
106 dir,
107 file,
108 outdated,
109 status
110 );
111 }
112
113 return outputs;
114 }
115
116 @Override
117 protected void generate(JavaAs3Input input, JavaAs3Output output) throws IOException, TemplateException {
118 Map<String, Object> bindings = getBindings(input, output);
119
120 // Write in memory (step 1).
121 PublicByteArrayOutputStream pbaos = new PublicByteArrayOutputStream(8192);
122 output.getTemplate().execute(bindings, new PrintWriter(new OutputStreamWriter(pbaos, "UTF-8")));
123
124 // If no exceptions were raised, write to file (step 2).
125 OutputStream stream = null;
126 try {
127 stream = output.openStream();
128 stream.write(pbaos.getBytes(), 0, pbaos.size());
129 } finally {
130 if (stream != null)
131 stream.close();
132 }
133 }
134
135 protected Map<String, Object> getBindings(JavaAs3Input input, JavaAs3Output output) {
136 Map<String, Object> bindings = new HashMap<String, Object>();
137 bindings.put("gVersion", Generator.VERSION);
138 bindings.put("jClass", input.getJavaType());
139 bindings.put("fAttributes", input.getAttributes());
140 return bindings;
141 }
142
143 protected TemplateUri[] getTemplateUris(JavaType javaType) {
144 return getConfig().getTemplateUris(getKind(javaType.getType()), javaType.getClass());
145 }
146
147 protected File getOutputDir(JavaAs3Input input, GroovyTemplate template) {
148 return (template.isBase() ? getConfig().getBaseOutputDir(input) : getConfig().getOutputDir(input));
149 }
150
151 protected abstract File getOutputFile(JavaAs3Input input, GroovyTemplate template, File outputDir);
152
153 protected String getOutputFileSuffix(JavaAs3Input input, GroovyTemplate template) {
154 return template.isBase() ? GENERATED_BASE_SUFFIX : "";
155 }
156
157 protected boolean isOutdated(JavaAs3Input input, GroovyTemplate template, File outputFile, boolean hasBaseTemplate) {
158 if (!outputFile.exists())
159 return true;
160 if (outputFile.lastModified() > System.currentTimeMillis()) {
161 getListener().warn(
162 outputFile.getAbsolutePath() +
163 " has a last modified time in the future: " +
164 DateFormat.getInstance().format(new Date(outputFile.lastModified()))
165 );
166 }
167 if (!template.isBase() && hasBaseTemplate)
168 return false;
169 return input.getFile().lastModified() > outputFile.lastModified();
170 }
171
172 protected String getOutputStatus(JavaAs3Input input, GroovyTemplate template, File outputFile, boolean hasBaseTemplate) {
173 if (!outputFile.exists())
174 return Listener.MSG_FILE_NOT_EXISTS;
175 if (!template.isBase() && hasBaseTemplate)
176 return Listener.MSG_FILE_EXISTS_NO_OVER;
177 if (input.getFile().lastModified() > outputFile.lastModified())
178 return Listener.MSG_FILE_OUTDATED;
179 return Listener.MSG_FILE_UPTODATE;
180 }
181
182 @Override
183 public ClientType getClientType(Type type, Class<?> declaringClass, ParameterizedType[] declaringTypes, boolean property) {
184 Class<?> clazz = ClassUtil.classOfType(type);
185
186 ClientType clientType = getConfig().getAs3TypeFactory().getClientType(type, declaringClass, declaringTypes, property);
187 if (clientType == null)
188 clientType = getConfig().getAs3TypeFactory().getAs3Type(clazz);
189
190 if (getConfig().getTranslators().isEmpty() || clazz.getPackage() == null)
191 return clientType;
192
193 String packageName = clazz.getPackage().getName();
194 PackageTranslator translator = PackageTranslator.forPackage(getConfig().getTranslators(), packageName);
195
196 if (translator != null)
197 clientType = clientType.translatePackage(translator);
198
199 return clientType;
200 }
201
202 @Override
203 public ClientType getAs3Type(Class<?> clazz) {
204 ClientType clientType = getConfig().getAs3TypeFactory().getAs3Type(clazz);
205 if (getConfig().getTranslators().isEmpty() || clazz.getPackage() == null)
206 return clientType;
207
208 String packageName = clazz.getPackage().getName();
209 PackageTranslator translator = PackageTranslator.forPackage(getConfig().getTranslators(), packageName);
210
211 if (translator != null)
212 clientType = clientType.translatePackage(translator);
213
214 return clientType;
215 }
216
217 @Override
218 public JavaImport getJavaImport(Class<?> clazz) {
219 JavaImport javaImport = javaImports.get(clazz.getName());
220 if (javaImport == null) {
221 URL url = ClassUtil.findResource(clazz);
222 javaImport = new JavaImport(this, clazz, url, false);
223 javaImports.put(clazz.getName(), javaImport);
224 }
225 return javaImport;
226 }
227
228 @Override
229 public Set<JavaImport> getJavaImports(ClientType clientType, boolean property) {
230 Set<JavaImport> imports = new HashSet<JavaImport>();
231
232 for (String className : clientType.getImports()) {
233 try {
234 JavaImport javaImport = property ? javaPropertyImports.get(className) : javaImports.get(className);
235 if (javaImport == null) {
236 javaImport = new ClientImport(this, className, property);
237 if (property)
238 javaPropertyImports.put(className, javaImport);
239 else
240 javaImports.put(className, javaImport);
241 }
242 imports.add(javaImport);
243 }
244 catch (Exception e) {
245 throw new RuntimeException("Could not get imports for type " + className);
246 }
247 }
248 return imports;
249 }
250
251 @Override
252 public JavaType getJavaType(Class<?> clazz) {
253 JavaType javaType = javaTypes.get(clazz);
254 if (javaType == null && getConfig().isGenerated(clazz)) {
255 URL url = ClassUtil.findResource(clazz);
256 Kind kind = getKind(clazz);
257 switch (kind) {
258 case ENUM:
259 javaType = new JavaEnum(this, clazz, url);
260 break;
261 case REMOTE_DESTINATION:
262 if (getConfig().getRemoteDestinationFactory() != null)
263 javaType = getConfig().getRemoteDestinationFactory().newRemoteDestination(this, clazz, url);
264 else
265 throw new RuntimeException("Remote destination could not be handled for " + clazz);
266 break;
267 case INTERFACE:
268 javaType = new JavaInterface(this, clazz, url);
269 break;
270 case ENTITY:
271 javaType = getConfig().getEntityFactory().newEntity(this, clazz, url);
272 break;
273 case BEAN:
274 javaType = getConfig().getEntityFactory().newBean(this, clazz, url);
275 break;
276 default:
277 throw new RuntimeException("Uknown class kind: " + kind);
278 }
279 javaTypes.put(clazz, javaType);
280 }
281 return javaType;
282 }
283
284 @Override
285 public Kind getKind(Class<?> clazz) {
286 if (clazz.isEnum() || Enum.class.getName().equals(clazz.getName()))
287 return Kind.ENUM;
288 if (getConfig().getRemoteDestinationFactory() != null) {
289 if (getConfig().getRemoteDestinationFactory().isRemoteDestination(clazz))
290 return Kind.REMOTE_DESTINATION;
291 }
292 if (clazz.isInterface())
293 return Kind.INTERFACE;
294 if (getConfig().getEntityFactory().isEntity(clazz))
295 return Kind.ENTITY;
296 return Kind.BEAN;
297 }
298
299 protected GenerationType getGenerationType(Class<?> clazz) {
300 return getGenerationType(getKind(clazz), clazz);
301 }
302 @Override
303 public GenerationType getGenerationType(Kind kind, Class<?> clazz) {
304 if (!getConfig().isGenerated(clazz))
305 return GenerationType.NOT_GENERATED;
306 TemplateUri[] uris = getConfig().getTemplateUris(kind, clazz);
307 if (uris == null || uris.length == 0)
308 return GenerationType.NOT_GENERATED;
309 return uris.length == 1 ? GenerationType.GENERATED_SINGLE : GenerationType.GENERATED_WITH_BASE;
310 }
311
312 @Override
313 public List<JavaInterface> getJavaTypeInterfaces(Class<?> clazz) {
314 List<JavaInterface> interfazes = new ArrayList<JavaInterface>();
315 for (Class<?> interfaze : clazz.getInterfaces()) {
316 if (getConfig().isGenerated(interfaze)) {
317 JavaType javaType = getJavaType(interfaze);
318 if (javaType instanceof JavaRemoteDestination)
319 javaType = ((JavaRemoteDestination)javaType).convertToJavaInterface();
320 interfazes.add((JavaInterface)javaType);
321 }
322 }
323 return interfazes;
324 }
325
326 @Override
327 public JavaType getJavaTypeSuperclass(Class<?> clazz) {
328 Class<?> superclass = clazz.getSuperclass();
329 if (superclass != null && getConfig().isGenerated(superclass))
330 return getJavaType(superclass);
331 return null;
332 }
333
334 @Override
335 public boolean isId(JavaFieldProperty fieldProperty) {
336 return getConfig().getEntityFactory().isId(fieldProperty);
337 }
338
339 @Override
340 public boolean isUid(JavaProperty property) {
341 return getConfig().getUid() == null
342 ? "uid".equals(property.getName())
343 : getConfig().getUid().equals(property.getName());
344 }
345
346 @Override
347 public boolean isVersion(JavaProperty property) {
348 return getConfig().getEntityFactory().isVersion(property);
349 }
350
351 @Override
352 public boolean isLazy(JavaProperty property) {
353 return getConfig().getEntityFactory().isLazy(property);
354 }
355
356 static class PublicByteArrayOutputStream extends ByteArrayOutputStream {
357 public PublicByteArrayOutputStream() {
358 }
359 public PublicByteArrayOutputStream(int size) {
360 super(size);
361 }
362 public byte[] getBytes() {
363 return buf; // no copy...
364 }
365 }
366 }