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.net.URL;
030 import java.text.DateFormat;
031 import java.util.ArrayList;
032 import java.util.Date;
033 import java.util.HashMap;
034 import java.util.List;
035 import java.util.Map;
036
037 import org.granite.generator.Generator;
038 import org.granite.generator.Input;
039 import org.granite.generator.Listener;
040 import org.granite.generator.TemplateUri;
041 import org.granite.generator.as3.reflect.JavaAbstractType.GenerationType;
042 import org.granite.generator.as3.reflect.JavaBean;
043 import org.granite.generator.as3.reflect.JavaEnum;
044 import org.granite.generator.as3.reflect.JavaFieldProperty;
045 import org.granite.generator.as3.reflect.JavaImport;
046 import org.granite.generator.as3.reflect.JavaInterface;
047 import org.granite.generator.as3.reflect.JavaProperty;
048 import org.granite.generator.as3.reflect.JavaRemoteDestination;
049 import org.granite.generator.as3.reflect.JavaType;
050 import org.granite.generator.as3.reflect.JavaType.Kind;
051 import org.granite.generator.as3.reflect.JavaTypeFactory;
052 import org.granite.generator.exception.TemplateException;
053 import org.granite.generator.exception.TemplateUriException;
054 import org.granite.generator.gsp.AbstractGroovyTransformer;
055 import org.granite.generator.gsp.GroovyTemplate;
056 import org.granite.util.ClassUtil;
057
058 /**
059 * @author Franck WOLFF
060 */
061 public class JavaAs3GroovyTransformer
062 extends AbstractGroovyTransformer<JavaAs3Input, JavaAs3Output, JavaAs3GroovyConfiguration>
063 implements JavaTypeFactory {
064
065 protected final Map<Class<?>, JavaType> javaTypes = new HashMap<Class<?>, JavaType>();
066 protected final Map<Class<?>, JavaImport> javaImports = new HashMap<Class<?>, JavaImport>();
067
068 public JavaAs3GroovyTransformer() {
069 }
070
071 public JavaAs3GroovyTransformer(JavaAs3GroovyConfiguration config, Listener listener) {
072 super(config, listener);
073 }
074
075 @Override
076 public boolean accept(Input<?> input) {
077 return (input instanceof JavaAs3Input);
078 }
079
080 @Override
081 protected JavaAs3Output[] getOutputs(JavaAs3Input input) throws IOException, TemplateUriException {
082 JavaType javaType = getJavaType(input.getType());
083 input.setJavaType(javaType);
084 TemplateUri[] templateUris = getTemplateUris(javaType);
085 boolean hasBaseTemplate = templateUris.length > 1;
086
087 JavaAs3Output[] outputs = new JavaAs3Output[templateUris.length];
088
089 for (int i = 0; i < templateUris.length; i++) {
090 GroovyTemplate template = getTemplate(templateUris[i]);
091 File dir = getOutputDir(input, template);
092 File file = getOutputFile(input, template, dir);
093 boolean outdated = isOutdated(input, template, file, hasBaseTemplate);
094 String status = getOutputStatus(input, template, file, hasBaseTemplate);
095
096 outputs[i] = new JavaAs3Output(
097 javaType,
098 template,
099 dir,
100 file,
101 outdated,
102 status
103 );
104 }
105
106 return outputs;
107 }
108
109 @Override
110 protected void generate(JavaAs3Input input, JavaAs3Output output) throws IOException, TemplateException {
111 Map<String, Object> bindings = getBindings(input, output);
112
113 // Write in memory (step 1).
114 PublicByteArrayOutputStream pbaos = new PublicByteArrayOutputStream(8192);
115 output.getTemplate().execute(bindings, new PrintWriter(new OutputStreamWriter(pbaos)));
116
117 // If no exceptions were raised, write to file (step 2).
118 OutputStream stream = null;
119 try {
120 stream = output.openStream();
121 stream.write(pbaos.getBytes(), 0, pbaos.size());
122 } finally {
123 if (stream != null)
124 stream.close();
125 }
126 }
127
128 protected Map<String, Object> getBindings(JavaAs3Input input, JavaAs3Output output) {
129 Map<String, Object> bindings = new HashMap<String, Object>();
130 bindings.put("gVersion", Generator.VERSION);
131 bindings.put("jClass", input.getJavaType());
132 return bindings;
133 }
134
135 protected TemplateUri[] getTemplateUris(JavaType javaType) {
136 return getConfig().getTemplateUris(getKind(javaType.getType()), javaType.getClass());
137 }
138
139 protected File getOutputDir(JavaAs3Input input, GroovyTemplate template) {
140 return (template.isBase() ? getConfig().getBaseOutputDir(input) : getConfig().getOutputDir(input));
141 }
142
143 protected File getOutputFile(JavaAs3Input input, GroovyTemplate template, File outputDir) {
144 As3Type as3Type = input.getJavaType().getAs3Type();
145
146 StringBuilder sb = new StringBuilder()
147 .append(outputDir.getAbsolutePath())
148 .append(File.separatorChar)
149 .append(as3Type.getQualifiedName().replace('.', File.separatorChar))
150 .append(getOutputFileSuffix(input, template))
151 .append(".as");
152
153 return new File(sb.toString());
154 }
155
156 protected String getOutputFileSuffix(JavaAs3Input input, GroovyTemplate template) {
157 return template.isBase() ? "Base" : "";
158 }
159
160 protected boolean isOutdated(JavaAs3Input input, GroovyTemplate template, File outputFile, boolean hasBaseTemplate) {
161 if (!outputFile.exists())
162 return true;
163 if (outputFile.lastModified() > System.currentTimeMillis()) {
164 getListener().warn(
165 outputFile.getAbsolutePath() +
166 " has a last modified time in the future: " +
167 DateFormat.getInstance().format(new Date(outputFile.lastModified()))
168 );
169 }
170 if (!template.isBase() && hasBaseTemplate)
171 return false;
172 return input.getFile().lastModified() > outputFile.lastModified();
173 }
174
175 protected String getOutputStatus(JavaAs3Input input, GroovyTemplate template, File outputFile, boolean hasBaseTemplate) {
176 if (!outputFile.exists())
177 return Listener.MSG_FILE_NOT_EXISTS;
178 if (!template.isBase() && hasBaseTemplate)
179 return Listener.MSG_FILE_EXISTS_NO_OVER;
180 if (input.getFile().lastModified() > outputFile.lastModified())
181 return Listener.MSG_FILE_OUTDATED;
182 return Listener.MSG_FILE_UPTODATE;
183 }
184
185 public As3Type getAs3Type(Class<?> clazz) {
186 As3Type as3Type = getConfig().getAs3TypeFactory().getAs3Type(clazz);
187 if (getConfig().getTranslators().isEmpty() || clazz.getPackage() == null)
188 return as3Type;
189
190 PackageTranslator translator = null;
191
192 String packageName = clazz.getPackage().getName();
193 int weight = 0;
194 for (PackageTranslator t : getConfig().getTranslators()) {
195 int w = t.match(packageName);
196 if (w > weight) {
197 weight = w;
198 translator = t;
199 }
200 }
201
202 if (translator != null)
203 as3Type = new As3Type(translator.translate(packageName), as3Type.getName());
204
205 return as3Type;
206 }
207
208 public JavaImport getJavaImport(Class<?> clazz) {
209 JavaImport javaImport = javaImports.get(clazz);
210 if (javaImport == null) {
211 URL url = ClassUtil.findResource(clazz);
212 javaImport = new JavaImport(this, clazz, url);
213 javaImports.put(clazz, javaImport);
214 }
215 return javaImport;
216 }
217
218 public JavaType getJavaType(Class<?> clazz) {
219 JavaType javaType = javaTypes.get(clazz);
220 if (javaType == null && getConfig().isGenerated(clazz)) {
221 URL url = ClassUtil.findResource(clazz);
222 Kind kind = getKind(clazz);
223 switch (kind) {
224 case ENUM:
225 javaType = new JavaEnum(this, clazz, url);
226 break;
227 case REMOTE_DESTINATION:
228 if (getConfig().getRemoteDestinationFactory() != null)
229 javaType = getConfig().getRemoteDestinationFactory().newRemoteDestination(this, clazz, url);
230 else
231 throw new RuntimeException("Remote destination could not be handled for " + clazz);
232 break;
233 case INTERFACE:
234 javaType = new JavaInterface(this, clazz, url);
235 break;
236 case ENTITY:
237 javaType = getConfig().getEntityFactory().newEntity(this, clazz, url);
238 break;
239 case BEAN:
240 javaType = new JavaBean(this, clazz, url);
241 break;
242 default:
243 throw new RuntimeException("Uknown class kind: " + kind);
244 }
245 javaTypes.put(clazz, javaType);
246 }
247 return javaType;
248 }
249
250 public Kind getKind(Class<?> clazz) {
251 if (clazz.isEnum() || Enum.class.getName().equals(clazz.getName()))
252 return Kind.ENUM;
253 if (getConfig().getRemoteDestinationFactory() != null) {
254 if (getConfig().getRemoteDestinationFactory().isRemoteDestination(clazz))
255 return Kind.REMOTE_DESTINATION;
256 }
257 if (clazz.isInterface())
258 return Kind.INTERFACE;
259 if (getConfig().getEntityFactory().isEntity(clazz))
260 return Kind.ENTITY;
261 return Kind.BEAN;
262 }
263
264 protected GenerationType getGenerationType(Class<?> clazz) {
265 return getGenerationType(getKind(clazz), clazz);
266 }
267 public GenerationType getGenerationType(Kind kind, Class<?> clazz) {
268 if (!getConfig().isGenerated(clazz))
269 return GenerationType.NOT_GENERATED;
270 TemplateUri[] uris = getConfig().getTemplateUris(kind, clazz);
271 if (uris == null || uris.length == 0)
272 return GenerationType.NOT_GENERATED;
273 return uris.length == 1 ? GenerationType.GENERATED_SINGLE : GenerationType.GENERATED_WITH_BASE;
274 }
275
276 public List<JavaInterface> getJavaTypeInterfaces(Class<?> clazz) {
277 List<JavaInterface> interfazes = new ArrayList<JavaInterface>();
278 for (Class<?> interfaze : clazz.getInterfaces()) {
279 if (getConfig().isGenerated(interfaze)) {
280 JavaType javaType = getJavaType(interfaze);
281 if (javaType instanceof JavaRemoteDestination)
282 javaType = ((JavaRemoteDestination)javaType).convertToJavaInterface();
283 interfazes.add((JavaInterface)javaType);
284 }
285 }
286 return interfazes;
287 }
288
289 public JavaType getJavaTypeSuperclass(Class<?> clazz) {
290 Class<?> superclass = clazz.getSuperclass();
291 if (superclass != null && getConfig().isGenerated(superclass))
292 return getJavaType(superclass);
293 return null;
294 }
295
296 public boolean isId(JavaFieldProperty fieldProperty) {
297 return getConfig().getEntityFactory().isId(fieldProperty);
298 }
299
300 public boolean isUid(JavaProperty property) {
301 return getConfig().getUid() == null
302 ? "uid".equals(property.getName())
303 : getConfig().getUid().equals(property.getName());
304 }
305
306 public boolean isVersion(JavaProperty property) {
307 return getConfig().getEntityFactory().isVersion(property);
308 }
309
310 public boolean isLazy(JavaProperty property) {
311 return getConfig().getEntityFactory().isLazy(property);
312 }
313
314 static class PublicByteArrayOutputStream extends ByteArrayOutputStream {
315 public PublicByteArrayOutputStream() {
316 }
317 public PublicByteArrayOutputStream(int size) {
318 super(size);
319 }
320 public byte[] getBytes() {
321 return buf; // no copy...
322 }
323 }
324 }