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.KotlinPackage;
030    import kotlin.jvm.functions.Function1;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.annotations.Nullable;
033    import org.jetbrains.kotlin.JetNodeTypes;
034    import org.jetbrains.kotlin.idea.JetFileType;
035    import org.jetbrains.kotlin.idea.JetLanguage;
036    import org.jetbrains.kotlin.name.FqName;
037    import org.jetbrains.kotlin.psi.stubs.KotlinFileStub;
038    import org.jetbrains.kotlin.psi.stubs.elements.JetPlaceHolderStubElementType;
039    import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes;
040    
041    import java.util.Arrays;
042    import java.util.Collections;
043    import java.util.List;
044    
045    public class JetFile extends PsiFileBase implements JetDeclarationContainer, JetAnnotated, JetElement, PsiClassOwner {
046    
047        private final boolean isCompiled;
048    
049        public JetFile(FileViewProvider viewProvider, boolean compiled) {
050            super(viewProvider, JetLanguage.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 JetFileType.INSTANCE;
067        }
068    
069        @Override
070        public String toString() {
071            return "JetFile: " + getName();
072        }
073    
074        @NotNull
075        @Override
076        public List<JetDeclaration> getDeclarations() {
077            KotlinFileStub stub = getStub();
078            if (stub != null) {
079                return Arrays.asList(stub.getChildrenByType(JetStubElementTypes.DECLARATION_TYPES, JetDeclaration.ARRAY_FACTORY));
080            }
081            return PsiTreeUtil.getChildrenOfTypeAsList(this, JetDeclaration.class);
082        }
083    
084        @Nullable
085        public JetImportList getImportList() {
086            return findChildByTypeOrClass(JetStubElementTypes.IMPORT_LIST, JetImportList.class);
087        }
088    
089        @Nullable
090        public JetFileAnnotationList getFileAnnotationList() {
091            return findChildByTypeOrClass(JetStubElementTypes.FILE_ANNOTATION_LIST, JetFileAnnotationList.class);
092        }
093    
094        @Nullable
095        public <T extends JetElementImplStub<? extends StubElement<?>>> T findChildByTypeOrClass(
096                @NotNull JetPlaceHolderStubElementType<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<JetImportDirective> getImportDirectives() {
109            JetImportList importList = getImportList();
110            return importList != null ? importList.getImports() : Collections.<JetImportDirective>emptyList();
111        }
112    
113        @Nullable
114        public JetImportDirective findImportByAlias(@NotNull String name) {
115            for (JetImportDirective 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 JetPackageDirective getPackageDirective() {
126            KotlinFileStub stub = getStub();
127            if (stub != null) {
128                StubElement<JetPackageDirective> packageDirectiveStub = stub.findChildStubByType(JetStubElementTypes.PACKAGE_DIRECTIVE);
129                return packageDirectiveStub != null ? packageDirectiveStub.getPsi() : null;
130            }
131            ASTNode ast = getNode().findChildByType(JetNodeTypes.PACKAGE_DIRECTIVE);
132            return ast != null ? (JetPackageDirective) 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            JetPackageDirective 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        // SCRIPT: find script in file
176        @Nullable
177        public JetScript getScript() {
178            return PsiTreeUtil.getChildOfType(this, JetScript.class);
179        }
180    
181        public boolean isScript() {
182            KotlinFileStub stub = getStub();
183            if (stub != null) {
184                return stub.isScript();
185            }
186            return isScriptByTree();
187        }
188    
189        public boolean isScriptByTree() {
190            return getScript() != null;
191        }
192    
193        @NotNull
194        @Override
195        public String getName() {
196            return super.getName(); // TODO
197        }
198    
199        @Override
200        public void accept(@NotNull PsiElementVisitor visitor) {
201            if (visitor instanceof JetVisitor) {
202                accept((JetVisitor) visitor, null);
203            }
204            else {
205                visitor.visitFile(this);
206            }
207        }
208    
209        @NotNull
210        @Override
211        public JetFile getContainingJetFile() {
212            return this;
213        }
214    
215        @Override
216        public <D> void acceptChildren(@NotNull JetVisitor<Void, D> visitor, D data) {
217            JetPsiUtil.visitChildren(this, visitor, data);
218        }
219    
220        @Override
221        public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
222            return visitor.visitJetFile(this, data);
223        }
224    
225        @NotNull
226        @Override
227        public List<JetAnnotation> getAnnotations() {
228            JetFileAnnotationList fileAnnotationList = getFileAnnotationList();
229            if (fileAnnotationList == null) return Collections.emptyList();
230    
231            return fileAnnotationList.getAnnotations();
232        }
233    
234        @NotNull
235        @Override
236        public List<JetAnnotationEntry> getAnnotationEntries() {
237            JetFileAnnotationList fileAnnotationList = getFileAnnotationList();
238            if (fileAnnotationList == null) return Collections.emptyList();
239    
240            return fileAnnotationList.getAnnotationEntries();
241        }
242    
243        /**
244         * @return annotations that do not belong to any declaration due to incomplete code or syntax errors
245         */
246        @NotNull
247        public List<JetAnnotationEntry> getDanglingAnnotations() {
248            KotlinFileStub stub = getStub();
249            JetModifierList[] danglingModifierLists = stub == null
250                                                      ? findChildrenByClass(JetModifierList.class)
251                                                      : stub.getChildrenByType(
252                                                              JetStubElementTypes.MODIFIER_LIST,
253                                                              JetStubElementTypes.MODIFIER_LIST.getArrayFactory()
254                                                      );
255            return KotlinPackage.flatMap(
256                    danglingModifierLists,
257                    new Function1<JetModifierList, Iterable<JetAnnotationEntry>>() {
258                        @Override
259                        public Iterable<JetAnnotationEntry> invoke(JetModifierList modifierList) {
260                            return modifierList.getAnnotationEntries();
261                        }
262                    });
263        }
264    }