001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2007-2010 ADEQUATE SYSTEMS SARL
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.JavaBean;
042    import org.granite.generator.as3.reflect.JavaEnum;
043    import org.granite.generator.as3.reflect.JavaFieldProperty;
044    import org.granite.generator.as3.reflect.JavaImport;
045    import org.granite.generator.as3.reflect.JavaInterface;
046    import org.granite.generator.as3.reflect.JavaProperty;
047    import org.granite.generator.as3.reflect.JavaRemoteDestination;
048    import org.granite.generator.as3.reflect.JavaType;
049    import org.granite.generator.as3.reflect.JavaTypeFactory;
050    import org.granite.generator.as3.reflect.JavaAbstractType.GenerationType;
051    import org.granite.generator.as3.reflect.JavaType.Kind;
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            static class PublicByteArrayOutputStream extends ByteArrayOutputStream {
311                    public PublicByteArrayOutputStream() {
312                    }
313                    public PublicByteArrayOutputStream(int size) {
314                            super(size);
315                    }
316                    public byte[] getBytes() {
317                            return buf; // no copy...
318                    }
319            }
320    }