/* * 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.compiler.impl.resourceCompiler; import com.intellij.compiler.impl.CompilerUtil; import com.intellij.compiler.make.MakeUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.Chunk; import com.intellij.util.ExceptionUtil; import consulo.compiler.impl.resourceCompiler.ResourceCompilerConfiguration; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.*; /* * @author: Eugene Zhuravlev * Date: Jan 17, 2003 * Time: 3:48:26 PM */ public class ResourceCompiler implements TranslatingCompiler { public static final Logger LOGGER = Logger.getInstance(ResourceCompiler.class); private final ResourceCompilerExtension[] myResourceCompilerExtensions = ResourceCompilerExtension.EP_NAME.getExtensions(); private final ResourceCompilerConfiguration myResourceCompilerConfiguration; private final ProjectFileIndex myProjectFileIndex; public ResourceCompiler(Project project) { myResourceCompilerConfiguration = ResourceCompilerConfiguration.getInstance(project); myProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project); } @Override @NotNull public String getDescription() { return CompilerBundle.message("resource.compiler.description"); } @Override public boolean validateConfiguration(CompileScope scope) { myResourceCompilerConfiguration.convertPatterns(); return true; } @Override public boolean isCompilableFile(VirtualFile file, CompileContext context) { final Module module = context.getModuleByFile(file); if (module == null) { return false; } if (myProjectFileIndex.isInResource(file) || myProjectFileIndex.isInTestResource(file)) { return true; } if (skipStandardResourceCompiler(module)) { return false; } return myResourceCompilerConfiguration.isResourceFile(file); } @Override public void compile(final CompileContext context, Chunk<Module> moduleChunk, final VirtualFile[] files, OutputSink sink) { context.getProgressIndicator().pushState(); context.getProgressIndicator().setText(CompilerBundle.message("progress.copying.resources")); final Map<String, Collection<OutputItem>> processed = new HashMap<String, Collection<OutputItem>>(); final LinkedList<CopyCommand> copyCommands = new LinkedList<CopyCommand>(); final Module singleChunkModule = moduleChunk.getNodes().size() == 1 ? moduleChunk.getNodes().iterator().next() : null; ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { for (final VirtualFile file : files) { if (context.getProgressIndicator().isCanceled()) { break; } final Module module = singleChunkModule != null ? singleChunkModule : context.getModuleByFile(file); if (module == null) { continue; // looks like file invalidated } final VirtualFile fileRoot = MakeUtil.getSourceRoot(context, module, file); if (fileRoot == null) { continue; } final String sourcePath = file.getPath(); final String relativePath = VfsUtilCore.getRelativePath(file, fileRoot, '/'); final VirtualFile outputDir = context.getOutputForFile(module, file); if (outputDir == null) { continue; } final String outputPath = outputDir.getPath(); final String packagePrefix = myProjectFileIndex.getPackageNameByDirectory(fileRoot); final String targetPath; if (packagePrefix != null && packagePrefix.length() > 0) { targetPath = outputPath + "/" + packagePrefix.replace('.', '/') + "/" + relativePath; } else { targetPath = outputPath + "/" + relativePath; } if (sourcePath.equals(targetPath)) { addToMap(processed, outputPath, new MyOutputItem(targetPath, file)); } else { copyCommands.add(new CopyCommand(outputPath, sourcePath, targetPath, file)); } } } }); final List<File> filesToRefresh = new ArrayList<File>(); // do actual copy outside of read action to reduce the time the application is locked on it while (!copyCommands.isEmpty()) { final CopyCommand command = copyCommands.removeFirst(); if (context.getProgressIndicator().isCanceled()) { break; } //context.getProgressIndicator().setFraction((idx++) * 1.0 / total); context.getProgressIndicator().setText2("Copying " + command.getFromPath() + "..."); try { final MyOutputItem outputItem = command.copy(filesToRefresh); addToMap(processed, command.getOutputPath(), outputItem); } catch (IOException e) { context.addMessage(CompilerMessageCategory.ERROR, CompilerBundle .message("error.copying", command.getFromPath(), command.getToPath(), ExceptionUtil.getThrowableText(e)), command.getSourceFileUrl(), -1, -1); } } if (!filesToRefresh.isEmpty()) { CompilerUtil.refreshIOFiles(filesToRefresh); filesToRefresh.clear(); } for (Iterator<Map.Entry<String, Collection<OutputItem>>> it = processed.entrySet().iterator(); it.hasNext(); ) { Map.Entry<String, Collection<OutputItem>> entry = it.next(); sink.add(entry.getKey(), entry.getValue(), VirtualFile.EMPTY_ARRAY); it.remove(); // to free memory } context.getProgressIndicator().popState(); } @NotNull @Override public FileType[] getInputFileTypes() { return FileType.EMPTY_ARRAY; } @NotNull @Override public FileType[] getOutputFileTypes() { return FileType.EMPTY_ARRAY; } private boolean skipStandardResourceCompiler(final Module module) { for (ResourceCompilerExtension extension : myResourceCompilerExtensions) { if (extension.skipStandardResourceCompiler(module)) { return true; } } return false; } private static void addToMap(Map<String, Collection<OutputItem>> map, String outputDir, OutputItem item) { Collection<OutputItem> list = map.get(outputDir); if (list == null) { list = new ArrayList<>(); map.put(outputDir, list); } list.add(item); } private static class CopyCommand { private final String myOutputPath; private final String myFromPath; private final String myToPath; private final VirtualFile mySourceFile; private CopyCommand(String outputPath, String fromPath, String toPath, VirtualFile sourceFile) { myOutputPath = outputPath; myFromPath = fromPath; myToPath = toPath; mySourceFile = sourceFile; } public MyOutputItem copy(List<File> filesToRefresh) throws IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Copying " + myFromPath + " to " + myToPath); } final File targetFile = new File(myToPath); FileUtil.copyContent(new File(myFromPath), targetFile); filesToRefresh.add(targetFile); return new MyOutputItem(myToPath, mySourceFile); } public String getOutputPath() { return myOutputPath; } public String getFromPath() { return myFromPath; } public String getToPath() { return myToPath; } public String getSourceFileUrl() { // do not use mySourseFile.getUrl() directly as it requires read action return VirtualFileManager.constructUrl(mySourceFile.getFileSystem().getProtocol(), myFromPath); } } private static class MyOutputItem implements OutputItem { private final String myTargetPath; private final VirtualFile myFile; private MyOutputItem(String targetPath, VirtualFile sourceFile) { myTargetPath = targetPath; myFile = sourceFile; } @Override public String getOutputPath() { return myTargetPath; } @Override public VirtualFile getSourceFile() { return myFile; } } }