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.
17 package org.jetbrains.plugins.groovy.compiler.generator;
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.ide.highlighter.JavaFileType;
23 import com.intellij.openapi.application.AccessToken;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.application.ModalityState;
26 import com.intellij.openapi.application.WriteAction;
27 import com.intellij.openapi.compiler.CompileContext;
28 import com.intellij.openapi.compiler.CompileScope;
29 import com.intellij.openapi.compiler.CompilerPaths;
30 import com.intellij.openapi.compiler.ex.CompileContextEx;
31 import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
32 import com.intellij.openapi.diagnostic.Logger;
33 import com.intellij.openapi.module.Module;
34 import com.intellij.openapi.module.ModuleManager;
35 import com.intellij.openapi.progress.ProgressIndicator;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.roots.*;
38 import com.intellij.openapi.util.Pair;
39 import com.intellij.openapi.util.io.FileUtil;
40 import com.intellij.openapi.util.text.StringUtil;
41 import com.intellij.openapi.vfs.LocalFileSystem;
42 import com.intellij.openapi.vfs.VfsUtil;
43 import com.intellij.openapi.vfs.VirtualFile;
44 import com.intellij.psi.JavaPsiFacade;
45 import com.intellij.psi.PsiClass;
46 import com.intellij.psi.PsiManager;
47 import com.intellij.psi.search.GlobalSearchScope;
48 import com.intellij.util.Chunk;
49 import com.intellij.util.Processor;
50 import com.intellij.util.containers.CollectionFactory;
51 import com.intellij.util.containers.ContainerUtil;
52 import com.intellij.util.containers.FactoryMap;
53 import org.jetbrains.annotations.NotNull;
54 import org.jetbrains.annotations.Nullable;
55 import org.jetbrains.plugins.groovy.compiler.GroovyCompilerBase;
56 import org.jetbrains.plugins.groovy.compiler.GroovyCompilerConfiguration;
57 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
58 import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil;
59 import org.jetbrains.plugins.groovy.refactoring.convertToJava.GroovyToJavaGenerator;
62 import java.io.IOException;
68 public class GroovycStubGenerator extends GroovyCompilerBase {
69 private static Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.compiler.generator.GroovycStubGenerator");
71 public static final String GROOVY_STUBS = "groovyStubs";
73 public GroovycStubGenerator(Project project) {
78 public void compile(CompileContext compileContext, Chunk<Module> moduleChunk, VirtualFile[] virtualFiles, OutputSink sink) {
79 final ExcludedEntriesConfiguration excluded = GroovyCompilerConfiguration.getExcludeConfiguration(myProject);
81 @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") FactoryMap<Pair<Module, Boolean>, Boolean> hasJava = new FactoryMap<Pair<Module, Boolean>, Boolean>() {
83 protected Boolean create(Pair<Module, Boolean> key) {
84 return containsJavaSources(key.first, key.second);
88 ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
90 List<VirtualFile> total = new ArrayList<VirtualFile>();
91 for (final VirtualFile virtualFile : virtualFiles) {
92 if (!excluded.isExcluded(virtualFile) &&
93 GroovyNamesUtil.isIdentifier(virtualFile.getNameWithoutExtension())) {
94 Module module = index.getModuleForFile(virtualFile);
95 if (module == null || hasJava.get(Pair.create(module, index.isInTestSourceContent(virtualFile)))) {
96 total.add(virtualFile);
101 if (total.isEmpty()) {
105 //long l = System.currentTimeMillis();
106 super.compile(compileContext, moduleChunk, VfsUtil.toVirtualFileArray(total), sink);
107 //System.out.println("Stub generation took " + (System.currentTimeMillis() - l));
110 private static boolean containsJavaSources(Module module, boolean inTests) {
111 ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
112 for (ContentEntry entry : rootManager.getContentEntries()) {
113 for (SourceFolder folder : entry.getSourceFolders()) {
114 VirtualFile dir = folder.getFile();
115 if (inTests == folder.isTestSource() && dir != null) {
116 if (!rootManager.getFileIndex().iterateContentUnderDirectory(dir, new ContentIterator() {
118 public boolean processFile(VirtualFile fileOrDir) {
119 if (!fileOrDir.isDirectory() && JavaFileType.INSTANCE == fileOrDir.getFileType()) {
134 protected void compileFiles(CompileContext compileContext,
136 final List<VirtualFile> toCompile,
139 final File outDir = getStubOutput(module, tests);
142 final VirtualFile tempOutput = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(outDir);
143 assert tempOutput != null;
144 cleanDirectory(tempOutput);
146 ((CompileContextEx)compileContext).assignModule(tempOutput, module, tests, this);
148 ProgressIndicator indicator = compileContext.getProgressIndicator();
149 indicator.pushState();
152 final GroovyToJavaGenerator generator = new GroovyToJavaGenerator(myProject, new HashSet<VirtualFile>(toCompile));
153 for (int i = 0; i < toCompile.size(); i++) {
154 indicator.setFraction((double)i / toCompile.size());
156 final Collection<VirtualFile> stubFiles = generateItems(generator, toCompile.get(i), tempOutput, compileContext, myProject);
157 ((CompileContextEx)compileContext).addScope(new FileSetCompileScope(stubFiles, new Module[]{module}));
161 indicator.popState();
165 private static File getStubOutput(Module module, boolean tests) {
166 final Project project = module.getProject();
167 final String rootPath = CompilerPaths.getGeneratedDataDirectory(project).getPath() + "/" + GROOVY_STUBS + "/";
168 return new File(rootPath + project.getLocationHash() + "/" + module.getName() + "/" + (tests ? "tests" : "production") + "/");
172 public static PsiClass findClassByStub(Project project, VirtualFile stubFile) {
173 final String[] components = StringUtil.trimEnd(stubFile.getPath(), ".java").split("[\\\\/]");
174 final int stubs = Arrays.asList(components).indexOf(GROOVY_STUBS);
175 if (stubs < 0 || stubs >= components.length - 4) return null;
177 final String moduleName = components[stubs + 2];
178 final Module module = ModuleManager.getInstance(project).findModuleByName(moduleName);
179 if (module == null) return null;
181 final String fqn = StringUtil.join(Arrays.asList(components).subList(stubs + 4, components.length), ".");
182 return JavaPsiFacade.getInstance(project).findClass(fqn, GlobalSearchScope.moduleScope(module));
185 private void cleanDirectory(final VirtualFile dir) {
186 Runnable runnable = new Runnable() {
189 AccessToken token = WriteAction.start();
191 VfsUtil.processFilesRecursively(dir, new Processor<VirtualFile>() {
193 public boolean process(VirtualFile virtualFile) {
194 if (!virtualFile.isDirectory()) {
195 TranslatingCompilerFilesMonitor.removeSourceInfo(virtualFile);
197 virtualFile.delete(this);
199 catch (IOException e) {
212 if (ApplicationManager.getApplication().isDispatchThread()) {
213 assert ApplicationManager.getApplication().isUnitTestMode();
216 ApplicationManager.getApplication().invokeAndWait(runnable, ModalityState.NON_MODAL);
221 public String getDescription() {
222 return "Groovy to java source code generator";
225 public boolean validateConfiguration(CompileScope scope) {
229 public static Collection<VirtualFile> generateItems(final GroovyToJavaGenerator generator,
230 final VirtualFile item,
231 final VirtualFile outputRootDirectory,
232 CompileContext context,
233 final Project project) {
234 ProgressIndicator indicator = context.getProgressIndicator();
235 indicator.setText("Generating stubs for " + item.getName() + "...");
237 if (LOG.isDebugEnabled()) {
238 LOG.debug("Generating stubs for " + item.getName() + "...");
241 final Map<String, CharSequence> output;
243 AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
246 output = generator.generateStubs((GroovyFile)PsiManager.getInstance(project).findFile(item));
249 accessToken.finish();
252 return writeStubs(outputRootDirectory, output, item);
255 private static List<VirtualFile> writeStubs(VirtualFile outputRootDirectory, Map<String, CharSequence> output, VirtualFile src) {
256 final ArrayList<VirtualFile> stubs = CollectionFactory.arrayList();
257 for (String relativePath : output.keySet()) {
258 final File stubFile = new File(outputRootDirectory.getPath(), relativePath);
259 FileUtil.createIfDoesntExist(stubFile);
261 FileUtil.writeToFile(stubFile, output.get(relativePath).toString().getBytes(src.getCharset()));
263 catch (IOException e) {
266 CompilerUtil.refreshIOFile(stubFile);
267 ContainerUtil.addIfNotNull(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(stubFile), stubs);