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