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        public List<JetProperty> getProperties() {
145            JetClassBody body = getBody();
146            if (body == null) return Collections.emptyList();
147    
148            return body.getProperties();
149        }
150    
151        public boolean isTrait() {
152            KotlinClassStub stub = getStub();
153            if (stub != null) {
154                return stub.isTrait();
155            }
156    
157            return findChildByType(JetTokens.TRAIT_KEYWORD) != null;
158        }
159    
160        public boolean isEnum() {
161            return hasModifier(JetTokens.ENUM_KEYWORD);
162        }
163    
164        public boolean isAnnotation() {
165            return hasModifier(JetTokens.ANNOTATION_KEYWORD);
166        }
167    
168        public boolean isInner() {
169            return hasModifier(JetTokens.INNER_KEYWORD);
170        }
171    
172        @Override
173        public void delete() throws IncorrectOperationException {
174            JetPsiUtil.deleteClass(this);
175        }
176    
177        @Override
178        public boolean isEquivalentTo(PsiElement another) {
179            if (super.isEquivalentTo(another)) {
180                return true;
181            }
182            if (another instanceof JetClass) {
183                String fq1 = getQualifiedName();
184                String fq2 = ((JetClass) another).getQualifiedName();
185                return fq1 != null && fq2 != null && fq1.equals(fq2);
186            }
187            return false;
188        }
189    
190        @Nullable
191        private String getQualifiedName() {
192            KotlinClassStub stub = getStub();
193            if (stub != null) {
194                FqName fqName = stub.getFqName();
195                return fqName == null ? null : fqName.asString();
196            }
197    
198            List<String> parts = new ArrayList<String>();
199            JetClassOrObject current = this;
200            while (current != null) {
201                parts.add(current.getName());
202                current = PsiTreeUtil.getParentOfType(current, JetClassOrObject.class);
203            }
204            PsiFile file = getContainingFile();
205            if (!(file instanceof JetFile)) return null;
206            String fileQualifiedName = ((JetFile) file).getPackageFqName().asString();
207            if (!fileQualifiedName.isEmpty()) {
208                parts.add(fileQualifiedName);
209            }
210            Collections.reverse(parts);
211            return StringUtil.join(parts, ".");
212        }
213    
214        @Override
215        public ItemPresentation getPresentation() {
216            return ItemPresentationProviders.getItemPresentation(this);
217        }
218    
219        @Override
220        public boolean isTopLevel() {
221            return getContainingFile() == getParent();
222        }
223    
224        @Override
225        public boolean isLocal() {
226            KotlinClassStub stub = getStub();
227            if (stub != null) {
228                return stub.isLocal();
229            }
230            return JetPsiUtil.isLocal(this);
231        }
232    
233        @NotNull
234        public List<JetObjectDeclaration> getDefaultObjects() {
235            JetClassBody body = getBody();
236            if (body == null) {
237                return Collections.emptyList();
238            }
239            return body.getAllDefaultObjects();
240        }
241    }