update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / file / PsiPackageImpl.java
blob7d00c952f9c5697c526d75e179f918869e22f3d8
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.
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;
65 import java.util.Set;
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) {
79 myManager = manager;
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();
93 @NotNull
94 public String getQualifiedName() {
95 return myQualifiedName;
98 @NotNull
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()));
111 }, false);
113 return myDirectories.getValue();
116 @NotNull
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);
144 return true;
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('.');
160 if (index < 0) {
161 return myQualifiedName;
163 else {
164 return myQualifiedName.substring(index + 1);
168 @Nullable
169 public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
170 checkSetName(name);
171 PsiDirectory[] dirs = getDirectories();
172 for (PsiDirectory dir : dirs) {
173 dir.setName(name);
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);
190 if (anyChanged) {
191 UndoManager.getInstance(myManager.getProject()).undoableActionPerformed(new UndoableAction() {
192 public void undo() {
193 changePackagePrefixes(newQualifiedName, oldQualifedName);
196 public void redo() {
197 changePackagePrefixes(oldQualifedName, newQualifiedName);
200 public DocumentReference[] getAffectedDocuments() {
201 return DocumentReference.EMPTY_ARRAY;
204 public boolean isGlobal() {
205 return true;
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()));
224 anyChange = true;
228 if (anyChange) {
229 modelsToCommit.add(rootModel);
230 } else {
231 rootModel.dispose();
235 if (!modelsToCommit.isEmpty()) {
236 ProjectRootManager.getInstance(myManager.getProject()).multiCommit(
237 modelsToCommit.toArray(new ModifiableRootModel[modelsToCommit.size()])
239 return true;
240 } else {
241 return false;
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();
257 if (file != null) {
258 result.add(file);
265 return result.toArray(new VirtualFile[result.size()]);
268 public PsiPackage getParentPackage() {
269 if (myQualifiedName.length() == 0) return null;
270 int lastDot = myQualifiedName.lastIndexOf('.');
271 if (lastDot < 0) {
272 return new PsiPackageImpl(myManager, "");
274 else {
275 return new PsiPackageImpl(myManager, myQualifiedName.substring(0, lastDot));
279 @NotNull
280 public Language getLanguage() {
281 return StdFileTypes.JAVA.getLanguage();
284 public PsiManager getManager() {
285 return myManager;
288 @NotNull
289 public PsiElement[] getChildren() {
290 LOG.error("method not implemented");
291 return PsiElement.EMPTY_ARRAY;
294 @Nullable
295 public PsiElement getParent() {
296 return getParentPackage();
299 @Nullable
300 public PsiFile getContainingFile() {
301 return null;
304 @Nullable
305 public TextRange getTextRange() {
306 return null;
309 public int getStartOffsetInParent() {
310 return -1;
313 public int getTextLength() {
314 return -1;
317 public PsiElement findElementAt(int offset) {
318 return null;
321 public int getTextOffset() {
322 return -1;
325 @Nullable
326 public String getText() {
327 return null;
330 @NotNull
331 public char[] textToCharArray() {
332 return ArrayUtil.EMPTY_CHAR_ARRAY; // TODO throw new InsupportedOperationException()
335 public boolean textMatches(@NotNull CharSequence text) {
336 return false;
339 public boolean textMatches(@NotNull PsiElement element) {
340 return false;
343 public PsiElement copy() {
344 LOG.error("method not implemented");
345 return null;
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 {
365 checkDelete();
366 PsiDirectory[] dirs = getDirectories();
367 for (PsiDirectory dir : dirs) {
368 dir.delete();
372 public void checkDelete() throws IncorrectOperationException {
373 for (PsiDirectory dir : getDirectories()) {
374 dir.checkDelete();
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;
392 return true;
395 public void accept(@NotNull PsiElementVisitor visitor) {
396 if (visitor instanceof JavaElementVisitor) {
397 ((JavaElementVisitor)visitor).visitPackage(this);
399 else {
400 visitor.visitElement(this);
404 public String toString() {
405 return "PsiPackage:" + getQualifiedName();
408 @NotNull
409 public PsiClass[] getClasses() {
410 return getClasses(GlobalSearchScope.allScope(myManager.getProject()));
413 @NotNull
414 public PsiClass[] getClasses(@NotNull GlobalSearchScope scope) {
415 return getFacade().getClasses(this, scope);
418 @Nullable
419 public PsiModifierList getAnnotationList() {
420 if (myAnnotationList == null) {
421 myAnnotationList = myManager.getCachedValuesManager().createCachedValue(new PackageAnnotationValueProvider());
423 return myAnnotationList.getValue();
426 @NotNull
427 public PsiPackage[] getSubPackages() {
428 return getSubPackages(GlobalSearchScope.allScope(myManager.getProject()));
431 @NotNull
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());
445 return classNames;
448 private Set<String> getClassNamesCache() {
449 if (myPublicClassNamesCache == null) {
450 synchronized (myPublicClassNamesCacheLock) {
451 if (myPublicClassNamesCache == null) {
452 myPublicClassNamesCache = buildClassnamesCache();
457 return myPublicClassNamesCache;
460 @NotNull
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);
471 @Nullable
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;
478 return aPackage;
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;
507 else {
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)) {
513 return false;
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;
527 else {
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))) {
533 continue;
535 if (!processor.execute(pack, state)) {
536 return false;
540 if (migration != null) {
541 for (PsiPackage aPackage : migration.getMigrationPackages(getQualifiedName())) {
542 if (!processor.execute(aPackage, state)) {
543 return false;
549 return true;
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;
555 return false;
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;
567 return true;
570 public boolean canNavigate() {
571 return isValid();
574 public boolean canNavigateToSource() {
575 return false;
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() {
592 return true;
595 public ASTNode getNode() {
596 return null;
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);
607 if (file != null) {
608 PsiPackageStatement stmt = PsiTreeUtil.getChildOfType(file, PsiPackageStatement.class);
609 if (stmt != null) {
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);
627 @Nullable
628 public PsiModifierList getModifierList() {
629 return getAnnotationList();
632 public boolean hasModifierProperty(@NonNls @NotNull final String name) {
633 return false;
636 public PsiQualifiedNamedElement getContainer() {
637 return getParentPackage();