blob: 946bf5e1a11be7abc81bab5a7b709c681f3fcae3 [file] [log] [blame]
/*
* 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;
}
}
}