001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.psi;
018    
019    import com.intellij.lang.ASTNode;
020    import com.intellij.navigation.ItemPresentation;
021    import com.intellij.navigation.ItemPresentationProviders;
022    import com.intellij.openapi.util.text.StringUtil;
023    import com.intellij.psi.PsiElement;
024    import com.intellij.psi.PsiFile;
025    import com.intellij.psi.util.PsiTreeUtil;
026    import com.intellij.util.IncorrectOperationException;
027    import org.jetbrains.annotations.NotNull;
028    import org.jetbrains.annotations.Nullable;
029    import org.jetbrains.kotlin.JetNodeTypes;
030    import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
031    import org.jetbrains.kotlin.lexer.JetTokens;
032    import org.jetbrains.kotlin.name.FqName;
033    import org.jetbrains.kotlin.psi.addRemoveModifier.AddRemoveModifierPackage;
034    import org.jetbrains.kotlin.psi.stubs.KotlinClassStub;
035    import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes;
036    
037    import java.util.ArrayList;
038    import java.util.Collections;
039    import java.util.List;
040    
041    public class JetClass extends JetTypeParameterListOwnerStub<KotlinClassStub> implements JetClassOrObject {
042    
043        public JetClass(@NotNull ASTNode node) {
044            super(node);
045        }
046    
047        public JetClass(@NotNull KotlinClassStub stub) {
048            super(stub, JetStubElementTypes.CLASS);
049        }
050    
051        @NotNull
052        @Override
053        public List<JetDeclaration> getDeclarations() {
054            JetClassBody body = getBody();
055            if (body == null) return Collections.emptyList();
056    
057            return body.getDeclarations();
058        }
059    
060        @Override
061        public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
062            return visitor.visitClass(this, data);
063        }
064    
065        @Nullable
066        public JetParameterList getPrimaryConstructorParameterList() {
067            return getStubOrPsiChild(JetStubElementTypes.VALUE_PARAMETER_LIST);
068        }
069    
070        @NotNull
071        public List<JetParameter> getPrimaryConstructorParameters() {
072            JetParameterList list = getPrimaryConstructorParameterList();
073            if (list == null) return Collections.emptyList();
074            return list.getParameters();
075        }
076    
077        @Override
078        @Nullable
079        public JetDelegationSpecifierList getDelegationSpecifierList() {
080            return getStubOrPsiChild(JetStubElementTypes.DELEGATION_SPECIFIER_LIST);
081        }
082    
083        @Override
084        @NotNull
085        public List<JetDelegationSpecifier> getDelegationSpecifiers() {
086            JetDelegationSpecifierList list = getDelegationSpecifierList();
087            return list != null ? list.getDelegationSpecifiers() : Collections.<JetDelegationSpecifier>emptyList();
088        }
089    
090        @Nullable
091        public JetModifierList getPrimaryConstructorModifierList() {
092            return getStubOrPsiChild(JetStubElementTypes.PRIMARY_CONSTRUCTOR_MODIFIER_LIST);
093        }
094    
095        public void addPrimaryConstructorModifier(@NotNull JetModifierKeywordToken modifier) {
096            JetModifierList modifierList = getPrimaryConstructorModifierList();
097            if (modifierList != null) {
098                AddRemoveModifierPackage.addModifier(modifierList, modifier, JetTokens.PUBLIC_KEYWORD);
099            }
100            else {
101                if (modifier == JetTokens.PUBLIC_KEYWORD) return;
102    
103                JetParameterList parameterList = getPrimaryConstructorParameterList();
104                assert parameterList != null;
105                JetModifierList newModifierList = new JetPsiFactory(getProject()).createModifierList(modifier);
106                addBefore(newModifierList, parameterList);
107            }
108        }
109    
110        public void removePrimaryConstructorModifier(@NotNull JetModifierKeywordToken modifier) {
111            JetModifierList list = getPrimaryConstructorModifierList();
112            if (list != null) {
113                PsiElement token = list.getModifier(modifier);
114                if (token != null) {
115                    token.delete();
116                }
117            }
118        }
119    
120        @Override
121        @NotNull
122        public List<JetClassInitializer> getAnonymousInitializers() {
123            JetClassBody body = getBody();
124            if (body == null) return Collections.emptyList();
125    
126            return body.getAnonymousInitializers();
127        }
128    
129        @Override
130        public boolean hasPrimaryConstructor() {
131            return getPrimaryConstructorParameterList() != null;
132        }
133    
134        @Override
135        public JetObjectDeclarationName getNameAsDeclaration() {
136            return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME);
137        }
138    
139        @Override
140        public JetClassBody getBody() {
141            return getStubOrPsiChild(JetStubElementTypes.CLASS_BODY);
142        }
143    
144        @Nullable
145        public JetClassObject getClassObject() {
146            JetClassBody body = getBody();
147            if (body == null) return null;
148            return body.getClassObject();
149        }
150    
151        public List<JetProperty> getProperties() {
152            JetClassBody body = getBody();
153            if (body == null) return Collections.emptyList();
154    
155            return body.getProperties();
156        }
157    
158        public boolean isTrait() {
159            KotlinClassStub stub = getStub();
160            if (stub != null) {
161                return stub.isTrait();
162            }
163    
164            return findChildByType(JetTokens.TRAIT_KEYWORD) != null;
165        }
166    
167        public boolean isEnum() {
168            return hasModifier(JetTokens.ENUM_KEYWORD);
169        }
170    
171        public boolean isAnnotation() {
172            return hasModifier(JetTokens.ANNOTATION_KEYWORD);
173        }
174    
175        public boolean isInner() {
176            return hasModifier(JetTokens.INNER_KEYWORD);
177        }
178    
179        @Override
180        public void delete() throws IncorrectOperationException {
181            JetPsiUtil.deleteClass(this);
182        }
183    
184        @Override
185        public boolean isEquivalentTo(PsiElement another) {
186            if (super.isEquivalentTo(another)) {
187                return true;
188            }
189            if (another instanceof JetClass) {
190                String fq1 = getQualifiedName();
191                String fq2 = ((JetClass) another).getQualifiedName();
192                return fq1 != null && fq2 != null && fq1.equals(fq2);
193            }
194            return false;
195        }
196    
197        @Nullable
198        private String getQualifiedName() {
199            KotlinClassStub stub = getStub();
200            if (stub != null) {
201                FqName fqName = stub.getFqName();
202                return fqName == null ? null : fqName.asString();
203            }
204    
205            List<String> parts = new ArrayList<String>();
206            JetClassOrObject current = this;
207            while (current != null) {
208                parts.add(current.getName());
209                current = PsiTreeUtil.getParentOfType(current, JetClassOrObject.class);
210            }
211            PsiFile file = getContainingFile();
212            if (!(file instanceof JetFile)) return null;
213            String fileQualifiedName = ((JetFile) file).getPackageFqName().asString();
214            if (!fileQualifiedName.isEmpty()) {
215                parts.add(fileQualifiedName);
216            }
217            Collections.reverse(parts);
218            return StringUtil.join(parts, ".");
219        }
220    
221        @Override
222        public ItemPresentation getPresentation() {
223            return ItemPresentationProviders.getItemPresentation(this);
224        }
225    
226        @Override
227        public boolean isTopLevel() {
228            return getContainingFile() == getParent();
229        }
230    
231        @Override
232        public boolean isLocal() {
233            KotlinClassStub stub = getStub();
234            if (stub != null) {
235                return stub.isLocal();
236            }
237            return JetPsiUtil.isLocal(this);
238        }
239    }