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 021package org.granite.generator.as3; 022 023import java.io.ByteArrayOutputStream; 024import java.io.File; 025import java.io.IOException; 026import java.io.OutputStream; 027import java.io.OutputStreamWriter; 028import java.io.PrintWriter; 029import java.lang.reflect.ParameterizedType; 030import java.lang.reflect.Type; 031import java.net.URL; 032import java.text.DateFormat; 033import java.util.ArrayList; 034import java.util.Date; 035import java.util.HashMap; 036import java.util.HashSet; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040 041import org.granite.generator.Generator; 042import org.granite.generator.Input; 043import org.granite.generator.Listener; 044import org.granite.generator.TemplateUri; 045import org.granite.generator.as3.reflect.ClientImport; 046import org.granite.generator.as3.reflect.JavaAbstractType.GenerationType; 047import org.granite.generator.as3.reflect.JavaEnum; 048import org.granite.generator.as3.reflect.JavaFieldProperty; 049import org.granite.generator.as3.reflect.JavaImport; 050import org.granite.generator.as3.reflect.JavaInterface; 051import org.granite.generator.as3.reflect.JavaProperty; 052import org.granite.generator.as3.reflect.JavaRemoteDestination; 053import org.granite.generator.as3.reflect.JavaType; 054import org.granite.generator.as3.reflect.JavaType.Kind; 055import org.granite.generator.as3.reflect.JavaTypeFactory; 056import org.granite.generator.exception.TemplateException; 057import org.granite.generator.exception.TemplateUriException; 058import org.granite.generator.gsp.AbstractGroovyTransformer; 059import org.granite.generator.gsp.GroovyTemplate; 060import org.granite.util.ClassUtil; 061 062/** 063 * @author Franck WOLFF 064 */ 065public 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, PropertyType propertyType) { 184 Class<?> clazz = ClassUtil.classOfType(type); 185 186 ClientType clientType = getConfig().getAs3TypeFactory().getClientType(type, declaringClass, declaringTypes, propertyType); 187 if (clientType == null) 188 clientType = getConfig().getAs3TypeFactory().getAs3Type(clazz); 189 190 clientType = clientType.translatePackages(getConfig().getTranslators()); 191 192 return clientType; 193 } 194 195 @Override 196 public ClientType getAs3Type(Class<?> clazz) { 197 ClientType clientType = getConfig().getAs3TypeFactory().getAs3Type(clazz); 198 if (getConfig().getTranslators().isEmpty() || clazz.getPackage() == null) 199 return clientType; 200 201 String packageName = clazz.getPackage().getName(); 202 PackageTranslator translator = PackageTranslator.forPackage(getConfig().getTranslators(), packageName); 203 204 if (translator != null) 205 clientType = clientType.translatePackage(translator); 206 207 return clientType; 208 } 209 210 @Override 211 public JavaImport getJavaImport(Class<?> clazz) { 212 JavaImport javaImport = javaImports.get(clazz.getName()); 213 if (javaImport == null) { 214 URL url = ClassUtil.findResource(clazz); 215 javaImport = new JavaImport(this, clazz, url, PropertyType.SIMPLE); 216 javaImports.put(clazz.getName(), javaImport); 217 } 218 return javaImport; 219 } 220 221 @Override 222 public Set<JavaImport> getJavaImports(ClientType clientType, boolean property) { 223 Set<JavaImport> imports = new HashSet<JavaImport>(); 224 225 for (String className : clientType.getImports()) { 226 try { 227 JavaImport javaImport = property ? javaPropertyImports.get(className) : javaImports.get(className); 228 if (javaImport == null) { 229 javaImport = new ClientImport(this, className, property); 230 if (property) 231 javaPropertyImports.put(className, javaImport); 232 else 233 javaImports.put(className, javaImport); 234 } 235 imports.add(javaImport); 236 } 237 catch (Exception e) { 238 throw new RuntimeException("Could not get imports for type " + className); 239 } 240 } 241 return imports; 242 } 243 244 @Override 245 public JavaType getJavaType(Class<?> clazz) { 246 JavaType javaType = javaTypes.get(clazz); 247 if (javaType == null && getConfig().isGenerated(clazz)) { 248 URL url = ClassUtil.findResource(clazz); 249 Kind kind = getKind(clazz); 250 switch (kind) { 251 case ENUM: 252 javaType = new JavaEnum(this, clazz, url); 253 break; 254 case REMOTE_DESTINATION: 255 if (getConfig().getRemoteDestinationFactory() != null) 256 javaType = getConfig().getRemoteDestinationFactory().newRemoteDestination(this, clazz, url); 257 else 258 throw new RuntimeException("Remote destination could not be handled for " + clazz); 259 break; 260 case INTERFACE: 261 javaType = new JavaInterface(this, clazz, url); 262 break; 263 case ENTITY: 264 javaType = getConfig().getEntityFactory().newEntity(this, clazz, url); 265 break; 266 case BEAN: 267 javaType = getConfig().getEntityFactory().newBean(this, clazz, url); 268 break; 269 default: 270 throw new RuntimeException("Uknown class kind: " + kind); 271 } 272 javaTypes.put(clazz, javaType); 273 } 274 return javaType; 275 } 276 277 @Override 278 public Kind getKind(Class<?> clazz) { 279 if (clazz.isEnum() || Enum.class.getName().equals(clazz.getName())) 280 return Kind.ENUM; 281 if (getConfig().getRemoteDestinationFactory() != null) { 282 if (getConfig().getRemoteDestinationFactory().isRemoteDestination(clazz)) 283 return Kind.REMOTE_DESTINATION; 284 } 285 if (clazz.isInterface()) 286 return Kind.INTERFACE; 287 if (getConfig().getEntityFactory().isEntity(clazz)) 288 return Kind.ENTITY; 289 return Kind.BEAN; 290 } 291 292 protected GenerationType getGenerationType(Class<?> clazz) { 293 return getGenerationType(getKind(clazz), clazz); 294 } 295 @Override 296 public GenerationType getGenerationType(Kind kind, Class<?> clazz) { 297 if (!getConfig().isGenerated(clazz)) 298 return GenerationType.NOT_GENERATED; 299 TemplateUri[] uris = getConfig().getTemplateUris(kind, clazz); 300 if (uris == null || uris.length == 0) 301 return GenerationType.NOT_GENERATED; 302 return uris.length == 1 ? GenerationType.GENERATED_SINGLE : GenerationType.GENERATED_WITH_BASE; 303 } 304 305 @Override 306 public List<JavaInterface> getJavaTypeInterfaces(Class<?> clazz) { 307 List<JavaInterface> interfazes = new ArrayList<JavaInterface>(); 308 for (Class<?> interfaze : clazz.getInterfaces()) { 309 if (getConfig().isGenerated(interfaze)) { 310 JavaType javaType = getJavaType(interfaze); 311 if (javaType instanceof JavaRemoteDestination) 312 javaType = ((JavaRemoteDestination)javaType).convertToJavaInterface(); 313 interfazes.add((JavaInterface)javaType); 314 } 315 } 316 return interfazes; 317 } 318 319 @Override 320 public JavaType getJavaTypeSuperclass(Class<?> clazz) { 321 Class<?> superclass = clazz.getSuperclass(); 322 if (superclass != null && getConfig().isGenerated(superclass)) 323 return getJavaType(superclass); 324 return null; 325 } 326 327 @Override 328 public boolean isId(JavaFieldProperty fieldProperty) { 329 return getConfig().getEntityFactory().isId(fieldProperty); 330 } 331 332 @Override 333 public boolean isUid(JavaProperty property) { 334 return getConfig().getUid() == null 335 ? "uid".equals(property.getName()) 336 : getConfig().getUid().equals(property.getName()); 337 } 338 339 @Override 340 public boolean isVersion(JavaProperty property) { 341 return getConfig().getEntityFactory().isVersion(property); 342 } 343 344 @Override 345 public boolean isLazy(JavaProperty property) { 346 return getConfig().getEntityFactory().isLazy(property); 347 } 348 349 static class PublicByteArrayOutputStream extends ByteArrayOutputStream { 350 public PublicByteArrayOutputStream() { 351 } 352 public PublicByteArrayOutputStream(int size) { 353 super(size); 354 } 355 public byte[] getBytes() { 356 return buf; // no copy... 357 } 358 } 359}