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.
16 package com
.intellij
.psi
.impl
.file
;
18 import com
.intellij
.codeInsight
.completion
.scope
.JavaCompletionProcessor
;
19 import com
.intellij
.ide
.projectView
.ProjectView
;
20 import com
.intellij
.ide
.projectView
.impl
.PackageViewPane
;
21 import com
.intellij
.ide
.projectView
.impl
.ProjectRootsUtil
;
22 import com
.intellij
.ide
.projectView
.impl
.nodes
.PackageElement
;
23 import com
.intellij
.lang
.ASTNode
;
24 import com
.intellij
.lang
.Language
;
25 import com
.intellij
.openapi
.application
.ApplicationManager
;
26 import com
.intellij
.openapi
.application
.ReadActionProcessor
;
27 import com
.intellij
.openapi
.command
.undo
.DocumentReference
;
28 import com
.intellij
.openapi
.command
.undo
.UndoManager
;
29 import com
.intellij
.openapi
.command
.undo
.UndoableAction
;
30 import com
.intellij
.openapi
.diagnostic
.Logger
;
31 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
32 import com
.intellij
.openapi
.module
.Module
;
33 import com
.intellij
.openapi
.module
.ModuleManager
;
34 import com
.intellij
.openapi
.roots
.*;
35 import com
.intellij
.openapi
.util
.Condition
;
36 import com
.intellij
.openapi
.util
.TextRange
;
37 import com
.intellij
.openapi
.vfs
.VirtualFile
;
38 import com
.intellij
.openapi
.wm
.ToolWindowId
;
39 import com
.intellij
.openapi
.wm
.ToolWindowManager
;
40 import com
.intellij
.psi
.*;
41 import com
.intellij
.psi
.impl
.DebugUtil
;
42 import com
.intellij
.psi
.impl
.JavaPsiFacadeImpl
;
43 import com
.intellij
.psi
.impl
.PsiElementBase
;
44 import com
.intellij
.psi
.impl
.PsiManagerEx
;
45 import com
.intellij
.psi
.impl
.migration
.PsiMigrationImpl
;
46 import com
.intellij
.psi
.impl
.source
.tree
.java
.PsiCompositeModifierList
;
47 import com
.intellij
.psi
.scope
.ElementClassHint
;
48 import com
.intellij
.psi
.scope
.NameHint
;
49 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
50 import com
.intellij
.psi
.search
.GlobalSearchScope
;
51 import com
.intellij
.psi
.search
.ProjectScope
;
52 import com
.intellij
.psi
.util
.*;
53 import com
.intellij
.refactoring
.rename
.RenameUtil
;
54 import com
.intellij
.ui
.RowIcon
;
55 import com
.intellij
.util
.*;
56 import com
.intellij
.util
.containers
.ContainerUtil
;
57 import gnu
.trove
.THashSet
;
58 import org
.jetbrains
.annotations
.NonNls
;
59 import org
.jetbrains
.annotations
.NotNull
;
60 import org
.jetbrains
.annotations
.Nullable
;
62 import java
.util
.ArrayList
;
63 import java
.util
.Collection
;
64 import java
.util
.List
;
67 public class PsiPackageImpl
extends PsiElementBase
implements PsiPackage
{
68 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.file.PsiPackageImpl");
70 private final PsiManagerEx myManager
;
71 private final String myQualifiedName
;
72 private volatile CachedValue
<PsiModifierList
> myAnnotationList
;
73 private volatile CachedValue
<Collection
<PsiDirectory
>> myDirectories
;
75 private volatile Set
<String
> myPublicClassNamesCache
;
76 private final Object myPublicClassNamesCacheLock
= new String("package classnames cache lock");
78 public PsiPackageImpl(PsiManagerEx manager
, String qualifiedName
) {
80 myQualifiedName
= qualifiedName
;
83 public boolean equals(Object o
) {
84 return o
instanceof PsiPackageImpl
85 && myManager
== ((PsiPackageImpl
)o
).myManager
86 && myQualifiedName
.equals(((PsiPackageImpl
)o
).myQualifiedName
);
89 public int hashCode() {
90 return myQualifiedName
.hashCode();
94 public String
getQualifiedName() {
95 return myQualifiedName
;
99 public PsiDirectory
[] getDirectories() {
100 final Collection
<PsiDirectory
> collection
= getAllDirectories();
101 return collection
.toArray(new PsiDirectory
[collection
.size()]);
104 private Collection
<PsiDirectory
> getAllDirectories() {
105 if (myDirectories
== null) {
106 myDirectories
= myManager
.getCachedValuesManager().createCachedValue(new CachedValueProvider
<Collection
<PsiDirectory
>>() {
107 public Result
<Collection
<PsiDirectory
>> compute() {
108 return Result
.create(new DirectoriesSearch().search(GlobalSearchScope
.allScope(myManager
.getProject())).findAll(),
109 PsiModificationTracker
.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
, ProjectRootManager
.getInstance(getProject()));
113 return myDirectories
.getValue();
117 public PsiDirectory
[] getDirectories(@NotNull GlobalSearchScope scope
) {
118 final List
<PsiDirectory
> result
= new ArrayList
<PsiDirectory
>();
119 final Collection
<PsiDirectory
> directories
= getAllDirectories();
120 for (final PsiDirectory directory
: directories
) {
121 if (scope
.contains(directory
.getVirtualFile())) {
122 result
.add(directory
);
125 return result
.toArray(new PsiDirectory
[result
.size()]);
128 public RowIcon
getElementIcon(final int elementFlags
) {
129 return createLayeredIcon(Icons
.PACKAGE_ICON
, elementFlags
);
132 private class DirectoriesSearch
extends QueryFactory
<PsiDirectory
, GlobalSearchScope
> {
133 public DirectoriesSearch() {
134 registerExecutor(new QueryExecutor
<PsiDirectory
, GlobalSearchScope
>() {
135 public boolean execute(final GlobalSearchScope scope
, final Processor
<PsiDirectory
> consumer
) {
136 PackageIndex
.getInstance(getProject()).getDirsByPackageName(myQualifiedName
, false).forEach(new ReadActionProcessor
<VirtualFile
>() {
137 public boolean processInReadAction(final VirtualFile dir
) {
138 if (!scope
.contains(dir
)) return true;
139 PsiDirectory psiDir
= myManager
.findDirectory(dir
);
140 assert psiDir
!= null;
141 return consumer
.process(psiDir
);
149 public Query
<PsiDirectory
> search(GlobalSearchScope scope
) {
150 return createQuery(scope
);
154 public String
getName() {
155 if (DebugUtil
.CHECK_INSIDE_ATOMIC_ACTION_ENABLED
) {
156 ApplicationManager
.getApplication().assertReadAccessAllowed();
158 if (myQualifiedName
.length() == 0) return null;
159 int index
= myQualifiedName
.lastIndexOf('.');
161 return myQualifiedName
;
164 return myQualifiedName
.substring(index
+ 1);
169 public PsiElement
setName(@NotNull String name
) throws IncorrectOperationException
{
171 PsiDirectory
[] dirs
= getDirectories();
172 for (PsiDirectory dir
: dirs
) {
175 String nameAfterRename
= RenameUtil
.getQualifiedNameAfterRename(getQualifiedName(), name
);
176 return getFacade().findPackage(nameAfterRename
);
179 public void checkSetName(@NotNull String name
) throws IncorrectOperationException
{
180 PsiDirectory
[] dirs
= getDirectories();
181 for (PsiDirectory dir
: dirs
) {
182 dir
.checkSetName(name
);
186 public void handleQualifiedNameChange(@NotNull final String newQualifiedName
) {
187 ApplicationManager
.getApplication().assertWriteAccessAllowed();
188 final String oldQualifedName
= myQualifiedName
;
189 final boolean anyChanged
= changePackagePrefixes(oldQualifedName
, newQualifiedName
);
191 UndoManager
.getInstance(myManager
.getProject()).undoableActionPerformed(new UndoableAction() {
193 changePackagePrefixes(newQualifiedName
, oldQualifedName
);
197 changePackagePrefixes(oldQualifedName
, newQualifiedName
);
200 public DocumentReference
[] getAffectedDocuments() {
201 return DocumentReference
.EMPTY_ARRAY
;
204 public boolean isGlobal() {
211 private boolean changePackagePrefixes(final String oldQualifiedName
, final String newQualifiedName
) {
212 final Module
[] modules
= ModuleManager
.getInstance(myManager
.getProject()).getModules();
213 List
<ModifiableRootModel
> modelsToCommit
= new ArrayList
<ModifiableRootModel
>();
214 for (final Module module
: modules
) {
215 boolean anyChange
= false;
216 final ModifiableRootModel rootModel
= ModuleRootManager
.getInstance(module
).getModifiableModel();
217 final ContentEntry
[] contentEntries
= rootModel
.getContentEntries();
218 for (final ContentEntry contentEntry
: contentEntries
) {
219 final SourceFolder
[] sourceFolders
= contentEntry
.getSourceFolders();
220 for (final SourceFolder sourceFolder
: sourceFolders
) {
221 final String packagePrefix
= sourceFolder
.getPackagePrefix();
222 if (packagePrefix
.startsWith(oldQualifiedName
)) {
223 sourceFolder
.setPackagePrefix(newQualifiedName
+ packagePrefix
.substring(oldQualifiedName
.length()));
229 modelsToCommit
.add(rootModel
);
235 if (!modelsToCommit
.isEmpty()) {
236 ProjectRootManager
.getInstance(myManager
.getProject()).multiCommit(
237 modelsToCommit
.toArray(new ModifiableRootModel
[modelsToCommit
.size()])
245 public VirtualFile
[] occursInPackagePrefixes() {
246 List
<VirtualFile
> result
= new ArrayList
<VirtualFile
>();
247 final Module
[] modules
= ModuleManager
.getInstance(myManager
.getProject()).getModules();
249 for (final Module module
: modules
) {
250 final ContentEntry
[] contentEntries
= ModuleRootManager
.getInstance(module
).getContentEntries();
251 for (final ContentEntry contentEntry
: contentEntries
) {
252 final SourceFolder
[] sourceFolders
= contentEntry
.getSourceFolders();
253 for (final SourceFolder sourceFolder
: sourceFolders
) {
254 final String packagePrefix
= sourceFolder
.getPackagePrefix();
255 if (packagePrefix
.startsWith(myQualifiedName
)) {
256 final VirtualFile file
= sourceFolder
.getFile();
265 return result
.toArray(new VirtualFile
[result
.size()]);
268 public PsiPackage
getParentPackage() {
269 if (myQualifiedName
.length() == 0) return null;
270 int lastDot
= myQualifiedName
.lastIndexOf('.');
272 return new PsiPackageImpl(myManager
, "");
275 return new PsiPackageImpl(myManager
, myQualifiedName
.substring(0, lastDot
));
280 public Language
getLanguage() {
281 return StdFileTypes
.JAVA
.getLanguage();
284 public PsiManager
getManager() {
289 public PsiElement
[] getChildren() {
290 LOG
.error("method not implemented");
291 return PsiElement
.EMPTY_ARRAY
;
295 public PsiElement
getParent() {
296 return getParentPackage();
300 public PsiFile
getContainingFile() {
305 public TextRange
getTextRange() {
309 public int getStartOffsetInParent() {
313 public int getTextLength() {
317 public PsiElement
findElementAt(int offset
) {
321 public int getTextOffset() {
326 public String
getText() {
331 public char[] textToCharArray() {
332 return ArrayUtil
.EMPTY_CHAR_ARRAY
; // TODO throw new InsupportedOperationException()
335 public boolean textMatches(@NotNull CharSequence text
) {
339 public boolean textMatches(@NotNull PsiElement element
) {
343 public PsiElement
copy() {
344 LOG
.error("method not implemented");
348 public PsiElement
add(@NotNull PsiElement element
) throws IncorrectOperationException
{
349 throw new IncorrectOperationException();
352 public PsiElement
addBefore(@NotNull PsiElement element
, PsiElement anchor
) throws IncorrectOperationException
{
353 throw new IncorrectOperationException();
356 public PsiElement
addAfter(@NotNull PsiElement element
, PsiElement anchor
) throws IncorrectOperationException
{
357 throw new IncorrectOperationException();
360 public void checkAdd(@NotNull PsiElement element
) throws IncorrectOperationException
{
361 throw new IncorrectOperationException();
364 public void delete() throws IncorrectOperationException
{
366 PsiDirectory
[] dirs
= getDirectories();
367 for (PsiDirectory dir
: dirs
) {
372 public void checkDelete() throws IncorrectOperationException
{
373 for (PsiDirectory dir
: getDirectories()) {
378 public PsiElement
replace(@NotNull PsiElement newElement
) throws IncorrectOperationException
{
379 throw new IncorrectOperationException();
382 public boolean isValid() {
383 if (new DirectoriesSearch().search(GlobalSearchScope
.allScope(getProject())).findFirst() != null) return true;
384 return getFacade().packagePrefixExists(myQualifiedName
);
387 public boolean isWritable() {
388 PsiDirectory
[] dirs
= getDirectories();
389 for (PsiDirectory dir
: dirs
) {
390 if (!dir
.isWritable()) return false;
395 public void accept(@NotNull PsiElementVisitor visitor
) {
396 if (visitor
instanceof JavaElementVisitor
) {
397 ((JavaElementVisitor
)visitor
).visitPackage(this);
400 visitor
.visitElement(this);
404 public String
toString() {
405 return "PsiPackage:" + getQualifiedName();
409 public PsiClass
[] getClasses() {
410 return getClasses(GlobalSearchScope
.allScope(myManager
.getProject()));
414 public PsiClass
[] getClasses(@NotNull GlobalSearchScope scope
) {
415 return getFacade().getClasses(this, scope
);
419 public PsiModifierList
getAnnotationList() {
420 if (myAnnotationList
== null) {
421 myAnnotationList
= myManager
.getCachedValuesManager().createCachedValue(new PackageAnnotationValueProvider());
423 return myAnnotationList
.getValue();
427 public PsiPackage
[] getSubPackages() {
428 return getSubPackages(GlobalSearchScope
.allScope(myManager
.getProject()));
432 public PsiPackage
[] getSubPackages(@NotNull GlobalSearchScope scope
) {
433 return getFacade().getSubPackages(this, scope
);
436 private JavaPsiFacadeImpl
getFacade() {
437 return (JavaPsiFacadeImpl
)JavaPsiFacade
.getInstance(myManager
.getProject());
440 private Set
<String
> buildClassnamesCache() {
441 Set
<String
> classNames
= new THashSet
<String
>();
442 for (PsiClass aClass
: getClasses()) {
443 classNames
.add(aClass
.getName());
448 private Set
<String
> getClassNamesCache() {
449 if (myPublicClassNamesCache
== null) {
450 synchronized (myPublicClassNamesCacheLock
) {
451 if (myPublicClassNamesCache
== null) {
452 myPublicClassNamesCache
= buildClassnamesCache();
457 return myPublicClassNamesCache
;
461 private PsiClass
[] findClassesByName(String name
, GlobalSearchScope scope
) {
462 final String qName
= getQualifiedName();
463 final String classQName
= qName
.length() > 0 ? qName
+ "." + name
: name
;
464 return getFacade().findClasses(classQName
, scope
);
467 public boolean containsClassNamed(String name
) {
468 return getClassNamesCache().contains(name
);
472 private PsiPackage
findSubPackageByName(String name
) {
473 final String qName
= getQualifiedName();
474 final String subpackageQName
= qName
.length() > 0 ? qName
+ "." + name
: name
;
475 PsiPackage aPackage
= getFacade().findPackage(subpackageQName
);
476 if (aPackage
== null) return null;
477 //if (aPackage.getDirectories(scope).length == 0) return null;
481 public boolean processDeclarations(@NotNull PsiScopeProcessor processor
,
482 @NotNull ResolveState state
,
483 PsiElement lastParent
,
484 @NotNull PsiElement place
) {
485 GlobalSearchScope scope
= place
.getResolveScope();
487 processor
.handleEvent(PsiScopeProcessor
.Event
.SET_DECLARATION_HOLDER
, this);
488 ElementClassHint classHint
= processor
.getHint(ElementClassHint
.KEY
);
490 final JavaPsiFacadeImpl facade
= getFacade();
491 final PsiMigrationImpl migration
= facade
.getCurrentMigration();
493 final Condition
<String
> prefixMatcher
= processor
.getHint(JavaCompletionProcessor
.NAME_FILTER
);
495 if (classHint
== null || classHint
.shouldProcess(ElementClassHint
.DeclaractionKind
.CLASS
)) {
496 NameHint nameHint
= processor
.getHint(NameHint
.KEY
);
497 if (nameHint
!= null) {
498 if (processClassesByName(processor
, state
, place
, scope
, nameHint
.getName(state
))) return false;
500 else if (prefixMatcher
!= null && migration
== null) {
501 for (String className
: getClassNamesCache()) {
502 if (prefixMatcher
.value(className
)) {
503 if (processClassesByName(processor
, state
, place
, scope
, className
)) return false;
508 PsiClass
[] classes
= getClasses(scope
);
509 if (!processClasses(processor
, state
, place
, classes
)) return false;
510 if (migration
!= null) {
511 for (PsiClass psiClass
: migration
.getMigrationClasses(getQualifiedName())) {
512 if (!processor
.execute(psiClass
, state
)) {
519 if (classHint
== null || classHint
.shouldProcess(ElementClassHint
.DeclaractionKind
.PACKAGE
)) {
520 NameHint nameHint
= processor
.getHint(NameHint
.KEY
);
521 if (nameHint
!= null) {
522 PsiPackage aPackage
= findSubPackageByName(nameHint
.getName(state
));
523 if (aPackage
!= null) {
524 if (!processor
.execute(aPackage
, state
)) return false;
528 PsiPackage
[] packs
= getSubPackages(scope
);
529 for (PsiPackage pack
: packs
) {
530 final String packageName
= pack
.getName();
531 if (packageName
== null) continue;
532 if (!facade
.getNameHelper().isIdentifier(packageName
, PsiUtil
.getLanguageLevel(this))) {
535 if (!processor
.execute(pack
, state
)) {
540 if (migration
!= null) {
541 for (PsiPackage aPackage
: migration
.getMigrationPackages(getQualifiedName())) {
542 if (!processor
.execute(aPackage
, state
)) {
552 private boolean processClassesByName(PsiScopeProcessor processor
, ResolveState state
, PsiElement place
, GlobalSearchScope scope
, String className
) {
553 final PsiClass
[] classes
= findClassesByName(className
, scope
);
554 if (!processClasses(processor
, state
, place
, classes
)) return true;
558 private boolean processClasses(PsiScopeProcessor processor
, ResolveState state
, PsiElement place
, PsiClass
[] classes
) {
559 boolean placePhysical
= place
.isPhysical();
561 final PsiResolveHelper helper
= getFacade().getResolveHelper();
562 for (PsiClass aClass
: classes
) {
563 if (!placePhysical
|| helper
.isAccessible(aClass
, place
, null)) {
564 if (!processor
.execute(aClass
, state
)) return false;
570 public boolean canNavigate() {
574 public boolean canNavigateToSource() {
578 public void navigate(boolean requestFocus
) {
579 final ProjectView projectView
= ProjectView
.getInstance(getProject());
580 projectView
.changeView(PackageViewPane
.ID
);
581 final PsiDirectory
[] directories
= getDirectories();
582 final VirtualFile firstDir
= directories
[0].getVirtualFile();
583 final boolean isLibraryRoot
= ProjectRootsUtil
.isLibraryRoot(firstDir
, getProject());
585 final Module module
= ProjectRootManager
.getInstance(getProject()).getFileIndex().getModuleForFile(firstDir
);
586 final PackageElement packageElement
= new PackageElement(module
, this, isLibraryRoot
);
587 projectView
.getProjectViewPaneById(PackageViewPane
.ID
).select(packageElement
, firstDir
, requestFocus
);
588 ToolWindowManager
.getInstance(getProject()).getToolWindow(ToolWindowId
.PROJECT_VIEW
).activate(null);
591 public boolean isPhysical() {
595 public ASTNode
getNode() {
599 private class PackageAnnotationValueProvider
implements CachedValueProvider
<PsiModifierList
> {
600 @NonNls private static final String PACKAGE_INFO_FILE
= "package-info.java";
601 private final Object
[] OOCB_DEPENDENCY
= new Object
[] { PsiModificationTracker
.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
};
603 public Result
<PsiModifierList
> compute() {
604 List
<PsiModifierList
> list
= new ArrayList
<PsiModifierList
>();
605 for(PsiDirectory directory
: getDirectories()) {
606 PsiFile file
= directory
.findFile(PACKAGE_INFO_FILE
);
608 PsiPackageStatement stmt
= PsiTreeUtil
.getChildOfType(file
, PsiPackageStatement
.class);
610 final PsiModifierList modifierList
= stmt
.getAnnotationList();
611 if (modifierList
!= null) {
612 list
.add(modifierList
);
618 final JavaPsiFacadeImpl facade
= getFacade();
619 for (PsiClass aClass
: facade
.findClasses(getQualifiedName() + ".package-info", ProjectScope
.getAllScope(getProject()))) {
620 ContainerUtil
.addIfNotNull(aClass
.getModifierList(), list
);
623 return new Result
<PsiModifierList
>(list
.isEmpty() ?
null : new PsiCompositeModifierList(getManager(), list
), OOCB_DEPENDENCY
);
628 public PsiModifierList
getModifierList() {
629 return getAnnotationList();
632 public boolean hasModifierProperty(@NonNls @NotNull final String name
) {
636 public PsiQualifiedNamedElement
getContainer() {
637 return getParentPackage();