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