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