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 }