module content search scopes
[idea/community.git] / platform / lang-impl / src / com / intellij / openapi / module / impl / ModuleImpl.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 com.intellij.openapi.module.impl;
18
19 import com.intellij.ide.highlighter.ModuleFileType;
20 import com.intellij.ide.plugins.IdeaPluginDescriptor;
21 import com.intellij.ide.plugins.PluginManager;
22 import com.intellij.openapi.application.impl.ApplicationInfoImpl;
23 import com.intellij.openapi.components.impl.ComponentManagerImpl;
24 import com.intellij.openapi.components.impl.ModulePathMacroManager;
25 import com.intellij.openapi.components.impl.stores.IComponentStore;
26 import com.intellij.openapi.components.impl.stores.IModuleStore;
27 import com.intellij.openapi.components.impl.stores.ModuleStoreImpl;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.extensions.AreaInstance;
30 import com.intellij.openapi.extensions.ExtensionPointName;
31 import com.intellij.openapi.extensions.Extensions;
32 import com.intellij.openapi.module.Module;
33 import com.intellij.openapi.module.ModuleComponent;
34 import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope;
35 import com.intellij.openapi.module.impl.scopes.ModuleWithDependentsScope;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.roots.impl.storage.ClasspathStorage;
38 import com.intellij.openapi.util.Comparing;
39 import com.intellij.openapi.util.text.StringUtil;
40 import com.intellij.openapi.vfs.*;
41 import com.intellij.psi.search.GlobalSearchScope;
42 import com.intellij.util.PathUtil;
43 import com.intellij.util.containers.StripedLockIntObjectConcurrentHashMap;
44 import org.jetbrains.annotations.NonNls;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47 import org.picocontainer.MutablePicoContainer;
48
49 import java.io.File;
50 import java.io.IOException;
51 import java.util.*;
52
53 /**
54  * @author max
55  */
56 public class ModuleImpl extends ComponentManagerImpl implements Module {
57   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.module.impl.ModuleImpl");
58
59   @NotNull private final Project myProject;
60   private boolean isModuleAdded;
61
62   @NonNls private static final String OPTION_WORKSPACE = "workspace";
63
64   private final StripedLockIntObjectConcurrentHashMap<GlobalSearchScope> myScopeCache = new StripedLockIntObjectConcurrentHashMap<GlobalSearchScope>();
65
66   private GlobalSearchScope myModuleWithDependentsScope;
67   private GlobalSearchScope myModuleTestsWithDependentsScope;
68
69   public static final Object MODULE_RENAMING_REQUESTOR = new Object();
70
71   private String myName;
72
73   private IModuleStore myComponentStore;
74
75   public ModuleImpl(String filePath, Project project) {
76     super(project);
77
78     getPicoContainer().registerComponentInstance(Module.class, this);
79
80     myProject = project;
81
82     init(filePath);
83   }
84
85   protected void bootstrapPicoContainer() {
86     Extensions.instantiateArea(PluginManager.AREA_IDEA_MODULE, this, (AreaInstance)getParentComponentManager());
87     super.bootstrapPicoContainer();
88     getPicoContainer().registerComponentImplementation(IComponentStore.class, ModuleStoreImpl.class);
89     getPicoContainer().registerComponentImplementation(ModulePathMacroManager.class);
90   }
91
92   @NotNull
93   public synchronized IModuleStore getStateStore() {
94     if (myComponentStore == null) {
95       myComponentStore = (IModuleStore)getPicoContainer().getComponentInstance(IComponentStore.class);
96     }
97     return myComponentStore;
98   }
99
100   @Override
101   public void initializeComponent(Object component, boolean service) {
102     getStateStore().initComponent(component, service);
103   }
104
105   private void init(String filePath) {
106     getStateStore().setModuleFilePath(filePath);
107     myName = moduleNameByFileName(PathUtil.getFileName(filePath));
108
109     MyVirtualFileListener myVirtualFileListener = new MyVirtualFileListener();
110     VirtualFileManager.getInstance().addVirtualFileListener(myVirtualFileListener, this);
111   }
112
113   public void loadModuleComponents() {
114     final IdeaPluginDescriptor[] plugins = PluginManager.getPlugins();
115     for (IdeaPluginDescriptor plugin : plugins) {
116       if (PluginManager.shouldSkipPlugin(plugin)) continue;
117       loadComponentsConfiguration(plugin.getModuleComponents(), plugin, false);
118     }
119   }
120
121   protected boolean isComponentSuitable(Map<String, String> options) {
122     if (!super.isComponentSuitable(options)) return false;
123     if (options == null) return true;
124
125     Set<String> optionNames = options.keySet();
126     for (String optionName : optionNames) {
127       if (Comparing.equal(OPTION_WORKSPACE, optionName)) continue;
128       if (!parseOptionValue(options.get(optionName)).contains(getOptionValue(optionName))) return false;
129     }
130
131     return true;
132   }
133
134   private static List<String> parseOptionValue(String optionValue) {
135     if (optionValue == null) return new ArrayList<String>(0);
136     return Arrays.asList(optionValue.split(";"));
137   }
138
139   @Nullable
140   public VirtualFile getModuleFile() {
141     return getStateStore().getModuleFile();
142   }
143
144   void rename(String newName) {
145     myName = newName;
146     final VirtualFile file = getStateStore().getModuleFile();
147     try {
148       if (file != null) {
149         ClasspathStorage.moduleRenamed(this, newName);
150         file.rename(MODULE_RENAMING_REQUESTOR, newName + ModuleFileType.DOT_DEFAULT_EXTENSION);
151         getStateStore().setModuleFilePath(VfsUtil.virtualToIoFile(file).getCanonicalPath());
152         return;
153       }
154
155       // [dsl] we get here if either old file didn't exist or renaming failed
156       final File oldFile = new File(getModuleFilePath());
157       final File parentFile = oldFile.getParentFile();
158
159       final File newFile = new File(parentFile, newName + ModuleFileType.DOT_DEFAULT_EXTENSION);
160       getStateStore().setModuleFilePath(newFile.getCanonicalPath());
161     }
162     catch (IOException e) {
163       LOG.debug(e);
164     }
165   }
166
167   @NotNull
168   public String getModuleFilePath() {
169     return getStateStore().getModuleFilePath();
170   }
171
172   public synchronized void dispose() {
173     isModuleAdded = false;
174     disposeComponents();
175     Extensions.disposeArea(this);
176     myComponentStore = null;
177     super.dispose();
178   }
179
180
181   public void projectOpened() {
182     for (ModuleComponent component : getComponents(ModuleComponent.class)) {
183       try {
184         component.projectOpened();
185       }
186       catch (Exception e) {
187         LOG.error(e);
188       }
189     }
190   }
191
192   public void projectClosed() {
193     List<ModuleComponent> components = new ArrayList<ModuleComponent>(Arrays.asList(getComponents(ModuleComponent.class)));
194     Collections.reverse(components);
195
196     for (ModuleComponent component : components) {
197       try {
198         component.projectClosed();
199       }
200       catch (Exception e) {
201         LOG.error(e);
202       }
203     }
204   }
205
206   @NotNull
207   public Project getProject() {
208     return myProject;
209   }
210
211   @NotNull
212   public String getName() {
213     return myName;
214   }
215
216   public boolean isLoaded() {
217     return isModuleAdded;
218   }
219
220   public void moduleAdded() {
221     isModuleAdded = true;
222     for (ModuleComponent component : getComponents(ModuleComponent.class)) {
223       component.moduleAdded();
224     }
225   }
226
227   public void setOption(@NotNull String optionName, @NotNull String optionValue) {
228     getStateStore().setOption(optionName, optionValue);
229   }
230
231   public void clearOption(@NotNull String optionName) {
232     getStateStore().clearOption(optionName);
233   }
234
235   public String getOptionValue(@NotNull String optionName) {
236     return getStateStore().getOptionValue(optionName);
237   }
238
239   public GlobalSearchScope getModuleScope() {
240     return getCachedScope(ModuleWithDependenciesScope.COMPILE | ModuleWithDependenciesScope.TESTS);
241   }
242
243   @Override
244   public GlobalSearchScope getModuleScope(boolean includeTests) {
245     return getCachedScope(ModuleWithDependenciesScope.COMPILE | (includeTests ? ModuleWithDependenciesScope.TESTS : 0));
246   }
247
248   public GlobalSearchScope getModuleWithLibrariesScope() {
249     return getCachedScope(ModuleWithDependenciesScope.COMPILE | ModuleWithDependenciesScope.TESTS | ModuleWithDependenciesScope.LIBRARIES);
250   }
251
252   public GlobalSearchScope getModuleWithDependenciesScope() {
253     return getCachedScope(ModuleWithDependenciesScope.COMPILE | ModuleWithDependenciesScope.TESTS | ModuleWithDependenciesScope.MODULES);
254   }
255
256   @Override
257   public GlobalSearchScope getModuleContentScope() {
258     return getCachedScope(ModuleWithDependenciesScope.CONTENT);
259   }
260
261   @Override
262   public GlobalSearchScope getModuleContentWithDependenciesScope() {
263     return getCachedScope(ModuleWithDependenciesScope.CONTENT | ModuleWithDependenciesScope.MODULES);
264   }
265
266   public GlobalSearchScope getModuleWithDependenciesAndLibrariesScope(boolean includeTests) {
267     return getCachedScope(ModuleWithDependenciesScope.COMPILE |
268                           ModuleWithDependenciesScope.MODULES |
269                           ModuleWithDependenciesScope.LIBRARIES | (includeTests ? ModuleWithDependenciesScope.TESTS : 0));
270   }
271
272   public GlobalSearchScope getModuleWithDependentsScope() {
273     if (myModuleWithDependentsScope == null) {
274       myModuleWithDependentsScope = new ModuleWithDependentsScope(this, false);
275     }
276     return myModuleWithDependentsScope;
277   }
278
279   public GlobalSearchScope getModuleTestsWithDependentsScope() {
280     if (myModuleTestsWithDependentsScope == null) {
281       myModuleTestsWithDependentsScope = new ModuleWithDependentsScope(this, true);
282     }
283     return myModuleTestsWithDependentsScope;
284   }
285
286   public GlobalSearchScope getModuleRuntimeScope(boolean includeTests) {
287     return getCachedScope(
288       ModuleWithDependenciesScope.MODULES | ModuleWithDependenciesScope.LIBRARIES | (includeTests ? ModuleWithDependenciesScope.TESTS : 0));
289   }
290
291   @NotNull
292   public GlobalSearchScope getCachedScope(@ModuleWithDependenciesScope.ScopeConstant int options) {
293     GlobalSearchScope scope = myScopeCache.get(options);
294     if (scope == null) {
295       scope = new ModuleWithDependenciesScope(this, options);
296       myScopeCache.put(options, scope);
297     }
298     return scope;
299   }
300
301   public void clearScopesCache() {
302     myScopeCache.clear();
303     myModuleWithDependentsScope = null;
304     myModuleTestsWithDependentsScope = null;
305   }
306
307   @SuppressWarnings({"HardCodedStringLiteral"})
308   public String toString() {
309     if (myName == null) return "Module (not initialized)";
310     return "Module: '" + getName() + "' " +(isDisposed() ? "(Disposed)" : "path: '" + getModuleFilePath() + "'");
311   }
312
313   private static String moduleNameByFileName(@NotNull String fileName) {
314     return StringUtil.trimEnd(fileName, ModuleFileType.DOT_DEFAULT_EXTENSION);
315   }
316
317   public <T> T[] getExtensions(final ExtensionPointName<T> extensionPointName) {
318     return Extensions.getArea(this).getExtensionPoint(extensionPointName).getExtensions();
319   }
320
321   @Override
322   protected boolean logSlowComponents() {
323     return super.logSlowComponents() || ApplicationInfoImpl.getShadowInstance().isEAP();
324   }
325
326   private class MyVirtualFileListener extends VirtualFileAdapter {
327     public void propertyChanged(VirtualFilePropertyEvent event) {
328       if (!isModuleAdded) return;
329       final Object requestor = event.getRequestor();
330       if (MODULE_RENAMING_REQUESTOR.equals(requestor)) return;
331       if (!VirtualFile.PROP_NAME.equals(event.getPropertyName())) return;
332       final VirtualFile moduleFile = getModuleFile();
333       if (moduleFile == null) return;
334       if (moduleFile.equals(event.getFile())) {
335         myName = moduleNameByFileName(moduleFile.getName());
336         ModuleManagerImpl.getInstanceImpl(getProject()).fireModuleRenamedByVfsEvent(ModuleImpl.this);
337       }
338     }
339   }
340
341   protected MutablePicoContainer createPicoContainer() {
342     return Extensions.getArea(this).getPicoContainer();
343   }
344 }