cleanup: too verbose logging removed
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / make / Form2ByteCodeCompiler.java
blob4cd472bc52dc1cf948317b85405b67ad22e8edda
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.uiDesigner.make;
18 import com.intellij.compiler.PsiClassWriter;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.compiler.*;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.fileEditor.FileDocumentManager;
24 import com.intellij.openapi.fileTypes.StdFileTypes;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.module.ModuleUtil;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.roots.ProjectClasspathTraversing;
29 import com.intellij.openapi.roots.ProjectRootsTraversing;
30 import com.intellij.openapi.util.Computable;
31 import com.intellij.openapi.vfs.VfsUtil;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.psi.JavaPsiFacade;
34 import com.intellij.psi.PsiClass;
35 import com.intellij.psi.PsiFile;
36 import com.intellij.psi.search.GlobalSearchScope;
37 import com.intellij.uiDesigner.FormEditingUtil;
38 import com.intellij.uiDesigner.GuiDesignerConfiguration;
39 import com.intellij.uiDesigner.UIDesignerBundle;
40 import com.intellij.uiDesigner.compiler.AlienFormFileException;
41 import com.intellij.uiDesigner.compiler.AsmCodeGenerator;
42 import com.intellij.uiDesigner.compiler.FormErrorInfo;
43 import com.intellij.uiDesigner.compiler.Utils;
44 import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider;
45 import com.intellij.uiDesigner.lw.LwRootContainer;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
49 import java.io.DataInput;
50 import java.io.File;
51 import java.io.IOException;
52 import java.net.URL;
53 import java.net.URLClassLoader;
54 import java.util.ArrayList;
55 import java.util.HashMap;
56 import java.util.StringTokenizer;
58 public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler {
59 private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.make.Form2ByteCodeCompiler");
61 @NotNull
62 public String getDescription() {
63 return UIDesignerBundle.message("component.gui.designer.form.to.bytecode.compiler");
66 public boolean validateConfiguration(CompileScope scope) {
67 return true;
70 @NotNull
71 public static URLClassLoader createClassLoader(@NotNull final String classPath){
72 final ArrayList<URL> urls = new ArrayList<URL>();
73 for (StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); tokenizer.hasMoreTokens();) {
74 final String s = tokenizer.nextToken();
75 try {
76 urls.add(new File(s).toURI().toURL());
78 catch (Exception exc) {
79 throw new RuntimeException(exc);
82 return new URLClassLoader(urls.toArray(new URL[urls.size()]), null);
85 @NotNull
86 public ProcessingItem[] getProcessingItems(final CompileContext context) {
87 final Project project = context.getProject();
88 if (!GuiDesignerConfiguration.getInstance(project).INSTRUMENT_CLASSES) {
89 return ProcessingItem.EMPTY_ARRAY;
92 final ArrayList<ProcessingItem> items = new ArrayList<ProcessingItem>();
94 ApplicationManager.getApplication().runReadAction(new Runnable() {
95 public void run() {
96 final CompileScope scope = context.getCompileScope();
97 final CompileScope projectScope = context.getProjectCompileScope();
99 final VirtualFile[] formFiles = projectScope.getFiles(StdFileTypes.GUI_DESIGNER_FORM, true);
100 if (formFiles.length==0) return;
101 final CompilerManager compilerManager = CompilerManager.getInstance(project);
102 final BindingsCache bindingsCache = new BindingsCache(project);
104 final HashMap<Module, ArrayList<VirtualFile>> module2formFiles = sortByModules(project, formFiles);
106 try {
107 for (final Module module : module2formFiles.keySet()) {
108 final HashMap<String, VirtualFile> class2form = new HashMap<String, VirtualFile>();
110 final ArrayList<VirtualFile> list = module2formFiles.get(module);
111 for (final VirtualFile formFile : list) {
112 if (compilerManager.isExcludedFromCompilation(formFile)) {
113 continue;
116 final String classToBind;
117 try {
118 classToBind = bindingsCache.getBoundClassName(formFile);
120 catch (AlienFormFileException e) {
121 // ignore non-IDEA forms
122 continue;
124 catch (Exception e) {
125 addMessage(context, UIDesignerBundle.message("error.cannot.process.form.file", e), formFile, CompilerMessageCategory.ERROR);
126 continue;
129 if (classToBind == null) {
130 continue;
133 final VirtualFile classFile = findFile(context, classToBind, module);
134 if (classFile == null) {
135 if (scope.belongs(formFile.getUrl())) {
136 addMessage(context, UIDesignerBundle.message("error.class.to.bind.does.not.exist", classToBind), formFile,
137 CompilerMessageCategory.ERROR);
139 continue;
142 final VirtualFile alreadyProcessedForm = class2form.get(classToBind);
143 if (alreadyProcessedForm != null) {
144 if (belongsToCompileScope(context, formFile, classToBind)) {
145 addMessage(
146 context,
147 UIDesignerBundle.message("error.duplicate.bind",
148 classToBind, alreadyProcessedForm.getPresentableUrl()),
149 formFile, CompilerMessageCategory.ERROR);
151 continue;
153 class2form.put(classToBind, formFile);
155 final ProcessingItem item = new MyInstrumentationItem(classFile, formFile, classToBind);
156 items.add(item);
160 finally {
161 bindingsCache.close();
166 return items.toArray(new ProcessingItem[items.size()]);
169 private static boolean belongsToCompileScope(final CompileContext context, final VirtualFile formFile, final String classToBind) {
170 final CompileScope compileScope = context.getCompileScope();
171 if (compileScope.belongs(formFile.getUrl())) {
172 return true;
174 final VirtualFile sourceFile = findSourceFile(context, formFile, classToBind);
175 return sourceFile != null && compileScope.belongs(sourceFile.getUrl());
178 private static HashMap<Module, ArrayList<VirtualFile>> sortByModules(final Project project, final VirtualFile[] formFiles) {
179 final HashMap<Module, ArrayList<VirtualFile>> module2formFiles = new HashMap<Module,ArrayList<VirtualFile>>();
180 for (final VirtualFile formFile : formFiles) {
181 final Module module = ModuleUtil.findModuleForFile(formFile, project);
182 if (module != null) {
183 ArrayList<VirtualFile> list = module2formFiles.get(module);
184 if (list == null) {
185 list = new ArrayList<VirtualFile>();
186 module2formFiles.put(module, list);
188 list.add(formFile);
190 else {
191 // todo[anton] handle somehow
194 return module2formFiles;
197 private static HashMap<Module, ArrayList<MyInstrumentationItem>> sortByModules(final Project project, final ProcessingItem[] items) {
198 final HashMap<Module, ArrayList<MyInstrumentationItem>> module2formFiles = new HashMap<Module,ArrayList<MyInstrumentationItem>>();
199 for (ProcessingItem item1 : items) {
200 final MyInstrumentationItem item = (MyInstrumentationItem)item1;
201 final VirtualFile formFile = item.getFormFile();
203 final Module module = ModuleUtil.findModuleForFile(formFile, project);
204 if (module != null) {
205 ArrayList<MyInstrumentationItem> list = module2formFiles.get(module);
206 if (list == null) {
207 list = new ArrayList<MyInstrumentationItem>();
208 module2formFiles.put(module, list);
210 list.add(item);
212 else {
213 // todo[anton] handle somehow
216 return module2formFiles;
219 @Nullable
220 private static VirtualFile findFile(final CompileContext context, final String className, final Module module) {
221 /*for most cases (top-level classes) this will work*/
222 VirtualFile file = findFileByRelativePath(context, module, className.replace('.', '/') + ".class");
223 if (file == null) {
224 // getClassFileName() is much longer than simply conversion from dots into slashes, but works for inner classes
225 file = findFileByRelativePath(context, module, getClassFileName(className.replace('$', '.'), module) + ".class");
227 return file;
230 private static VirtualFile findFileByRelativePath(final CompileContext context, final Module module, final String relativepath) {
231 final VirtualFile output = context.getModuleOutputDirectory(module);
232 VirtualFile file = output != null? output.findFileByRelativePath(relativepath) : null;
233 if (file == null) {
234 final VirtualFile testsOutput = context.getModuleOutputDirectoryForTests(module);
235 if (testsOutput != null && !testsOutput.equals(output)) {
236 file = testsOutput.findFileByRelativePath(relativepath);
239 return file;
242 private static String getClassFileName(final String _className, final Module module) {
243 final PsiClass aClass = JavaPsiFacade.getInstance(module.getProject()).findClass(_className, GlobalSearchScope.moduleScope(module));
244 if (aClass == null) {
245 return _className.replace('.', '/');
248 PsiClass outerClass = aClass;
249 while (outerClass.getParent() instanceof PsiClass) {
250 outerClass = (PsiClass)outerClass.getParent();
253 final String outerQualifiedName = outerClass.getQualifiedName();
255 assert outerQualifiedName != null;
256 return outerQualifiedName.replace('.','/') + _className.substring(outerQualifiedName.length()).replace('.','$');
259 public ProcessingItem[] process(final CompileContext context, final ProcessingItem[] items) {
260 final ArrayList<ProcessingItem> compiledItems = new ArrayList<ProcessingItem>();
262 context.getProgressIndicator().pushState();
263 context.getProgressIndicator().setText(UIDesignerBundle.message("progress.compiling.ui.forms"));
265 final Project project = context.getProject();
266 final HashMap<Module, ArrayList<MyInstrumentationItem>> module2itemsList = sortByModules(project, items);
268 int formsProcessed = 0;
270 for (final Module module : module2itemsList.keySet()) {
271 final String classPath =
272 ProjectRootsTraversing.collectRoots(module, ProjectClasspathTraversing.FULL_CLASSPATH_RECURSIVE).getPathsString();
273 final ClassLoader loader = createClassLoader(classPath);
275 if (GuiDesignerConfiguration.getInstance(project).COPY_FORMS_RUNTIME_TO_OUTPUT) {
276 final String moduleOutputPath = CompilerPaths.getModuleOutputPath(module, false);
277 try {
278 if (moduleOutputPath != null) {
279 CopyResourcesUtil.copyFormsRuntime(moduleOutputPath, false);
281 final String testsOutputPath = CompilerPaths.getModuleOutputPath(module, true);
282 if (testsOutputPath != null && !testsOutputPath.equals(moduleOutputPath)) {
283 CopyResourcesUtil.copyFormsRuntime(testsOutputPath, false);
286 catch (IOException e) {
287 addMessage(
288 context,
289 UIDesignerBundle.message("error.cannot.copy.gui.designer.form.runtime", module.getName(), e.toString()),
290 null, CompilerMessageCategory.ERROR);
294 final ArrayList<MyInstrumentationItem> list = module2itemsList.get(module);
296 for (final MyInstrumentationItem item : list) {
297 //context.getProgressIndicator().setFraction((double)++formsProcessed / (double)items.length);
299 final VirtualFile formFile = item.getFormFile();
300 context.getProgressIndicator().setText2(formFile.getPresentableUrl());
302 final Document doc = ApplicationManager.getApplication().runReadAction(new Computable<Document>() {
303 public Document compute() {
304 if (!belongsToCompileScope(context, formFile, item.getClassToBindFQname())) {
305 return null;
307 return FileDocumentManager.getInstance().getDocument(formFile);
310 if (doc == null) {
311 continue; // does not belong to current scope
314 final LwRootContainer rootContainer;
315 try {
316 rootContainer = Utils.getRootContainer(doc.getText(), new CompiledClassPropertiesProvider(loader));
318 catch (Exception e) {
319 addMessage(context, UIDesignerBundle.message("error.cannot.process.form.file", e), formFile, CompilerMessageCategory.ERROR);
320 continue;
323 final File classFile = VfsUtil.virtualToIoFile(item.getFile());
324 LOG.assertTrue(classFile.exists(), classFile.getPath());
326 final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader,
327 new PsiNestedFormLoader(module), false,
328 new PsiClassWriter(module));
329 ApplicationManager.getApplication().runReadAction(new Runnable() {
330 public void run() {
331 codeGenerator.patchFile(classFile);
334 final FormErrorInfo[] errors = codeGenerator.getErrors();
335 final FormErrorInfo[] warnings = codeGenerator.getWarnings();
336 for (FormErrorInfo warning : warnings) {
337 addMessage(context, warning, formFile, CompilerMessageCategory.WARNING);
339 for (FormErrorInfo error : errors) {
340 addMessage(context, error, formFile, CompilerMessageCategory.ERROR);
342 if (errors.length == 0) {
343 compiledItems.add(item);
347 context.getProgressIndicator().popState();
349 return compiledItems.toArray(new ProcessingItem[compiledItems.size()]);
352 private static void addMessage(final CompileContext context,
353 final String s,
354 final VirtualFile formFile,
355 final CompilerMessageCategory severity) {
356 addMessage(context, new FormErrorInfo(null, s), formFile, severity);
359 private static void addMessage(final CompileContext context,
360 final FormErrorInfo e,
361 final VirtualFile formFile,
362 final CompilerMessageCategory severity) {
363 if (formFile != null) {
364 FormElementNavigatable navigatable = new FormElementNavigatable(context.getProject(), formFile, e.getComponentId());
365 context.addMessage(severity,
366 formFile.getPresentableUrl() + ": " + e.getErrorMessage(),
367 formFile.getUrl(), -1, -1, navigatable);
369 else {
370 context.addMessage(severity, e.getErrorMessage(), null, -1, -1);
374 public ValidityState createValidityState(final DataInput in) throws IOException {
375 return TimestampValidityState.load(in);
378 public static VirtualFile findSourceFile(final CompileContext context, final VirtualFile formFile, final String className) {
379 final Module module = context.getModuleByFile(formFile);
380 if (module == null) {
381 return null;
383 final PsiClass aClass = FormEditingUtil.findClassToBind(module, className);
384 if (aClass == null) {
385 return null;
388 final PsiFile containingFile = aClass.getContainingFile();
389 if (containingFile == null){
390 return null;
393 return containingFile.getVirtualFile();
396 private static final class MyInstrumentationItem implements ProcessingItem {
397 private final VirtualFile myClassFile;
398 private final VirtualFile myFormFile;
399 private final String myClassToBindFQname;
400 private final TimestampValidityState myState;
402 private MyInstrumentationItem(final VirtualFile classFile, final VirtualFile formFile, final String classToBindFQname) {
403 myClassFile = classFile;
404 myFormFile = formFile;
405 myClassToBindFQname = classToBindFQname;
406 myState = new TimestampValidityState(formFile.getTimeStamp());
409 @NotNull
410 public VirtualFile getFile() {
411 return myClassFile;
414 public VirtualFile getFormFile() {
415 return myFormFile;
418 public String getClassToBindFQname() {
419 return myClassToBindFQname;
422 public ValidityState getValidityState() {
423 return myState;