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.
18 * @author: Eugene Zhuravlev
22 package com
.intellij
.compiler
.impl
.resourceCompiler
;
24 import com
.intellij
.compiler
.CompilerConfiguration
;
25 import com
.intellij
.compiler
.CompilerConfigurationImpl
;
26 import com
.intellij
.compiler
.impl
.CompilerUtil
;
27 import com
.intellij
.compiler
.make
.MakeUtil
;
28 import com
.intellij
.openapi
.application
.ApplicationManager
;
29 import com
.intellij
.openapi
.compiler
.*;
30 import com
.intellij
.openapi
.compiler
.ex
.CompileContextEx
;
31 import com
.intellij
.openapi
.diagnostic
.Logger
;
32 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
33 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
34 import com
.intellij
.openapi
.module
.Module
;
35 import com
.intellij
.openapi
.project
.Project
;
36 import com
.intellij
.openapi
.roots
.ProjectFileIndex
;
37 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
38 import com
.intellij
.openapi
.util
.io
.FileUtil
;
39 import com
.intellij
.openapi
.vfs
.VfsUtil
;
40 import com
.intellij
.openapi
.vfs
.VirtualFile
;
41 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
42 import com
.intellij
.util
.Chunk
;
43 import org
.jetbrains
.annotations
.NotNull
;
46 import java
.io
.IOException
;
49 public class ResourceCompiler
implements TranslatingCompiler
{
50 private static final Logger LOG
= Logger
.getInstance("#com.intellij.compiler.impl.resourceCompiler.ResourceCompiler");
51 private final Project myProject
;
52 private final CompilerConfiguration myConfiguration
;
53 private static final FileTypeManager FILE_TYPE_MANAGER
= FileTypeManager
.getInstance();
55 public ResourceCompiler(Project project
, CompilerConfiguration compilerConfiguration
) {
57 myConfiguration
= compilerConfiguration
;
61 public String
getDescription() {
62 return CompilerBundle
.message("resource.compiler.description");
65 public boolean validateConfiguration(CompileScope scope
) {
66 ((CompilerConfigurationImpl
) CompilerConfiguration
.getInstance(myProject
)).convertPatterns();
70 public boolean isCompilableFile(VirtualFile file
, CompileContext context
) {
71 return !StdFileTypes
.JAVA
.equals(FILE_TYPE_MANAGER
.getFileTypeByFile(file
)) && myConfiguration
.isResourceFile(file
);
74 public void compile(final CompileContext context
, Chunk
<Module
> moduleChunk
, final VirtualFile
[] files
, OutputSink sink
) {
75 context
.getProgressIndicator().pushState();
76 context
.getProgressIndicator().setText(CompilerBundle
.message("progress.copying.resources"));
78 final Map
<String
, Collection
<OutputItem
>> processed
= new HashMap
<String
, Collection
<OutputItem
>>();
79 final LinkedList
<CopyCommand
> copyCommands
= new LinkedList
<CopyCommand
>();
80 final Module singleChunkModule
= moduleChunk
.getNodes().size() == 1? moduleChunk
.getNodes().iterator().next() : null;
81 final long start
= System
.currentTimeMillis();
82 ApplicationManager
.getApplication().runReadAction(new Runnable() {
84 final ProjectFileIndex fileIndex
= ProjectRootManager
.getInstance(myProject
).getFileIndex();
85 for (final VirtualFile file
: files
) {
86 if (context
.getProgressIndicator().isCanceled()) {
89 final Module module
= singleChunkModule
!= null? singleChunkModule
: context
.getModuleByFile(file
);
91 continue; // looks like file invalidated
93 final VirtualFile fileRoot
= MakeUtil
.getSourceRoot(context
, module
, file
);
94 if (fileRoot
== null) {
97 final String sourcePath
= file
.getPath();
98 final String relativePath
= VfsUtil
.getRelativePath(file
, fileRoot
, '/');
99 final boolean inTests
= ((CompileContextEx
)context
).isInTestSourceContent(file
);
100 final VirtualFile outputDir
= inTests? context
.getModuleOutputDirectoryForTests(module
) : context
.getModuleOutputDirectory(module
);
101 if (outputDir
== null) {
104 final String outputPath
= outputDir
.getPath();
106 final String packagePrefix
= fileIndex
.getPackageNameByDirectory(fileRoot
);
107 final String targetPath
;
108 if (packagePrefix
!= null && packagePrefix
.length() > 0) {
109 targetPath
= outputPath
+ "/" + packagePrefix
.replace('.', '/') + "/" + relativePath
;
112 targetPath
= outputPath
+ "/" + relativePath
;
114 if (sourcePath
.equals(targetPath
)) {
115 addToMap(processed
, outputPath
, new MyOutputItem(targetPath
, file
));
118 copyCommands
.add(new CopyCommand(outputPath
, sourcePath
, targetPath
, file
));
124 final Set
<String
> rootsToRefresh
= new HashSet
<String
>();
125 // do actual copy outside of read action to reduce the time the application is locked on it
127 final int total
= copyCommands
.size();
128 CopyCommand
.ourCopyingTime
= 0L;
129 while (!copyCommands
.isEmpty()) {
130 final CopyCommand command
= copyCommands
.removeFirst();
131 if (context
.getProgressIndicator().isCanceled()) {
134 //context.getProgressIndicator().setFraction((idx++) * 1.0 / total);
135 context
.getProgressIndicator().setText2("Copying " + command
.getFromPath() + "...");
137 rootsToRefresh
.add(command
.getOutputPath());
138 final MyOutputItem outputItem
= command
.copy();
139 addToMap(processed
, command
.getOutputPath(), outputItem
);
141 catch (IOException e
) {
143 CompilerMessageCategory
.ERROR
,
144 CompilerBundle
.message("error.copying", command
.getFromPath(), command
.getToPath(), e
.getMessage()),
145 command
.getSourceFileUrl(), -1, -1
149 final long stop
= System
.currentTimeMillis();
151 CompilerUtil
.logDuration("Copying resources TOTAL", stop
- start
);
152 CompilerUtil
.logDuration("\tCopying resources (actual copying)", CopyCommand
.ourCopyingTime
);
154 if (!rootsToRefresh
.isEmpty()) {
155 final List
<File
> dirs
= new ArrayList
<File
>();
156 for (String path
: rootsToRefresh
) {
157 dirs
.add(new File(path
));
159 CompilerUtil
.refreshIODirectories(dirs
);
160 rootsToRefresh
.clear();
163 for (Iterator
<Map
.Entry
<String
, Collection
<OutputItem
>>> it
= processed
.entrySet().iterator(); it
.hasNext();) {
164 Map
.Entry
<String
, Collection
<OutputItem
>> entry
= it
.next();
165 sink
.add(entry
.getKey(), entry
.getValue(), VirtualFile
.EMPTY_ARRAY
);
166 it
.remove(); // to free memory
168 context
.getProgressIndicator().popState();
171 private static void addToMap(Map
<String
, Collection
<OutputItem
>> map
, String outputDir
, OutputItem item
) {
172 Collection
<OutputItem
> list
= map
.get(outputDir
);
174 list
= new ArrayList
<OutputItem
>();
175 map
.put(outputDir
, list
);
180 private static class CopyCommand
{
181 private final String myOutputPath
;
182 private final String myFromPath
;
183 private final String myToPath
;
184 private final VirtualFile mySourceFile
;
185 public static long ourCopyingTime
= 0L;
187 private CopyCommand(String outputPath
, String fromPath
, String toPath
, VirtualFile sourceFile
) {
188 myOutputPath
= outputPath
;
189 myFromPath
= fromPath
;
191 mySourceFile
= sourceFile
;
194 public MyOutputItem
copy() throws IOException
{
195 if (LOG
.isDebugEnabled()) {
196 LOG
.debug("Copying " + myFromPath
+ " to " + myToPath
);
198 final File targetFile
= new File(myToPath
);
199 final long start
= System
.currentTimeMillis();
200 FileUtil
.copyContent(new File(myFromPath
), targetFile
);
201 ourCopyingTime
+= (System
.currentTimeMillis() - start
);
202 return new MyOutputItem(myToPath
, mySourceFile
);
205 public String
getOutputPath() {
209 public String
getFromPath() {
213 public String
getToPath() {
217 public String
getSourceFileUrl() {
218 // do not use mySourseFile.getUrl() directly as it requires read action
219 return VirtualFileManager
.constructUrl(mySourceFile
.getFileSystem().getProtocol(), myFromPath
);
223 private static class MyOutputItem
implements OutputItem
{
224 private final String myTargetPath
;
225 private final VirtualFile myFile
;
227 private MyOutputItem(String targetPath
, VirtualFile sourceFile
) {
228 myTargetPath
= targetPath
;
232 public String
getOutputPath() {
236 public VirtualFile
getSourceFile() {