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 023package org.granite.generator.as3; 024 025import java.io.ByteArrayOutputStream; 026import java.io.File; 027import java.io.IOException; 028import java.io.OutputStream; 029import java.io.OutputStreamWriter; 030import java.io.PrintWriter; 031import java.lang.reflect.ParameterizedType; 032import java.lang.reflect.Type; 033import java.net.URL; 034import java.text.DateFormat; 035import java.util.ArrayList; 036import java.util.Date; 037import java.util.HashMap; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Map; 041import java.util.Set; 042 043import org.granite.generator.Generator; 044import org.granite.generator.Input; 045import org.granite.generator.Listener; 046import org.granite.generator.TemplateUri; 047import org.granite.generator.as3.reflect.ClientImport; 048import org.granite.generator.as3.reflect.JavaAbstractType.GenerationType; 049import org.granite.generator.as3.reflect.JavaEnum; 050import org.granite.generator.as3.reflect.JavaFieldProperty; 051import org.granite.generator.as3.reflect.JavaImport; 052import org.granite.generator.as3.reflect.JavaInterface; 053import org.granite.generator.as3.reflect.JavaProperty; 054import org.granite.generator.as3.reflect.JavaRemoteDestination; 055import org.granite.generator.as3.reflect.JavaType; 056import org.granite.generator.as3.reflect.JavaType.Kind; 057import org.granite.generator.as3.reflect.JavaTypeFactory; 058import org.granite.generator.exception.TemplateException; 059import org.granite.generator.exception.TemplateUriException; 060import org.granite.generator.gsp.AbstractGroovyTransformer; 061import org.granite.generator.gsp.GroovyTemplate; 062import org.granite.util.ClassUtil; 063 064/** 065 * @author Franck WOLFF 066 */ 067public 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}