| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.uiDesigner.make; |
| |
| import com.intellij.compiler.impl.CompilerUtil; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ModalityState; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.compiler.*; |
| import com.intellij.openapi.fileEditor.FileDocumentManager; |
| import com.intellij.openapi.fileTypes.StdFileTypes; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleUtilCore; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiClass; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.uiDesigner.FormEditingUtil; |
| import com.intellij.uiDesigner.GuiDesignerConfiguration; |
| import com.intellij.uiDesigner.UIDesignerBundle; |
| import com.intellij.uiDesigner.compiler.AlienFormFileException; |
| import com.intellij.uiDesigner.compiler.FormErrorInfo; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.jps.incremental.java.CopyResourcesUtil; |
| |
| import java.io.DataInput; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| |
| public final class Form2SourceCompiler implements SourceInstrumentingCompiler{ |
| |
| private static VirtualFile findSourceFile(final CompileContext context, final VirtualFile formFile, final String className) { |
| final Module module = context.getModuleByFile(formFile); |
| if (module == null) { |
| return null; |
| } |
| final PsiClass aClass = FormEditingUtil.findClassToBind(module, className); |
| if (aClass == null) { |
| return null; |
| } |
| |
| final PsiFile containingFile = aClass.getContainingFile(); |
| if (containingFile == null){ |
| return null; |
| } |
| |
| return containingFile.getVirtualFile(); |
| } |
| |
| @NotNull |
| public String getDescription() { |
| return UIDesignerBundle.message("component.gui.designer.form.to.source.compiler"); |
| } |
| |
| public boolean validateConfiguration(CompileScope scope) { |
| return true; |
| } |
| |
| @NotNull |
| public ProcessingItem[] getProcessingItems(final CompileContext context) { |
| final Project project = context.getProject(); |
| if (GuiDesignerConfiguration.getInstance(project).INSTRUMENT_CLASSES) { |
| return ProcessingItem.EMPTY_ARRAY; |
| } |
| |
| final ArrayList<ProcessingItem> items = new ArrayList<ProcessingItem>(); |
| |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| public void run() { |
| final CompileScope scope = context.getCompileScope(); |
| final CompileScope projectScope = context.getProjectCompileScope(); |
| |
| final VirtualFile[] formFiles = projectScope.getFiles(StdFileTypes.GUI_DESIGNER_FORM, true); |
| final CompilerManager compilerManager = CompilerManager.getInstance(project); |
| final BindingsCache bindingsCache = new BindingsCache(project); |
| |
| try { |
| final HashMap<String, VirtualFile> class2form = new HashMap<String, VirtualFile>(); |
| |
| for (final VirtualFile formFile : formFiles) { |
| if (compilerManager.isExcludedFromCompilation(formFile)) { |
| continue; |
| } |
| |
| final String classToBind; |
| try { |
| classToBind = bindingsCache.getBoundClassName(formFile); |
| } |
| catch (AlienFormFileException e) { |
| // ignore non-IDEA forms |
| continue; |
| } |
| catch (Exception e) { |
| addError(context, new FormErrorInfo(null, UIDesignerBundle.message("error.cannot.process.form.file", e)), formFile); |
| continue; |
| } |
| |
| if (classToBind == null) { |
| continue; |
| } |
| |
| final VirtualFile sourceFile = findSourceFile(context, formFile, classToBind); |
| if (sourceFile == null) { |
| if (scope.belongs(formFile.getUrl())) { |
| addError(context, new FormErrorInfo(null, UIDesignerBundle.message("error.class.to.bind.does.not.exist", classToBind)), formFile); |
| } |
| continue; |
| } |
| |
| final boolean inScope = scope.belongs(sourceFile.getUrl()) || scope.belongs(formFile.getUrl()); |
| |
| final VirtualFile alreadyProcessedForm = class2form.get(classToBind); |
| if (alreadyProcessedForm != null) { |
| if (inScope) { |
| addError(context, new FormErrorInfo(null, UIDesignerBundle.message("error.duplicate.bind", classToBind, alreadyProcessedForm.getPresentableUrl())), formFile); |
| } |
| continue; |
| } |
| class2form.put(classToBind, formFile); |
| |
| if (!inScope) { |
| continue; |
| } |
| |
| items.add(new MyInstrumentationItem(sourceFile, formFile)); |
| } |
| } |
| finally { |
| bindingsCache.close(); |
| } |
| } |
| }); |
| |
| return items.toArray(new ProcessingItem[items.size()]); |
| } |
| |
| public ProcessingItem[] process(final CompileContext context, final ProcessingItem[] items) { |
| final ArrayList<ProcessingItem> compiledItems = new ArrayList<ProcessingItem>(); |
| |
| context.getProgressIndicator().setText(UIDesignerBundle.message("progress.compiling.ui.forms")); |
| |
| int formsProcessed = 0; |
| |
| final Project project = context.getProject(); |
| final FormSourceCodeGenerator generator = new FormSourceCodeGenerator(project); |
| |
| final HashSet<Module> processedModules = new HashSet<Module>(); |
| |
| final List<File> filesToRefresh = new ArrayList<File>(); |
| for (ProcessingItem item1 : items) { |
| context.getProgressIndicator().setFraction((double)(++formsProcessed) / ((double)items.length)); |
| |
| final MyInstrumentationItem item = (MyInstrumentationItem)item1; |
| |
| final VirtualFile formFile = item.getFormFile(); |
| |
| if (GuiDesignerConfiguration.getInstance(project).COPY_FORMS_RUNTIME_TO_OUTPUT) { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| public void run() { |
| final Module module = ModuleUtilCore.findModuleForFile(formFile, project); |
| if (module != null && !processedModules.contains(module)) { |
| processedModules.add(module); |
| final String moduleOutputPath = CompilerPaths.getModuleOutputPath(module, false); |
| try { |
| if (moduleOutputPath != null) { |
| filesToRefresh.addAll(CopyResourcesUtil.copyFormsRuntime(moduleOutputPath, false)); |
| } |
| final String testsOutputPath = CompilerPaths.getModuleOutputPath(module, true); |
| if (testsOutputPath != null && !testsOutputPath.equals(moduleOutputPath)) { |
| filesToRefresh.addAll(CopyResourcesUtil.copyFormsRuntime(testsOutputPath, false)); |
| } |
| } |
| catch (IOException e) { |
| addError( |
| context, |
| new FormErrorInfo(null, UIDesignerBundle.message("error.cannot.copy.gui.designer.form.runtime", |
| module.getName(), e.toString())), |
| null |
| ); |
| } |
| } |
| } |
| }); |
| } |
| |
| ApplicationManager.getApplication().invokeAndWait(new Runnable() { |
| public void run() { |
| CommandProcessor.getInstance().executeCommand(project, new Runnable() { |
| public void run() { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| public void run() { |
| PsiDocumentManager.getInstance(project).commitAllDocuments(); |
| generator.generate(formFile); |
| final ArrayList<FormErrorInfo> errors = generator.getErrors(); |
| if (errors.size() == 0) { |
| compiledItems.add(item); |
| } |
| else { |
| for (final FormErrorInfo e : errors) { |
| addError(context, e, formFile); |
| } |
| } |
| } |
| }); |
| } |
| }, "", null); |
| FileDocumentManager.getInstance().saveAllDocuments(); |
| } |
| }, ModalityState.NON_MODAL); |
| } |
| |
| CompilerUtil.refreshIOFiles(filesToRefresh); |
| return compiledItems.toArray(new ProcessingItem[compiledItems.size()]); |
| } |
| |
| private static void addError(final CompileContext context, final FormErrorInfo e, final VirtualFile formFile) { |
| if (formFile != null) { |
| FormElementNavigatable navigatable = new FormElementNavigatable(context.getProject(), formFile, e.getComponentId()); |
| context.addMessage(CompilerMessageCategory.ERROR, |
| formFile.getPresentableUrl() + ": " + e.getErrorMessage(), |
| formFile.getUrl(), -1, -1, navigatable); |
| } |
| else { |
| context.addMessage(CompilerMessageCategory.ERROR, e.getErrorMessage(), null, -1, -1); |
| } |
| } |
| |
| public ValidityState createValidityState(final DataInput in) throws IOException { |
| return TimestampValidityState.load(in); |
| } |
| |
| private static final class MyInstrumentationItem implements ProcessingItem { |
| @NotNull private final VirtualFile mySourceFile; |
| private final VirtualFile myFormFile; |
| private final TimestampValidityState myState; |
| |
| public MyInstrumentationItem(@NotNull final VirtualFile sourceFile, final VirtualFile formFile) { |
| mySourceFile = sourceFile; |
| myFormFile = formFile; |
| myState = new TimestampValidityState(formFile.getTimeStamp()); |
| } |
| |
| @NotNull |
| public VirtualFile getFile() { |
| return mySourceFile; |
| } |
| |
| public VirtualFile getFormFile() { |
| return myFormFile; |
| } |
| |
| public ValidityState getValidityState() { |
| return myState; |
| } |
| } |
| |
| } |