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