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
;
56 name
= FacetAutodetectingManagerImpl
.COMPONENT_NAME
,
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
) {
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() {
94 myDetectedFacetManager
.initUI();
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();
110 public String
getComponentName() {
111 return COMPONENT_NAME
;
114 public void initComponent() {
115 boolean unitTestMode
= ApplicationManager
.getApplication().isUnitTestMode();
116 if (!unitTestMode
&& !myProject
.isDefault()) {
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
);
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
)) {
200 myDetectionInProgress
= true;
201 FacetInfo2
<Module
> facet
= detector
.detectFacet(virtualFile
, myPsiManager
);
204 if (facets
== null) {
205 facets
= new SmartList
<FacetInfo2
<Module
>>();
211 myDetectionInProgress
= false;
217 public void disposeComponent() {
218 if (!ApplicationManager
.getApplication().isUnitTestMode() && !myProject
.isDefault()) {
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();
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()) {
262 if (ApplicationManager
.getApplication().isUnitTestMode()) {
266 myMergingUpdateQueue
.queue(update
);
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())) {
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
;
333 public FacetDetector
<?
,?
> findDetector(final String detectorId
) {
334 return myId2Detector
.get(detectorId
);
337 public FacetDetectionIndex
getFileIndex() {
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
) {
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
);
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
);
400 processChangedElement(event
);
404 public void childReplaced(final PsiTreeChangeEvent event
) {
405 processChangedElement(event
);
408 public void childMoved(final PsiTreeChangeEvent event
) {
409 processChangedElement(event
);