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        @Override
111        @NotNull
112        public List<JetClassInitializer> getAnonymousInitializers() {
113            JetClassBody body = getBody();
114            if (body == null) return Collections.emptyList();
115    
116            return body.getAnonymousInitializers();
117        }
118    
119        private boolean hasExplicitPrimaryConstructor() {
120            return getPrimaryConstructorParameterList() != null;
121        }
122    
123        @Override
124        public JetObjectDeclarationName getNameAsDeclaration() {
125            return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME);
126        }
127    
128        @Override
129        public JetClassBody getBody() {
130            return getStubOrPsiChild(JetStubElementTypes.CLASS_BODY);
131        }
132    
133        public List<JetProperty> getProperties() {
134            JetClassBody body = getBody();
135            if (body == null) return Collections.emptyList();
136    
137            return body.getProperties();
138        }
139    
140        public boolean isTrait() {
141            KotlinClassStub stub = getStub();
142            if (stub != null) {
143                return stub.isTrait();
144            }
145    
146            return findChildByType(JetTokens.TRAIT_KEYWORD) != null;
147        }
148    
149        public boolean isEnum() {
150            return hasModifier(JetTokens.ENUM_KEYWORD);
151        }
152    
153        public boolean isAnnotation() {
154            return hasModifier(JetTokens.ANNOTATION_KEYWORD);
155        }
156    
157        public boolean isInner() {
158            return hasModifier(JetTokens.INNER_KEYWORD);
159        }
160    
161        @Override
162        public void delete() throws IncorrectOperationException {
163            JetPsiUtil.deleteClass(this);
164        }
165    
166        @Override
167        public boolean isEquivalentTo(PsiElement another) {
168            if (super.isEquivalentTo(another)) {
169                return true;
170            }
171            if (another instanceof JetClass) {
172                String fq1 = getQualifiedName();
173                String fq2 = ((JetClass) another).getQualifiedName();
174                return fq1 != null && fq2 != null && fq1.equals(fq2);
175            }
176            return false;
177        }
178    
179        @Nullable
180        private String getQualifiedName() {
181            KotlinClassStub stub = getStub();
182            if (stub != null) {
183                FqName fqName = stub.getFqName();
184                return fqName == null ? null : fqName.asString();
185            }
186    
187            List<String> parts = new ArrayList<String>();
188            JetClassOrObject current = this;
189            while (current != null) {
190                parts.add(current.getName());
191                current = PsiTreeUtil.getParentOfType(current, JetClassOrObject.class);
192            }
193            PsiFile file = getContainingFile();
194            if (!(file instanceof JetFile)) return null;
195            String fileQualifiedName = ((JetFile) file).getPackageFqName().asString();
196            if (!fileQualifiedName.isEmpty()) {
197                parts.add(fileQualifiedName);
198            }
199            Collections.reverse(parts);
200            return StringUtil.join(parts, ".");
201        }
202    
203        @Override
204        public ItemPresentation getPresentation() {
205            return ItemPresentationProviders.getItemPresentation(this);
206        }
207    
208        @Override
209        public boolean isTopLevel() {
210            return getContainingFile() == getParent();
211        }
212    
213        @Override
214        public boolean isLocal() {
215            KotlinClassStub stub = getStub();
216            if (stub != null) {
217                return stub.isLocal();
218            }
219            return JetPsiUtil.isLocal(this);
220        }
221    
222        @NotNull
223        public List<JetObjectDeclaration> getCompanionObjects() {
224            JetClassBody body = getBody();
225            if (body == null) {
226                return Collections.emptyList();
227            }
228            return body.getAllCompanionObjects();
229        }
230    
231        public boolean hasPrimaryConstructor() {
232            return hasExplicitPrimaryConstructor() || !hasSecondaryConstructors();
233        }
234    
235        private boolean hasSecondaryConstructors() {
236            return !getSecondaryConstructors().isEmpty();
237        }
238    
239        @NotNull
240        public List<JetSecondaryConstructor> getSecondaryConstructors() {
241            JetClassBody body = getBody();
242            return body != null ? body.getSecondaryConstructors() : Collections.<JetSecondaryConstructor>emptyList();
243        }
244    }