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    
017    package org.jetbrains.jet.lang.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.stubs.IStubElementType;
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.jet.JetNodeTypes;
031    import org.jetbrains.jet.lang.psi.stubs.PsiJetClassStub;
032    import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes;
033    import org.jetbrains.jet.lang.resolve.name.FqName;
034    import org.jetbrains.jet.lexer.JetTokens;
035    
036    import java.util.ArrayList;
037    import java.util.Collections;
038    import java.util.List;
039    
040    public 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    }