update copyright
[fedora-idea.git] / java / idea-ui / src / com / intellij / facet / impl / autodetecting / FacetAutodetectingManagerImpl.java
blob6b079b7781a2fc40b8626efde179022bb949c03b
1 /*
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 com.intellij.facet.impl.autodetecting;
19 import com.intellij.facet.Facet;
20 import com.intellij.facet.FacetConfiguration;
21 import com.intellij.facet.FacetType;
22 import com.intellij.facet.FacetTypeRegistry;
23 import com.intellij.facet.autodetecting.FacetDetector;
24 import com.intellij.facet.autodetecting.UnderlyingFacetSelector;
25 import com.intellij.facet.impl.autodetecting.model.FacetInfo2;
26 import com.intellij.facet.impl.autodetecting.model.ProjectFacetInfoSet;
27 import com.intellij.facet.pointers.FacetPointersManager;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.components.PersistentStateComponent;
30 import com.intellij.openapi.components.ProjectComponent;
31 import com.intellij.openapi.components.State;
32 import com.intellij.openapi.components.Storage;
33 import com.intellij.openapi.fileTypes.FileType;
34 import com.intellij.openapi.module.Module;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.util.Condition;
37 import com.intellij.openapi.util.MultiValuesMap;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.openapi.vfs.VirtualFileFilter;
40 import com.intellij.psi.*;
41 import com.intellij.util.SmartList;
42 import com.intellij.util.ui.update.MergingUpdateQueue;
43 import com.intellij.util.ui.update.Update;
44 import gnu.trove.THashSet;
45 import org.jetbrains.annotations.NonNls;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48 import org.jetbrains.annotations.TestOnly;
50 import java.util.*;
52 /**
53 * @author nik
55 @State(
56 name = FacetAutodetectingManagerImpl.COMPONENT_NAME,
57 storages = {
58 @Storage(
59 id="other",
60 file = "$PROJECT_FILE$"
64 public class FacetAutodetectingManagerImpl extends FacetAutodetectingManager implements AutodetectionFilter, ProjectComponent, PersistentStateComponent<DisabledAutodetectionInfo> {
65 @NonNls public static final String COMPONENT_NAME = "FacetAutodetectingManager";
66 private final MultiValuesMap<FileType, FacetDetectorWrapper> myDetectors = new MultiValuesMap<FileType, FacetDetectorWrapper>();
67 private final Map<String, FacetDetector<?,?>> myId2Detector = new HashMap<String, FacetDetector<?,?>>();
68 private final Project myProject;
69 private final PsiManager myPsiManager;
70 private final FacetPointersManager myFacetPointersManager;
71 private FacetDetectionIndex myFileIndex;
72 private MergingUpdateQueue myMergingUpdateQueue;
73 private final ProjectFacetInfoSet myDetectedFacetSet;
74 private DetectedFacetManager myDetectedFacetManager;
75 private DisabledAutodetectionInfo myDisabledAutodetectionInfo = new DisabledAutodetectionInfo();
76 private boolean myDetectionInProgress;
77 private final Set<FacetType<?,?>> myFacetTypesWithDetectors = new THashSet<FacetType<?,?>>();
78 private final EnableAutodetectionWorker myEnableAutodetectionWorker;
80 public FacetAutodetectingManagerImpl(final Project project, PsiManager psiManager, FacetPointersManager facetPointersManager) {
81 myProject = project;
82 myPsiManager = psiManager;
83 myFacetPointersManager = facetPointersManager;
84 myDetectedFacetSet = new ProjectFacetInfoSet(project, project);
85 myEnableAutodetectionWorker = new EnableAutodetectionWorker(project, this);
88 public void projectOpened() {
89 if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) return;
91 myDetectedFacetSet.loadDetectedFacets(FacetDetectionIndex.getDetectedFacetsFile(myProject));
92 ApplicationManager.getApplication().invokeLater(new Runnable() {
93 public void run() {
94 myDetectedFacetManager.initUI();
96 });
99 public void projectClosed() {
100 if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) return;
102 myDetectedFacetSet.saveDetectedFacets(FacetDetectionIndex.getDetectedFacetsFile(myProject));
103 if (myDetectedFacetManager != null) {
104 myDetectedFacetManager.disposeUI();
108 @NonNls
109 @NotNull
110 public String getComponentName() {
111 return COMPONENT_NAME;
114 public void initComponent() {
115 boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
116 if (!unitTestMode && !myProject.isDefault()) {
117 initialize();
121 public void initialize() {
122 myDetectedFacetManager = new DetectedFacetManager(myProject, this, myDetectedFacetSet);
123 FacetType[] types = FacetTypeRegistry.getInstance().getFacetTypes();
124 for (FacetType<?,?> type : types) {
125 registerDetectors(type);
127 myFileIndex = new FacetDetectionIndex(myProject, this, myDetectors.keySet());
128 myFileIndex.initialize();
129 MyPsiTreeChangeListener psiTreeChangeListener = new MyPsiTreeChangeListener();
130 myPsiManager.addPsiTreeChangeListener(psiTreeChangeListener, myProject);
131 myMergingUpdateQueue = new MergingUpdateQueue("FacetAutodetectionQueue", 500, true, null, myProject);
134 private <F extends Facet<C>, C extends FacetConfiguration> void registerDetectors(final FacetType<F, C> type) {
135 FacetOnTheFlyDetectorRegistryImpl<C, F> detectorRegistry = new FacetOnTheFlyDetectorRegistryImpl<C, F>(type);
136 type.registerDetectors(new FacetDetectorRegistryEx<C>(null, detectorRegistry));
137 if (detectorRegistry.hasDetectors()) {
138 myFacetTypesWithDetectors.add(type);
139 myDetectedFacetManager.registerListeners(type);
143 @Nullable
144 public DisabledAutodetectionByTypeElement getDisabledAutodetectionState(@NotNull FacetType<?,?> type) {
145 return myDisabledAutodetectionInfo.findElement(type.getStringId());
148 public DisabledAutodetectionInfo getState() {
149 return myDisabledAutodetectionInfo;
152 public void loadState(final DisabledAutodetectionInfo state) {
153 myDisabledAutodetectionInfo = state;
156 public void processFile(VirtualFile virtualFile) {
157 if (!virtualFile.isValid() || virtualFile.isDirectory() || myProject.isDisposed()
158 || !virtualFile.exists() || !myFileIndex.getProjectFileIndex().isInContent(virtualFile)) return;
160 FileType fileType = virtualFile.getFileType();
161 Collection<FacetDetectorWrapper> detectors = myDetectors.get(fileType);
162 if (detectors == null) return;
164 List<FacetInfo2<Module>> facets = null;
165 for (FacetDetectorWrapper<?,?,?,?> detector : detectors) {
166 facets = process(virtualFile, detector, facets);
169 String url = virtualFile.getUrl();
170 FacetDetectionIndexEntry indexEntry = myFileIndex.getIndexEntry(url);
171 if (indexEntry == null) {
172 indexEntry = new FacetDetectionIndexEntry(virtualFile.getTimeStamp());
175 Collection<Integer> removed = indexEntry.update(myFacetPointersManager, facets);
176 myFileIndex.putIndexEntry(url, indexEntry);
178 if (removed != null) {
179 removeObsoleteFacets(removed);
183 public void removeObsoleteFacets(final Collection<Integer> ids) {
184 for (Integer id : ids) {
185 Set<String> urls = myFileIndex.getFiles(id);
186 if (urls == null || urls.isEmpty()) {
187 myDetectedFacetSet.removeDetectedFacetWithSubFacets(id);
192 public ProjectFacetInfoSet getDetectedFacetSet() {
193 return myDetectedFacetSet;
196 private List<FacetInfo2<Module>> process(final VirtualFile virtualFile, final FacetDetectorWrapper<?, ?, ?, ?> detector,
197 List<FacetInfo2<Module>> facets) {
198 if (!myDetectionInProgress && detector.getVirtualFileFilter().accept(virtualFile)) {
199 try {
200 myDetectionInProgress = true;
201 FacetInfo2<Module> facet = detector.detectFacet(virtualFile, myPsiManager);
203 if (facet != null) {
204 if (facets == null) {
205 facets = new SmartList<FacetInfo2<Module>>();
207 facets.add(facet);
210 finally {
211 myDetectionInProgress = false;
214 return facets;
217 public void disposeComponent() {
218 if (!ApplicationManager.getApplication().isUnitTestMode() && !myProject.isDefault()) {
219 dispose();
223 public void dispose() {
224 if (myFileIndex != null) {
225 myFileIndex.dispose();
229 public boolean hasDetectors(@NotNull FacetType<?, ?> facetType) {
230 return myFacetTypesWithDetectors.contains(facetType);
233 public void redetectFacets() {
234 myEnableAutodetectionWorker.redetectFacets();
237 @TestOnly
238 public EnableAutodetectionWorker getEnableAutodetectionWorker() {
239 return myEnableAutodetectionWorker;
242 public boolean isAutodetectionEnabled(final Module module, final FacetType facetType, final String url) {
243 return !myDisabledAutodetectionInfo.isDisabled(facetType.getStringId(), module.getName(), url);
246 private void queueUpdate(final PsiFile psiFile) {
247 if (!myDetectors.keySet().contains(psiFile.getFileType())) return;
249 VirtualFile virtualFile = psiFile.getVirtualFile();
250 if (virtualFile != null) {
251 queueUpdate(virtualFile);
255 public void queueUpdate(final VirtualFile file) {
256 Update update = new Update("file:" + file.getUrl()) {
257 public void run() {
258 processFile(file);
262 if (ApplicationManager.getApplication().isUnitTestMode()) {
263 update.run();
265 else {
266 myMergingUpdateQueue.queue(update);
270 @Nullable
271 public Set<String> getFiles(final Facet facet) {
272 return myFileIndex.getFiles(facet);
275 public void removeFacetFromCache(final Facet facet) {
276 myFileIndex.removeFacetFromCache(myFacetPointersManager.create(facet));
279 public Set<FileType> getFileTypes(final Set<FacetType> facetTypes) {
280 THashSet<FileType> fileTypes = new THashSet<FileType>();
281 for (FileType type : myDetectors.keySet()) {
282 Collection<FacetDetectorWrapper> detectorWrappers = myDetectors.get(type);
283 if (detectorWrappers != null) {
284 for (FacetDetectorWrapper detectorWrapper : detectorWrappers) {
285 if (facetTypes.contains(detectorWrapper.getFacetType())) {
286 fileTypes.add(type);
287 break;
292 return fileTypes;
295 public void disableAutodetectionInModule(final FacetType type, final Module module) {
296 getState().addDisabled(type.getStringId(), module.getName());
299 public void disableAutodetectionInProject() {
300 for (FacetType facetType : FacetTypeRegistry.getInstance().getFacetTypes()) {
301 disableAutodetectionInProject(facetType);
305 public void disableAutodetectionInProject(final FacetType type) {
306 getState().addDisabled(type.getStringId());
309 public void disableAutodetectionInDirs(@NotNull Module module, @NotNull String... dirUrls) {
310 for (FacetType<?, ?> facetType : myFacetTypesWithDetectors) {
311 for (String dirUrl : dirUrls) {
312 getState().addDisabled(facetType.getStringId(), module.getName(), dirUrl, true);
317 public void disableAutodetectionInFiles(@NotNull final FacetType type, @NotNull final Module module, @NotNull final String... fileUrls) {
318 getState().addDisabled(type.getStringId(), module.getName(), fileUrls);
321 public void setDisabledAutodetectionState(final @NotNull FacetType<?, ?> facetType, final @Nullable DisabledAutodetectionByTypeElement element) {
322 String id = facetType.getStringId();
323 DisabledAutodetectionByTypeElement oldElement = myDisabledAutodetectionInfo.findElement(id);
324 myEnableAutodetectionWorker.queueChanges(facetType, oldElement, element);
325 myDisabledAutodetectionInfo.replaceElement(id, element);
328 public DetectedFacetManager getDetectedFacetManager() {
329 return myDetectedFacetManager;
332 @Nullable
333 public FacetDetector<?,?> findDetector(final String detectorId) {
334 return myId2Detector.get(detectorId);
337 public FacetDetectionIndex getFileIndex() {
338 return myFileIndex;
341 private class FacetOnTheFlyDetectorRegistryImpl<C extends FacetConfiguration, F extends Facet<C>> implements FacetOnTheFlyDetectorRegistry<C> {
342 private final FacetType<F, C> myType;
343 private boolean myHasDetectors;
345 public FacetOnTheFlyDetectorRegistryImpl(final FacetType<F, C> type) {
346 myType = type;
349 public <U extends FacetConfiguration> void register(@NotNull final FileType fileType, @NotNull final VirtualFileFilter virtualFileFilter,
350 @NotNull final FacetDetector<VirtualFile, C> facetDetector,
351 final UnderlyingFacetSelector<VirtualFile, U> selector) {
352 myHasDetectors = true;
353 myId2Detector.put(facetDetector.getId(), facetDetector);
354 myDetectors.put(fileType, new FacetByVirtualFileDetectorWrapper<C, F, U>(myDetectedFacetSet, myType,
355 FacetAutodetectingManagerImpl.this, virtualFileFilter,
356 facetDetector, selector));
359 public <U extends FacetConfiguration> void register(@NotNull final FileType fileType, @NotNull final VirtualFileFilter virtualFileFilter,
360 @NotNull final Condition<PsiFile> psiFileFilter, @NotNull final FacetDetector<PsiFile, C> facetDetector,
361 final UnderlyingFacetSelector<VirtualFile, U> selector) {
362 myHasDetectors = true;
363 myId2Detector.put(facetDetector.getId(), facetDetector);
364 myDetectors.put(fileType, new FacetByPsiFileDetectorWrapper<C, F, U>(myDetectedFacetSet, myType, FacetAutodetectingManagerImpl.this,
365 virtualFileFilter, facetDetector, psiFileFilter, selector));
368 public boolean hasDetectors() {
369 return myHasDetectors;
373 private class MyPsiTreeChangeListener extends PsiTreeChangeAdapter {
374 public void childAdded(final PsiTreeChangeEvent event) {
375 PsiElement child = event.getChild();
376 if (child instanceof PsiFile) {
377 queueUpdate((PsiFile)child);
379 else {
380 processChangedElement(event);
384 private void processChangedElement(final PsiTreeChangeEvent event) {
385 PsiFile psiFile = event.getFile();
386 if (psiFile != null) {
387 queueUpdate(psiFile);
391 public void childRemoved(final PsiTreeChangeEvent event) {
392 PsiElement child = event.getChild();
393 if (child instanceof PsiFile) {
394 VirtualFile virtualFile = ((PsiFile)child).getVirtualFile();
395 if (virtualFile != null) {
396 myFileIndex.removeIndexEntry(virtualFile);
399 else {
400 processChangedElement(event);
404 public void childReplaced(final PsiTreeChangeEvent event) {
405 processChangedElement(event);
408 public void childMoved(final PsiTreeChangeEvent event) {
409 processChangedElement(event);