f20688a42db73cec8eeadba428d2eea8d7b00ffd
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / compiler / generator / GroovycStubGenerator.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.plugins.groovy.compiler.generator;
18
19 import com.intellij.compiler.impl.CompilerUtil;
20 import com.intellij.compiler.impl.FileSetCompileScope;
21 import com.intellij.compiler.impl.TranslatingCompilerFilesMonitor;
22 import com.intellij.openapi.application.AccessToken;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.application.ModalityState;
25 import com.intellij.openapi.application.WriteAction;
26 import com.intellij.openapi.compiler.CompileContext;
27 import com.intellij.openapi.compiler.CompileScope;
28 import com.intellij.openapi.compiler.CompilerPaths;
29 import com.intellij.openapi.compiler.ex.CompileContextEx;
30 import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.fileTypes.FileType;
33 import com.intellij.openapi.fileTypes.StdFileTypes;
34 import com.intellij.openapi.module.Module;
35 import com.intellij.openapi.module.ModuleManager;
36 import com.intellij.openapi.progress.ProgressIndicator;
37 import com.intellij.openapi.project.Project;
38 import com.intellij.openapi.util.io.FileUtil;
39 import com.intellij.openapi.util.text.StringUtil;
40 import com.intellij.openapi.vfs.LocalFileSystem;
41 import com.intellij.openapi.vfs.VfsUtil;
42 import com.intellij.openapi.vfs.VirtualFile;
43 import com.intellij.psi.JavaPsiFacade;
44 import com.intellij.psi.PsiClass;
45 import com.intellij.psi.PsiManager;
46 import com.intellij.psi.search.GlobalSearchScope;
47 import com.intellij.util.Chunk;
48 import com.intellij.util.Processor;
49 import com.intellij.util.containers.CollectionFactory;
50 import com.intellij.util.containers.ContainerUtil;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
53 import org.jetbrains.plugins.groovy.GroovyFileType;
54 import org.jetbrains.plugins.groovy.compiler.GroovyCompilerBase;
55 import org.jetbrains.plugins.groovy.compiler.GroovyCompilerConfiguration;
56 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
57 import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
58 import org.jetbrains.plugins.groovy.refactoring.convertToJava.GroovyToJavaGenerator;
59
60 import java.io.File;
61 import java.io.IOException;
62 import java.util.*;
63
64 /**
65  * @author peter
66  */
67 public class GroovycStubGenerator extends GroovyCompilerBase {
68   private static Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.compiler.generator.GroovycStubGenerator");
69
70   public static final String GROOVY_STUBS = "groovyStubs";
71
72   public GroovycStubGenerator(Project project) {
73     super(project);
74   }
75
76   @Override
77   public void compile(CompileContext compileContext, Chunk<Module> moduleChunk, VirtualFile[] virtualFiles, OutputSink sink) {
78     boolean hasJava = false;
79
80     final ExcludedEntriesConfiguration excluded = GroovyCompilerConfiguration.getExcludeConfiguration(myProject);
81
82     List<VirtualFile> total = new ArrayList<VirtualFile>();
83     for (final VirtualFile virtualFile : virtualFiles) {
84       final FileType fileType = virtualFile.getFileType();
85       if (fileType == StdFileTypes.JAVA) {
86         hasJava = true;
87       }
88
89       if (!excluded.isExcluded(virtualFile)) {
90         if (fileType == GroovyFileType.GROOVY_FILE_TYPE && GroovyNamesUtil.isIdentifier(virtualFile.getNameWithoutExtension())) {
91           total.add(virtualFile);
92         }
93       }
94     }
95
96     if (!hasJava) {
97       return;
98     }
99
100     if (total.isEmpty()) {
101       return;
102     }
103
104     //long l = System.currentTimeMillis();
105     super.compile(compileContext, moduleChunk, VfsUtil.toVirtualFileArray(total), sink);
106     //System.out.println("Stub generation took " + (System.currentTimeMillis() - l));
107   }
108
109   @Override
110   public boolean isCompilableFile(VirtualFile file, CompileContext context) {
111     return super.isCompilableFile(file, context) || StdFileTypes.JAVA.equals(file.getFileType());
112   }
113
114   @Override
115   protected void compileFiles(CompileContext compileContext,
116                               Module module,
117                               final List<VirtualFile> toCompile,
118                               OutputSink sink,
119                               boolean tests) {
120     final File outDir = getStubOutput(module, tests);
121     outDir.mkdirs();
122
123     final VirtualFile tempOutput = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(outDir);
124     assert tempOutput != null;
125     cleanDirectory(tempOutput);
126
127     ((CompileContextEx)compileContext).assignModule(tempOutput, module, tests, this);
128
129     ProgressIndicator indicator = compileContext.getProgressIndicator();
130     indicator.pushState();
131
132     try {
133       final GroovyToJavaGenerator generator = new GroovyToJavaGenerator(myProject, new HashSet<VirtualFile>(toCompile));
134       for (int i = 0; i < toCompile.size(); i++) {
135         indicator.setFraction((double)i / toCompile.size());
136
137         final Collection<VirtualFile> stubFiles = generateItems(generator, toCompile.get(i), tempOutput, compileContext, myProject);
138         ((CompileContextEx)compileContext).addScope(new FileSetCompileScope(stubFiles, new Module[]{module}));
139       }
140     }
141     finally {
142       indicator.popState();
143     }
144   }
145
146   private static File getStubOutput(Module module, boolean tests) {
147     final Project project = module.getProject();
148     final String rootPath = CompilerPaths.getGeneratedDataDirectory(project).getPath() + "/" + GROOVY_STUBS + "/";
149     return new File(rootPath + project.getLocationHash() + "/" + module.getName() + "/" + (tests ? "tests" : "production") + "/");
150   }
151
152   @Nullable
153   public static PsiClass findClassByStub(Project project, VirtualFile stubFile) {
154     final String[] components = StringUtil.trimEnd(stubFile.getPath(), ".java").split("[\\\\/]");
155     final int stubs = Arrays.asList(components).indexOf(GROOVY_STUBS);
156     if (stubs < 0 || stubs >= components.length - 4) return null;
157
158     final String moduleName = components[stubs + 2];
159     final Module module = ModuleManager.getInstance(project).findModuleByName(moduleName);
160     if (module == null) return null;
161
162     final String fqn = StringUtil.join(Arrays.asList(components).subList(stubs + 4, components.length), ".");
163     return JavaPsiFacade.getInstance(project).findClass(fqn, GlobalSearchScope.moduleScope(module));
164   }
165
166   private void cleanDirectory(final VirtualFile dir) {
167     Runnable runnable = new Runnable() {
168       @Override
169       public void run() {
170         AccessToken token = WriteAction.start();
171         try {
172           VfsUtil.processFilesRecursively(dir, new Processor<VirtualFile>() {
173             @Override
174             public boolean process(VirtualFile virtualFile) {
175               if (!virtualFile.isDirectory()) {
176                 TranslatingCompilerFilesMonitor.removeSourceInfo(virtualFile);
177                 try {
178                   virtualFile.delete(this);
179                 }
180                 catch (IOException e) {
181                   LOG.info(e);
182                 }
183               }
184               return true;
185             }
186           });
187         }
188         finally {
189           token.finish();
190         }
191       }
192     };
193     if (ApplicationManager.getApplication().isDispatchThread()) {
194       assert ApplicationManager.getApplication().isUnitTestMode();
195       runnable.run();
196     } else {
197       ApplicationManager.getApplication().invokeAndWait(runnable, ModalityState.NON_MODAL);
198     }
199   }
200
201   @NotNull
202   public String getDescription() {
203     return "Groovy to java source code generator";
204   }
205
206   public boolean validateConfiguration(CompileScope scope) {
207     return true;
208   }
209
210   public static Collection<VirtualFile> generateItems(final GroovyToJavaGenerator generator,
211                                                       final VirtualFile item,
212                                                       final VirtualFile outputRootDirectory,
213                                                       CompileContext context,
214                                                       final Project project) {
215     ProgressIndicator indicator = context.getProgressIndicator();
216     indicator.setText("Generating stubs for " + item.getName() + "...");
217
218     if (LOG.isDebugEnabled()) {
219       LOG.debug("Generating stubs for " + item.getName() + "...");
220     }
221
222     final Map<String, CharSequence> output;
223
224     AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
225
226     try {
227       output = generator.generateStubs((GroovyFile)PsiManager.getInstance(project).findFile(item));
228     }
229     finally {
230       accessToken.finish();
231     }
232
233     return writeStubs(outputRootDirectory, output, item);
234   }
235
236   private static List<VirtualFile> writeStubs(VirtualFile outputRootDirectory, Map<String, CharSequence> output, VirtualFile src) {
237     final ArrayList<VirtualFile> stubs = CollectionFactory.arrayList();
238     for (String relativePath : output.keySet()) {
239       final File stubFile = new File(outputRootDirectory.getPath(), relativePath);
240       FileUtil.createIfDoesntExist(stubFile);
241       try {
242         FileUtil.writeToFile(stubFile, output.get(relativePath).toString().getBytes(src.getCharset()));
243       }
244       catch (IOException e) {
245         LOG.error(e);
246       }
247       CompilerUtil.refreshIOFile(stubFile);
248       ContainerUtil.addIfNotNull(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(stubFile), stubs);
249     }
250     return stubs;
251   }
252 }