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.tree.TokenSet;
026    import com.intellij.psi.util.PsiTreeUtil;
027    import com.intellij.util.IncorrectOperationException;
028    import org.jetbrains.annotations.NotNull;
029    import org.jetbrains.annotations.Nullable;
030    import org.jetbrains.kotlin.JetNodeTypes;
031    import org.jetbrains.kotlin.lexer.JetTokens;
032    import org.jetbrains.kotlin.name.FqName;
033    import org.jetbrains.kotlin.psi.stubs.KotlinClassStub;
034    import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes;
035    
036    import java.util.ArrayList;
037    import java.util.Collections;
038    import java.util.List;
039    
040    public class JetClass extends JetTypeParameterListOwnerStub<KotlinClassStub> implements JetClassOrObject {
041    
042        public JetClass(@NotNull ASTNode node) {
043            super(node);
044        }
045    
046        public JetClass(@NotNull KotlinClassStub stub) {
047            super(stub, JetStubElementTypes.CLASS);
048        }
049    
050        @NotNull
051        @Override
052        public List<JetDeclaration> getDeclarations() {
053            JetClassBody body = getBody();
054            if (body == null) return Collections.emptyList();
055    
056            return body.getDeclarations();
057        }
058    
059        @Override
060        public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
061            return visitor.visitClass(this, data);
062        }
063    
064        @Nullable
065        public JetPrimaryConstructor getPrimaryConstructor() {
066            return getStubOrPsiChild(JetStubElementTypes.PRIMARY_CONSTRUCTOR);
067        }
068    
069        @Nullable
070        public JetParameterList getPrimaryConstructorParameterList() {
071            JetPrimaryConstructor primaryConstructor = getPrimaryConstructor();
072            return primaryConstructor != null ? primaryConstructor.getValueParameterList() : null;
073        }
074    
075        @NotNull
076        public List<JetParameter> getPrimaryConstructorParameters() {
077            JetParameterList list = getPrimaryConstructorParameterList();
078            if (list == null) return Collections.emptyList();
079            return list.getParameters();
080        }
081    
082        @NotNull
083        public JetPrimaryConstructor createPrimaryConstructorIfAbsent() {
084            JetPrimaryConstructor constructor = getPrimaryConstructor();
085            if (constructor != null) return constructor;
086            PsiElement anchor = getTypeParameterList();
087            if (anchor == null) anchor = getNameIdentifier();
088            if (anchor == null) anchor = getLastChild();
089            return (JetPrimaryConstructor) addAfter(new JetPsiFactory(getProject()).createPrimaryConstructor(), anchor);
090        }
091    
092        @NotNull
093        public JetParameterList createPrimaryConstructorParameterListIfAbsent() {
094            JetPrimaryConstructor constructor = createPrimaryConstructorIfAbsent();
095            JetParameterList parameterList = constructor.getValueParameterList();
096            if (parameterList != null) return parameterList;
097            return (JetParameterList) constructor.add(new JetPsiFactory(getProject()).createParameterList("()"));
098        }
099    
100        @Override
101        @Nullable
102        public JetDelegationSpecifierList getDelegationSpecifierList() {
103            return getStubOrPsiChild(JetStubElementTypes.DELEGATION_SPECIFIER_LIST);
104        }
105    
106        @Override
107        @NotNull
108        public List<JetDelegationSpecifier> getDelegationSpecifiers() {
109            JetDelegationSpecifierList list = getDelegationSpecifierList();
110            return list != null ? list.getDelegationSpecifiers() : Collections.<JetDelegationSpecifier>emptyList();
111        }
112    
113        @Nullable
114        public JetModifierList getPrimaryConstructorModifierList() {
115            JetPrimaryConstructor primaryConstructor = getPrimaryConstructor();
116            return primaryConstructor != null ? primaryConstructor.getModifierList() : null;
117        }
118    
119        @Override
120        @NotNull
121        public List<JetClassInitializer> getAnonymousInitializers() {
122            JetClassBody body = getBody();
123            if (body == null) return Collections.emptyList();
124    
125            return body.getAnonymousInitializers();
126        }
127    
128        public boolean hasExplicitPrimaryConstructor() {
129            return getPrimaryConstructor() != null;
130        }
131    
132        @Override
133        public JetObjectDeclarationName getNameAsDeclaration() {
134            return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME);
135        }
136    
137        @Override
138        public JetClassBody getBody() {
139            return getStubOrPsiChild(JetStubElementTypes.CLASS_BODY);
140        }
141    
142        @Nullable
143        public PsiElement getColon() {
144            return findChildByType(JetTokens.COLON);
145        }
146    
147        public List<JetProperty> getProperties() {
148            JetClassBody body = getBody();
149            if (body == null) return Collections.emptyList();
150    
151            return body.getProperties();
152        }
153    
154        public boolean isInterface() {
155            KotlinClassStub stub = getStub();
156            if (stub != null) {
157                return stub.isInterface();
158            }
159    
160            return findChildByType(JetTokens.TRAIT_KEYWORD) != null ||
161                   findChildByType(JetTokens.INTERFACE_KEYWORD) != null;
162        }
163    
164        public boolean isEnum() {
165            return hasModifier(JetTokens.ENUM_KEYWORD);
166        }
167    
168        public boolean isAnnotation() {
169            return hasModifier(JetTokens.ANNOTATION_KEYWORD);
170        }
171    
172        public boolean isInner() {
173            return hasModifier(JetTokens.INNER_KEYWORD);
174        }
175    
176        @Override
177        public boolean isEquivalentTo(PsiElement another) {
178            if (super.isEquivalentTo(another)) {
179                return true;
180            }
181            if (another instanceof JetClass) {
182                String fq1 = getQualifiedName();
183                String fq2 = ((JetClass) another).getQualifiedName();
184                return fq1 != null && fq2 != null && fq1.equals(fq2);
185            }
186            return false;
187        }
188    
189        @Nullable
190        private String getQualifiedName() {
191            KotlinClassStub stub = getStub();
192            if (stub != null) {
193                FqName fqName = stub.getFqName();
194                return fqName == null ? null : fqName.asString();
195            }
196    
197            List<String> parts = new ArrayList<String>();
198            JetClassOrObject current = this;
199            while (current != null) {
200                parts.add(current.getName());
201                current = PsiTreeUtil.getParentOfType(current, JetClassOrObject.class);
202            }
203            PsiFile file = getContainingFile();
204            if (!(file instanceof JetFile)) return null;
205            String fileQualifiedName = ((JetFile) file).getPackageFqName().asString();
206            if (!fileQualifiedName.isEmpty()) {
207                parts.add(fileQualifiedName);
208            }
209            Collections.reverse(parts);
210            return StringUtil.join(parts, ".");
211        }
212    
213        @Override
214        public ItemPresentation getPresentation() {
215            return ItemPresentationProviders.getItemPresentation(this);
216        }
217    
218        @Override
219        public boolean isTopLevel() {
220            return getContainingFile() == getParent();
221        }
222    
223        @Override
224        public boolean isLocal() {
225            KotlinClassStub stub = getStub();
226            if (stub != null) {
227                return stub.isLocal();
228            }
229            return JetPsiUtil.isLocal(this);
230        }
231    
232        @NotNull
233        public List<JetObjectDeclaration> getCompanionObjects() {
234            JetClassBody body = getBody();
235            if (body == null) {
236                return Collections.emptyList();
237            }
238            return body.getAllCompanionObjects();
239        }
240    
241        public boolean hasPrimaryConstructor() {
242            return hasExplicitPrimaryConstructor() || !hasSecondaryConstructors();
243        }
244    
245        private boolean hasSecondaryConstructors() {
246            return !getSecondaryConstructors().isEmpty();
247        }
248    
249        @NotNull
250        public List<JetSecondaryConstructor> getSecondaryConstructors() {
251            JetClassBody body = getBody();
252            return body != null ? body.getSecondaryConstructors() : Collections.<JetSecondaryConstructor>emptyList();
253        }
254    
255        @Nullable
256        public PsiElement getClassOrInterfaceKeyword() {
257            return findChildByType(TokenSet.create(JetTokens.CLASS_KEYWORD, JetTokens.INTERFACE_KEYWORD));
258        }
259    }