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.builder;
022    
023    import java.io.File;
024    import java.text.DateFormat;
025    import java.util.Date;
026    import java.util.HashSet;
027    import java.util.Map;
028    import java.util.Set;
029    
030    import org.eclipse.core.resources.IFile;
031    import org.eclipse.core.resources.IProject;
032    import org.eclipse.core.resources.IResource;
033    import org.eclipse.core.resources.IResourceDelta;
034    import org.eclipse.core.resources.IResourceDeltaVisitor;
035    import org.eclipse.core.resources.IResourceVisitor;
036    import org.eclipse.core.resources.IncrementalProjectBuilder;
037    import org.eclipse.core.runtime.CoreException;
038    import org.eclipse.core.runtime.IProgressMonitor;
039    import org.eclipse.core.runtime.NullProgressMonitor;
040    import org.granite.builder.properties.Gas3Source;
041    import org.granite.builder.properties.Gas3Transformer;
042    import org.granite.builder.properties.GranitePropertiesLoader;
043    import org.granite.builder.ui.AddNatureWizard;
044    import org.granite.builder.util.BuilderUtil;
045    import org.granite.builder.util.FlexConfigGenerator;
046    import org.granite.builder.util.JavaClassInfo;
047    import org.granite.builder.util.ProjectUtil;
048    import org.granite.generator.Generator;
049    import org.granite.generator.Output;
050    import org.granite.generator.Transformer;
051    import org.granite.generator.as3.JavaAs3Input;
052    import org.granite.generator.as3.JavaAs3Output;
053    
054    /**
055     * @author Franck WOLFF
056     */
057    public class GraniteBuilder extends IncrementalProjectBuilder {
058    
059        
060        public static final String JAVA_BUILDER_ID = "org.eclipse.jdt.core.javabuilder";
061        public static final String FLEX_BUILDER_ID = "com.adobe.flexbuilder.project.flexbuilder";
062        public static final String GRANITE_BUILDER_ID = "org.granite.builder.granitebuilder";
063    
064        private static final int PROGRESS_TOTAL = 100;
065    
066        private final Generator generator;
067        private final BuilderListener listener;
068        private BuilderConfiguration config;
069    
070        ///////////////////////////////////////////////////////////////////////////
071        // Constructor.
072    
073        public GraniteBuilder() {
074                    super();
075                    this.generator = new Generator();
076                    this.listener = new BuilderListener();
077            }
078    
079        ///////////////////////////////////////////////////////////////////////////
080        // Build.
081    
082            @SuppressWarnings("rawtypes")
083        @Override
084        protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
085            listener.title("Building project \"" + getProject().getName() + "\" (" + DateFormat.getInstance().format(new Date()) + ")...");
086            long t0 = System.currentTimeMillis();
087            
088            
089            GenerationResult result = null;
090            try {
091                    if (!GranitePropertiesLoader.exists(getProject())) {
092                            BuilderConsole.activate();
093                    AddNatureWizard.run(getProject());
094                            config = null;
095                            generator.clear();
096                    } else if (Boolean.TRUE.equals(args.get(GraniteRebuildJob.RESET_KEY)) || (config != null && config.isOutdated())) {
097                            config = null;
098                            generator.clear();
099                    }
100                    
101                    config = getConfig();
102                    config.resetClassLoader();
103                    config.getGroovyTemplateFactory().cleanOutdated();
104                    
105                    generator.setConfig(config);
106                    
107                    BuilderConsole.setDebugEnabled(config.getProperties().getGas3().isDebugEnabled());
108                            
109                    if (generator.isEmpty()) {
110                            for (Gas3Transformer gas3Transformer : config.getProperties().getGas3().getTransformers()) {
111                            try {
112                                    Transformer<?,?,?> transformer = BuilderUtil.newInstance(Transformer.class, gas3Transformer.getType(), config.getClassLoader());
113                                    transformer.setListener(listener);
114                                    generator.add(transformer);
115                            } catch (Exception e) {
116                                    listener.error("Could not load transformer: " + gas3Transformer.getType(), e);
117                                    
118                                    if (e instanceof CoreException)
119                                            throw (CoreException)e;
120                                    if (e.getCause() instanceof CoreException)
121                                            throw (CoreException)e.getCause();
122                                    throw new CoreException(ProjectUtil.createErrorStatus(
123                                            "Could not load transformer: " + gas3Transformer.getType(), null
124                                    ));
125                            }
126                            }
127                    }
128            
129                    
130                    if (monitor == null)
131                        monitor = new NullProgressMonitor();
132            
133                    try {
134                        if (kind == FULL_BUILD)
135                            result = fullBuild(monitor);
136                        else {
137                            IResourceDelta delta = getDelta(getProject());
138                            if (delta == null)
139                                    result = fullBuild(monitor);
140                            else
141                                    result = incrementalBuild(delta, monitor);
142                        }
143                    } catch (CoreException e) {
144                        throw e;
145                    } catch (Exception e) {
146                        throw new CoreException(ProjectUtil.createErrorStatus("Granite Build Failed", e));
147                    }
148                    
149                    boolean refreshFlexConfig = false;
150                    try {
151                            if (result.generateFlexConfig && config.getProperties().getGas3().isFlexConfig())
152                                    refreshFlexConfig = FlexConfigGenerator.generateFlexConfig(config, listener, getProject());
153                    }
154                    catch (Exception e) {
155                            listener.warn("Could not generate Flex Builder configuration", e);
156                    }
157            
158                    File projectDir = ProjectUtil.getProjectFile(getProject());
159                    for (File dir : result.dirsToRefresh) {
160                            StringBuilder relativePath = new StringBuilder();
161                            while (dir != null && !dir.equals(projectDir)) {
162                                    relativePath.insert(0, '/').insert(1, dir.getName());
163                                    dir = dir.getParentFile();
164                            }
165                            getProject().getFolder(relativePath.toString()).refreshLocal(IResource.DEPTH_INFINITE, monitor);
166                    }
167                    if (refreshFlexConfig)
168                            getProject().getFile(FlexConfigGenerator.FILE_NAME).refreshLocal(IResource.DEPTH_ZERO, monitor);
169            }
170            finally {
171                    long t1 = System.currentTimeMillis();
172                    
173                    if (result != null) {
174                            listener.title(
175                                    "Done (" + (result.affectedFiles > 0 ? result.affectedFiles + " affected files" : "nothing to do") +
176                                    " - " + (t1 - t0) + "ms)."
177                            );
178                    } else
179                            listener.title("Done (error) - " + (t1 - t0) + "ms).");
180                    
181                    listener.title("");
182            }
183            
184            return null;
185        }
186            
187            class GenerationResult {
188                    public int affectedFiles = 0;
189                    public Set<File> dirsToRefresh = new HashSet<File>();
190                    public boolean generateFlexConfig = false;
191            }
192    
193        ///////////////////////////////////////////////////////////////////////////
194        // Full Build.
195    
196        private GenerationResult fullBuild(final IProgressMonitor monitor) throws CoreException {
197            monitor.beginTask("Granite Full Build", PROGRESS_TOTAL);
198            FullBuildVisitor visitor = new FullBuildVisitor(monitor);
199            try {
200                getProject().accept(visitor);
201            } finally {
202                monitor.done();
203            }
204            return visitor.getResult();
205        }
206    
207        class FullBuildVisitor implements IResourceVisitor {
208    
209            private IProgressMonitor monitor;
210            private GenerationResult result = new GenerationResult();
211    
212            public FullBuildVisitor(IProgressMonitor monitor) {
213                this.monitor = monitor;
214            }
215    
216            public boolean visit(IResource resource) throws CoreException {
217                if (!resource.isAccessible() || resource.isPhantom())
218                    return false;
219    
220                Output<?>[] outputs = generate(resource, monitor);
221                if (outputs != null) {
222                    for (Output<?> output : outputs) {
223                            if (output.isOutdated()) {
224                                    result.affectedFiles++;
225                                    result.dirsToRefresh.add(((JavaAs3Output)output).getDir());
226                            }
227                    }
228                }
229                            result.generateFlexConfig = true;
230    
231                return true;
232            }
233    
234                    public GenerationResult getResult() {
235                            return result;
236                    }
237        }
238    
239        ///////////////////////////////////////////////////////////////////////////
240        // Incremental Build.
241    
242        private GenerationResult incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException {
243            monitor.beginTask("Granite Incremental Build", PROGRESS_TOTAL);
244            IncrementalBuildVisitor visitor = new IncrementalBuildVisitor(monitor);
245            try {
246                delta.accept(visitor);
247            } finally {
248                monitor.done();
249            }
250            return visitor.getResult();
251        }
252    
253        class IncrementalBuildVisitor implements IResourceDeltaVisitor {
254    
255            private IProgressMonitor monitor;
256            private GenerationResult result = new GenerationResult();
257    
258            public IncrementalBuildVisitor(IProgressMonitor monitor) {
259                this.monitor = monitor;
260            }
261    
262            public boolean visit(IResourceDelta delta) throws CoreException {
263                IResource resource = delta.getResource();
264    
265                Output<?>[] outputs = null;
266                
267                switch (delta.getKind()) {
268                case IResourceDelta.ADDED:
269                    if (!resource.isAccessible() || resource.isPhantom())
270                        return false;
271                    outputs = generate(resource, monitor);
272                    if (!result.generateFlexConfig)
273                            result.generateFlexConfig = "as".equals(resource.getFileExtension());
274                    break;
275                case IResourceDelta.REMOVED:
276                    if (!result.generateFlexConfig)
277                            result.generateFlexConfig = "as".equals(resource.getFileExtension());
278                    break;
279                case IResourceDelta.CHANGED:
280                    if (!resource.isAccessible() || resource.isPhantom())
281                        return false;
282                    outputs = generate(resource, monitor);
283                    break;
284                }
285                
286                if (outputs != null) {
287                    for (Output<?> output : outputs) {
288                            if (output.isOutdated()) {
289                                    result.affectedFiles++;
290                                    result.dirsToRefresh.add(((JavaAs3Output)output).getDir());
291                                    result.generateFlexConfig = true;
292                            }
293                    }
294                }
295    
296                return true;
297            }
298    
299                    public GenerationResult getResult() {
300                            return result;
301                    }
302        }
303        
304        private BuilderConfiguration getConfig() {
305            if (config == null || config.isOutdated())
306                    config = new BuilderConfiguration(listener, getProject());
307            return config;
308        }
309    
310        private Output<?>[] generate(IResource resource, IProgressMonitor monitor) /*throws CoreException*/ {
311            if (resource instanceof IFile && "class".equals(resource.getFileExtension())) {
312                IFile file = (IFile)resource;
313                            
314                try {
315                        JavaClassInfo info = ProjectUtil.getJavaClassInfo(config.getJavaProject(), (IFile)resource);
316                        if (info == null) {
317                            listener.warn("Could not get class informations for: " + resource.toString());
318                            return null;
319                        }
320                        
321                        Gas3Source source = config.getProperties().getGas3().getMatchingSource(
322                            info.getSourceFolderPath(),
323                            info.getSourceFilePath()
324                        );
325                        
326                        if (source != null) {
327                                monitor.subTask("Generating AS3 code for: " + file.getProjectRelativePath().toString());
328                                try {
329                                    Class<?> clazz = config.getClassLoader().loadClass(info.getClassName());
330                                    if (!clazz.isAnonymousClass() && config.isGenerated(clazz)) {
331                                            JavaAs3Input input = new BuilderJavaAs3Input(clazz, info.getClassFile(), source);
332                                            return generator.generate(input);
333                                    }
334                                } finally {
335                                    monitor.worked(1);
336                                }
337                        }
338                } catch (Throwable t) {
339                    listener.error("", t);
340                    
341    //              if (t instanceof CoreException)
342    //                      throw (CoreException)t;
343    //              if (t.getCause() instanceof CoreException)
344    //                      throw (CoreException)t.getCause();
345    //                throw new CoreException(ProjectUtil.createErrorStatus(
346    //                    "Could not generate AS3 bean for: " + file.getProjectRelativePath() + " - " + t.toString(), null
347    //                ));
348                }
349            }
350            
351            return null;
352        }
353    }