| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.psi.impl.source; |
| |
| import com.intellij.lang.ASTNode; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.psi.*; |
| import com.intellij.psi.augment.PsiAugmentProvider; |
| import com.intellij.psi.impl.CheckUtil; |
| import com.intellij.psi.impl.PsiImplUtil; |
| import com.intellij.psi.impl.cache.ModifierFlags; |
| import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; |
| import com.intellij.psi.impl.java.stubs.PsiModifierListStub; |
| import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; |
| import com.intellij.psi.impl.source.tree.CompositeElement; |
| import com.intellij.psi.impl.source.tree.Factory; |
| import com.intellij.psi.impl.source.tree.TreeElement; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.ContainerUtil; |
| import gnu.trove.THashMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| public class PsiModifierListImpl extends JavaStubPsiElement<PsiModifierListStub> implements PsiModifierList { |
| private static final Map<String, IElementType> NAME_TO_KEYWORD_TYPE_MAP; |
| static { |
| NAME_TO_KEYWORD_TYPE_MAP = new THashMap<String, IElementType>(); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.PUBLIC, JavaTokenType.PUBLIC_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.PROTECTED, JavaTokenType.PROTECTED_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.PRIVATE, JavaTokenType.PRIVATE_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.STATIC, JavaTokenType.STATIC_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.ABSTRACT, JavaTokenType.ABSTRACT_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.FINAL, JavaTokenType.FINAL_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.NATIVE, JavaTokenType.NATIVE_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.SYNCHRONIZED, JavaTokenType.SYNCHRONIZED_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.STRICTFP, JavaTokenType.STRICTFP_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.TRANSIENT, JavaTokenType.TRANSIENT_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.VOLATILE, JavaTokenType.VOLATILE_KEYWORD); |
| NAME_TO_KEYWORD_TYPE_MAP.put(PsiModifier.DEFAULT, JavaTokenType.DEFAULT_KEYWORD); |
| } |
| |
| public PsiModifierListImpl(final PsiModifierListStub stub) { |
| super(stub, JavaStubElementTypes.MODIFIER_LIST); |
| } |
| |
| public PsiModifierListImpl(final ASTNode node) { |
| super(node); |
| } |
| |
| @Override |
| public boolean hasModifierProperty(@NotNull String name) { |
| final PsiModifierListStub stub = getStub(); |
| if (stub != null) { |
| return ModifierFlags.hasModifierProperty(name, stub.getModifiersMask()); |
| } |
| |
| IElementType type = NAME_TO_KEYWORD_TYPE_MAP.get(name); |
| |
| PsiElement parent = getParent(); |
| if (parent instanceof PsiClass) { |
| PsiElement grandParent = parent.getParent(); |
| if (grandParent instanceof PsiClass && ((PsiClass)grandParent).isInterface()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD) { |
| return true; |
| } |
| if (type == null /* package local */) { |
| return false; |
| } |
| if (type == JavaTokenType.STATIC_KEYWORD) { |
| return true; |
| } |
| } |
| if (((PsiClass)parent).isInterface()) { |
| if (type == JavaTokenType.ABSTRACT_KEYWORD) { |
| return true; |
| } |
| |
| // nested interface is implicitly static |
| if (grandParent instanceof PsiClass) { |
| if (type == JavaTokenType.STATIC_KEYWORD) { |
| return true; |
| } |
| } |
| } |
| if (((PsiClass)parent).isEnum()) { |
| if (type == JavaTokenType.STATIC_KEYWORD) { |
| if (!(grandParent instanceof PsiFile)) return true; |
| } |
| else if (type == JavaTokenType.FINAL_KEYWORD) { |
| final PsiField[] fields = ((PsiClass)parent).getFields(); |
| for (PsiField field : fields) { |
| if (field instanceof PsiEnumConstant && ((PsiEnumConstant)field).getInitializingClass() != null) return false; |
| } |
| return true; |
| } |
| else if (type == JavaTokenType.ABSTRACT_KEYWORD) { |
| final PsiMethod[] methods = ((PsiClass)parent).getMethods(); |
| for (PsiMethod method : methods) { |
| if (method.hasModifierProperty(PsiModifier.ABSTRACT)) return true; |
| } |
| return false; |
| } |
| } |
| } |
| else if (parent instanceof PsiMethod) { |
| PsiClass aClass = ((PsiMethod)parent).getContainingClass(); |
| if (aClass != null && aClass.isInterface()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD) { |
| return true; |
| } |
| if (type == null /* package local */) { |
| return false; |
| } |
| if (type == JavaTokenType.ABSTRACT_KEYWORD) { |
| return getNode().findChildByType(JavaTokenType.DEFAULT_KEYWORD) == null && getNode().findChildByType(JavaTokenType.STATIC_KEYWORD) == null; |
| } |
| } |
| } |
| else if (parent instanceof PsiField) { |
| if (parent instanceof PsiEnumConstant) { |
| return type == JavaTokenType.PUBLIC_KEYWORD || type == JavaTokenType.STATIC_KEYWORD || type == JavaTokenType.FINAL_KEYWORD; |
| } |
| else { |
| PsiClass aClass = ((PsiField)parent).getContainingClass(); |
| if (aClass != null && aClass.isInterface()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD) { |
| return true; |
| } |
| if (type == null /* package local */) { |
| return false; |
| } |
| if (type == JavaTokenType.STATIC_KEYWORD) { |
| return true; |
| } |
| if (type == JavaTokenType.FINAL_KEYWORD) { |
| return true; |
| } |
| } |
| } |
| } |
| else if (parent instanceof PsiParameter) { |
| if (type == JavaTokenType.FINAL_KEYWORD && ((PsiParameter)parent).getType() instanceof PsiDisjunctionType) return true; |
| } |
| else if (parent instanceof PsiResourceVariable) { |
| if (type == JavaTokenType.FINAL_KEYWORD) return true; |
| } |
| |
| if (type == null /* package local */) { |
| return !hasModifierProperty(PsiModifier.PUBLIC) && |
| !hasModifierProperty(PsiModifier.PRIVATE) && |
| !hasModifierProperty(PsiModifier.PROTECTED); |
| } |
| |
| return getNode().findChildByType(type) != null; |
| } |
| |
| @Override |
| public boolean hasExplicitModifier(@NotNull String name) { |
| final CompositeElement tree = (CompositeElement)getNode(); |
| final IElementType type = NAME_TO_KEYWORD_TYPE_MAP.get(name); |
| return tree.findChildByType(type) != null; |
| } |
| |
| @Override |
| public void setModifierProperty(@NotNull String name, boolean value) throws IncorrectOperationException{ |
| checkSetModifierProperty(name, value); |
| |
| PsiElement parent = getParent(); |
| PsiElement grandParent = parent != null ? parent.getParent() : null; |
| IElementType type = NAME_TO_KEYWORD_TYPE_MAP.get(name); |
| CompositeElement treeElement = (CompositeElement)getNode(); |
| |
| // There is a possible case that parameters list occupies more than one line and its elements are aligned. Modifiers list change |
| // changes horizontal position of parameters list start, hence, we need to reformat them in order to preserve alignment. |
| if (parent instanceof PsiMethod) { |
| PsiMethod method = (PsiMethod)parent; |
| CodeEditUtil.markToReformat(method.getParameterList().getNode(), true); |
| } |
| |
| if (value) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD || |
| type == JavaTokenType.PRIVATE_KEYWORD || |
| type == JavaTokenType.PROTECTED_KEYWORD || |
| type == null /* package local */) { |
| if (type != JavaTokenType.PUBLIC_KEYWORD) { |
| setModifierProperty(PsiModifier.PUBLIC, false); |
| } |
| if (type != JavaTokenType.PRIVATE_KEYWORD) { |
| setModifierProperty(PsiModifier.PRIVATE, false); |
| } |
| if (type != JavaTokenType.PROTECTED_KEYWORD) { |
| setModifierProperty(PsiModifier.PROTECTED, false); |
| } |
| if (type == null) return; |
| } |
| |
| if (parent instanceof PsiField && grandParent instanceof PsiClass && ((PsiClass)grandParent).isInterface()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD || type == JavaTokenType.STATIC_KEYWORD || type == JavaTokenType.FINAL_KEYWORD) return; |
| } |
| else if (parent instanceof PsiMethod && grandParent instanceof PsiClass && ((PsiClass)grandParent).isInterface()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD || type == JavaTokenType.ABSTRACT_KEYWORD) return; |
| } |
| else if (parent instanceof PsiClass && grandParent instanceof PsiClass && ((PsiClass)grandParent).isInterface()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD) return; |
| } |
| else if (parent instanceof PsiAnnotationMethod && grandParent instanceof PsiClass && ((PsiClass)grandParent).isAnnotationType()) { |
| if (type == JavaTokenType.PUBLIC_KEYWORD || type == JavaTokenType.ABSTRACT_KEYWORD) return; |
| } |
| |
| if (treeElement.findChildByType(type) == null) { |
| TreeElement keyword = Factory.createSingleLeafElement(type, name, null, getManager()); |
| treeElement.addInternal(keyword, keyword, null, null); |
| } |
| } |
| else { |
| if (type == null /* package local */) { |
| throw new IncorrectOperationException("Cannot reset package local modifier."); //? |
| } |
| |
| ASTNode child = treeElement.findChildByType(type); |
| if (child != null) { |
| SourceTreeToPsiMap.treeToPsiNotNull(child).delete(); |
| } |
| } |
| } |
| |
| @Override |
| public void checkSetModifierProperty(@NotNull String name, boolean value) throws IncorrectOperationException{ |
| CheckUtil.checkWritable(this); |
| } |
| |
| @Override |
| @NotNull |
| public PsiAnnotation[] getAnnotations() { |
| final PsiAnnotation[] own = getStubOrPsiChildren(JavaStubElementTypes.ANNOTATION, PsiAnnotation.ARRAY_FACTORY); |
| final List<PsiAnnotation> ext = PsiAugmentProvider.collectAugments(this, PsiAnnotation.class); |
| return ArrayUtil.mergeArrayAndCollection(own, ext, PsiAnnotation.ARRAY_FACTORY); |
| } |
| |
| @Override |
| @NotNull |
| public PsiAnnotation[] getApplicableAnnotations() { |
| final PsiAnnotation.TargetType[] targets = PsiImplUtil.getTargetsForLocation(this); |
| List<PsiAnnotation> filtered = ContainerUtil.findAll(getAnnotations(), new Condition<PsiAnnotation>() { |
| @Override |
| public boolean value(PsiAnnotation annotation) { |
| PsiAnnotation.TargetType target = PsiImplUtil.findApplicableTarget(annotation, targets); |
| return target != null && target != PsiAnnotation.TargetType.UNKNOWN; |
| } |
| }); |
| |
| return filtered.toArray(new PsiAnnotation[filtered.size()]); |
| } |
| |
| @Override |
| public PsiAnnotation findAnnotation(@NotNull String qualifiedName) { |
| return PsiImplUtil.findAnnotation(this, qualifiedName); |
| } |
| |
| @Override |
| @NotNull |
| public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { |
| return (PsiAnnotation)addAfter(JavaPsiFacade.getInstance(getProject()).getElementFactory().createAnnotationFromText("@" + qualifiedName, this), null); |
| } |
| |
| @Override |
| public void accept(@NotNull PsiElementVisitor visitor){ |
| if (visitor instanceof JavaElementVisitor) { |
| ((JavaElementVisitor)visitor).visitModifierList(this); |
| } |
| else { |
| visitor.visitElement(this); |
| } |
| } |
| |
| public String toString(){ |
| return "PsiModifierList:" + getText(); |
| } |
| } |