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