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.extapi.psi.PsiFileBase;
020    import com.intellij.lang.ASTNode;
021    import com.intellij.lang.FileASTNode;
022    import com.intellij.openapi.components.ServiceManager;
023    import com.intellij.openapi.fileTypes.FileType;
024    import com.intellij.psi.*;
025    import com.intellij.psi.stubs.StubElement;
026    import com.intellij.psi.util.PsiTreeUtil;
027    import kotlin.collections.ArraysKt;
028    import kotlin.jvm.functions.Function1;
029    import org.jetbrains.annotations.NotNull;
030    import org.jetbrains.annotations.Nullable;
031    import org.jetbrains.kotlin.KtNodeTypes;
032    import org.jetbrains.kotlin.idea.KotlinFileType;
033    import org.jetbrains.kotlin.idea.KotlinLanguage;
034    import org.jetbrains.kotlin.name.FqName;
035    import org.jetbrains.kotlin.psi.stubs.KotlinFileStub;
036    import org.jetbrains.kotlin.psi.stubs.elements.KtPlaceHolderStubElementType;
037    import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
038    
039    import java.util.Arrays;
040    import java.util.Collections;
041    import java.util.List;
042    
043    public class KtFile extends PsiFileBase implements KtDeclarationContainer, KtAnnotated, KtElement, PsiClassOwner, PsiNamedElement {
044    
045        private final boolean isCompiled;
046    
047        public KtFile(FileViewProvider viewProvider, boolean compiled) {
048            super(viewProvider, KotlinLanguage.INSTANCE);
049            this.isCompiled = compiled;
050        }
051    
052        @Override
053        public FileASTNode getNode() {
054            return super.getNode();
055        }
056    
057        public boolean isCompiled() {
058            return isCompiled;
059        }
060    
061        @Override
062        @NotNull
063        public FileType getFileType() {
064            return KotlinFileType.INSTANCE;
065        }
066    
067        @Override
068        public String toString() {
069            return "JetFile: " + getName();
070        }
071    
072        @NotNull
073        @Override
074        public List<KtDeclaration> getDeclarations() {
075            KotlinFileStub stub = getStub();
076            if (stub != null) {
077                return Arrays.asList(stub.getChildrenByType(KtStubElementTypes.DECLARATION_TYPES, KtDeclaration.ARRAY_FACTORY));
078            }
079            return PsiTreeUtil.getChildrenOfTypeAsList(this, KtDeclaration.class);
080        }
081    
082        @Nullable
083        public KtImportList getImportList() {
084            return findChildByTypeOrClass(KtStubElementTypes.IMPORT_LIST, KtImportList.class);
085        }
086    
087        @Nullable
088        public KtFileAnnotationList getFileAnnotationList() {
089            return findChildByTypeOrClass(KtStubElementTypes.FILE_ANNOTATION_LIST, KtFileAnnotationList.class);
090        }
091    
092        @Nullable
093        public <T extends KtElementImplStub<? extends StubElement<?>>> T findChildByTypeOrClass(
094                @NotNull KtPlaceHolderStubElementType<T> elementType,
095                @NotNull Class<T> elementClass
096        ) {
097            KotlinFileStub stub = getStub();
098            if (stub != null) {
099                StubElement<T> importListStub = stub.findChildStubByType(elementType);
100                return importListStub != null ? importListStub.getPsi() : null;
101            }
102            return findChildByClass(elementClass);
103        }
104    
105        @NotNull
106        public List<KtImportDirective> getImportDirectives() {
107            KtImportList importList = getImportList();
108            return importList != null ? importList.getImports() : Collections.<KtImportDirective>emptyList();
109        }
110    
111        @Nullable
112        public KtImportDirective findImportByAlias(@NotNull String name) {
113            for (KtImportDirective directive : getImportDirectives()) {
114                if (name.equals(directive.getAliasName())) {
115                    return directive;
116                }
117            }
118            return null;
119        }
120    
121        // scripts have no package directive, all other files must have package directives
122        @Nullable
123        public KtPackageDirective getPackageDirective() {
124            KotlinFileStub stub = getStub();
125            if (stub != null) {
126                StubElement<KtPackageDirective> packageDirectiveStub = stub.findChildStubByType(KtStubElementTypes.PACKAGE_DIRECTIVE);
127                return packageDirectiveStub != null ? packageDirectiveStub.getPsi() : null;
128            }
129            ASTNode ast = getNode().findChildByType(KtNodeTypes.PACKAGE_DIRECTIVE);
130            return ast != null ? (KtPackageDirective) ast.getPsi() : null;
131        }
132    
133        @Deprecated // getPackageFqName should be used instead
134        @Override
135        @NotNull
136        public String getPackageName() {
137            return getPackageFqName().asString();
138        }
139    
140        @NotNull
141        public FqName getPackageFqName() {
142            KotlinFileStub stub = getStub();
143            if (stub != null) {
144                return stub.getPackageFqName();
145            }
146            return getPackageFqNameByTree();
147        }
148    
149        @NotNull
150        public FqName getPackageFqNameByTree() {
151            KtPackageDirective packageDirective = getPackageDirective();
152            if (packageDirective == null) {
153                return FqName.ROOT;
154            }
155            return packageDirective.getFqName();
156        }
157    
158        @Override
159        @Nullable
160        public KotlinFileStub getStub() {
161            return (KotlinFileStub) super.getStub();
162        }
163    
164        @NotNull
165        @Override
166        public PsiClass[] getClasses() {
167            KtFileClassProvider fileClassProvider = ServiceManager.getService(getProject(), KtFileClassProvider.class);
168            // TODO We don't currently support finding light classes for scripts
169            if (fileClassProvider != null && !isScript()) {
170                return fileClassProvider.getFileClasses(this);
171            }
172            return PsiClass.EMPTY_ARRAY;
173        }
174    
175        @Override
176        public void setPackageName(String packageName) { }
177    
178        @Nullable
179        public KtScript getScript() {
180            return PsiTreeUtil.getChildOfType(this, KtScript.class);
181        }
182    
183        public boolean isScript() {
184            KotlinFileStub stub = getStub();
185            if (stub != null) {
186                return stub.isScript();
187            }
188            return isScriptByTree();
189        }
190    
191        public boolean isScriptByTree() {
192            return getScript() != null;
193        }
194    
195        @NotNull
196        @Override
197        public String getName() {
198            return super.getName(); // TODO
199        }
200    
201        @Override
202        public void accept(@NotNull PsiElementVisitor visitor) {
203            if (visitor instanceof KtVisitor) {
204                accept((KtVisitor) visitor, null);
205            }
206            else {
207                visitor.visitFile(this);
208            }
209        }
210    
211        @NotNull
212        @Override
213        public KtFile getContainingKtFile() {
214            return this;
215        }
216    
217        @Override
218        public <D> void acceptChildren(@NotNull KtVisitor<Void, D> visitor, D data) {
219            KtPsiUtil.visitChildren(this, visitor, data);
220        }
221    
222        @Override
223        public <R, D> R accept(@NotNull KtVisitor<R, D> visitor, D data) {
224            return visitor.visitKtFile(this, data);
225        }
226    
227        @NotNull
228        @Override
229        public List<KtAnnotation> getAnnotations() {
230            KtFileAnnotationList fileAnnotationList = getFileAnnotationList();
231            if (fileAnnotationList == null) return Collections.emptyList();
232    
233            return fileAnnotationList.getAnnotations();
234        }
235    
236        @NotNull
237        @Override
238        public List<KtAnnotationEntry> getAnnotationEntries() {
239            KtFileAnnotationList fileAnnotationList = getFileAnnotationList();
240            if (fileAnnotationList == null) return Collections.emptyList();
241    
242            return fileAnnotationList.getAnnotationEntries();
243        }
244    
245        /**
246         * @return annotations that do not belong to any declaration due to incomplete code or syntax errors
247         */
248        @NotNull
249        public List<KtAnnotationEntry> getDanglingAnnotations() {
250            KotlinFileStub stub = getStub();
251            KtModifierList[] danglingModifierLists = stub == null
252                                                      ? findChildrenByClass(KtModifierList.class)
253                                                      : stub.getChildrenByType(
254                                                              KtStubElementTypes.MODIFIER_LIST,
255                                                              KtStubElementTypes.MODIFIER_LIST.getArrayFactory()
256                                                      );
257            return ArraysKt.flatMap(
258                    danglingModifierLists,
259                    new Function1<KtModifierList, Iterable<KtAnnotationEntry>>() {
260                        @Override
261                        public Iterable<KtAnnotationEntry> invoke(KtModifierList modifierList) {
262                            return modifierList.getAnnotationEntries();
263                        }
264                    });
265        }
266    }