001/*
002 * Copyright 2010-2013 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
017package org.jetbrains.jet.lang.psi;
018
019import com.intellij.lang.ASTNode;
020import com.intellij.navigation.ItemPresentation;
021import com.intellij.navigation.ItemPresentationProviders;
022import com.intellij.openapi.util.text.StringUtil;
023import com.intellij.psi.PsiElement;
024import com.intellij.psi.PsiFile;
025import com.intellij.psi.stubs.IStubElementType;
026import com.intellij.psi.util.PsiTreeUtil;
027import com.intellij.util.IncorrectOperationException;
028import org.jetbrains.annotations.NotNull;
029import org.jetbrains.annotations.Nullable;
030import org.jetbrains.jet.JetNodeTypes;
031import org.jetbrains.jet.lang.psi.stubs.PsiJetClassStub;
032import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes;
033import org.jetbrains.jet.lang.resolve.name.FqName;
034import org.jetbrains.jet.lexer.JetTokens;
035
036import java.util.ArrayList;
037import java.util.Collections;
038import java.util.List;
039
040public class JetClass extends JetTypeParameterListOwnerStub<PsiJetClassStub> implements JetClassOrObject {
041
042    public JetClass(@NotNull ASTNode node) {
043        super(node);
044    }
045
046    public JetClass(@NotNull PsiJetClassStub stub) {
047        super(stub, JetStubElementTypes.CLASS);
048    }
049
050    @NotNull
051    @Override
052    public List<JetDeclaration> getDeclarations() {
053        JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
054        if (body == null) return Collections.emptyList();
055
056        return body.getDeclarations();
057    }
058
059    @Override
060    public void accept(@NotNull JetVisitorVoid visitor) {
061        visitor.visitClass(this);
062    }
063
064    @Override
065    public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
066        return visitor.visitClass(this, data);
067    }
068
069    @Nullable
070    public JetParameterList getPrimaryConstructorParameterList() {
071        return (JetParameterList) findChildByType(JetNodeTypes.VALUE_PARAMETER_LIST);
072    }
073
074    @NotNull
075    public List<JetParameter> getPrimaryConstructorParameters() {
076        JetParameterList list = getPrimaryConstructorParameterList();
077        if (list == null) return Collections.emptyList();
078        return list.getParameters();
079    }
080
081    @Override
082    @Nullable
083    public JetDelegationSpecifierList getDelegationSpecifierList() {
084        return (JetDelegationSpecifierList) findChildByType(JetNodeTypes.DELEGATION_SPECIFIER_LIST);
085    }
086
087    @Override
088    @NotNull
089    public List<JetDelegationSpecifier> getDelegationSpecifiers() {
090        JetDelegationSpecifierList list = getDelegationSpecifierList();
091        return list != null ? list.getDelegationSpecifiers() : Collections.<JetDelegationSpecifier>emptyList();
092    }
093
094    @Nullable
095    public JetModifierList getPrimaryConstructorModifierList() {
096        return (JetModifierList) findChildByType(JetNodeTypes.PRIMARY_CONSTRUCTOR_MODIFIER_LIST);
097    }
098
099    @Override
100    @NotNull
101    public List<JetClassInitializer> getAnonymousInitializers() {
102        JetClassBody body = getBody();
103        if (body == null) return Collections.emptyList();
104
105        return body.getAnonymousInitializers();
106    }
107
108    @Override
109    public boolean hasPrimaryConstructor() {
110        return getPrimaryConstructorParameterList() != null;
111    }
112
113    @Override
114    public JetObjectDeclarationName getNameAsDeclaration() {
115        return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME);
116    }
117
118    @Override
119    public JetClassBody getBody() {
120        return (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
121    }
122
123    @Nullable
124    public JetClassObject getClassObject() {
125        JetClassBody body = getBody();
126        if (body == null) return null;
127        return body.getClassObject();
128    }
129
130    public List<JetProperty> getProperties() {
131        JetClassBody body = getBody();
132        if (body == null) return Collections.emptyList();
133
134        return body.getProperties();
135    }
136
137    public boolean isTrait() {
138        PsiJetClassStub stub = getStub();
139        if (stub != null) {
140            return stub.isTrait();
141        }
142
143        return findChildByType(JetTokens.TRAIT_KEYWORD) != null;
144    }
145
146    public boolean isEnum() {
147        PsiJetClassStub stub = getStub();
148        if (stub != null) {
149            return stub.isEnumClass();
150        }
151
152        return hasModifier(JetTokens.ENUM_KEYWORD);
153    }
154
155    public boolean isAnnotation() {
156        PsiJetClassStub stub = getStub();
157        if (stub != null) {
158            return stub.isAnnotation();
159        }
160
161        return hasModifier(JetTokens.ANNOTATION_KEYWORD);
162    }
163
164    public boolean isInner() {
165        PsiJetClassStub stub = getStub();
166        if (stub != null) {
167            return stub.isInner();
168        }
169
170        return hasModifier(JetTokens.INNER_KEYWORD);
171    }
172
173    @NotNull
174    @Override
175    public IStubElementType getElementType() {
176        return JetStubElementTypes.CLASS;
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        PsiJetClassStub 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).getNamespaceHeader().getQualifiedName();
214        if (!fileQualifiedName.isEmpty()) {
215            parts.add(fileQualifiedName);
216        }
217        Collections.reverse(parts);
218        return StringUtil.join(parts, ".");
219    }
220
221    /**
222     * Returns the list of unqualified names that are indexed as the superclass names of this class. For the names that might be imported
223     * via an aliased import, includes both the original and the aliased name (reference resolution during inheritor search will sort this out).
224     *
225     * @return the list of possible superclass names
226     */
227    @NotNull
228    public List<String> getSuperNames() {
229        PsiJetClassStub stub = getStub();
230        if (stub != null) {
231            return stub.getSuperNames();
232        }
233
234        List<JetDelegationSpecifier> specifiers = getDelegationSpecifiers();
235        if (specifiers.size() == 0) return Collections.emptyList();
236        List<String> result = new ArrayList<String>();
237        for (JetDelegationSpecifier specifier : specifiers) {
238            JetUserType superType = specifier.getTypeAsUserType();
239            if (superType != null) {
240                String referencedName = superType.getReferencedName();
241                if (referencedName != null) {
242                    addSuperName(result, referencedName);
243                }
244            }
245        }
246        return result;
247    }
248
249    private void addSuperName(List<String> result, String referencedName) {
250        result.add(referencedName);
251        if (getContainingFile() instanceof JetFile) {
252            JetImportDirective directive = ((JetFile) getContainingFile()).findImportByAlias(referencedName);
253            if (directive != null) {
254                JetExpression reference = directive.getImportedReference();
255                while (reference instanceof JetDotQualifiedExpression) {
256                    reference = ((JetDotQualifiedExpression) reference).getSelectorExpression();
257                }
258                if (reference instanceof JetSimpleNameExpression) {
259                    result.add(((JetSimpleNameExpression) reference).getReferencedName());
260                }
261            }
262        }
263    }
264
265    @Override
266    public ItemPresentation getPresentation() {
267        return ItemPresentationProviders.getItemPresentation(this);
268    }
269}