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.lang.ASTNode;
020    import com.intellij.navigation.ItemPresentation;
021    import com.intellij.navigation.ItemPresentationProviders;
022    import com.intellij.psi.PsiElement;
023    import com.intellij.psi.PsiModifiableCodeBlock;
024    import com.intellij.psi.tree.IElementType;
025    import com.intellij.psi.util.PsiTreeUtil;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.kotlin.lexer.JetTokens;
029    import org.jetbrains.kotlin.psi.stubs.KotlinFunctionStub;
030    import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes;
031    import org.jetbrains.kotlin.psi.typeRefHelpers.TypeRefHelpersPackage;
032    
033    import java.util.Collections;
034    import java.util.List;
035    
036    public class JetNamedFunction extends JetTypeParameterListOwnerStub<KotlinFunctionStub>
037            implements JetFunction, JetWithExpressionInitializer, PsiModifiableCodeBlock {
038        public JetNamedFunction(@NotNull ASTNode node) {
039            super(node);
040        }
041    
042        public JetNamedFunction(@NotNull KotlinFunctionStub stub) {
043            super(stub, JetStubElementTypes.FUNCTION);
044        }
045    
046        @Override
047        public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
048            return visitor.visitNamedFunction(this, data);
049        }
050    
051        public boolean hasTypeParameterListBeforeFunctionName() {
052            KotlinFunctionStub stub = getStub();
053            if (stub != null) {
054                return stub.hasTypeParameterListBeforeFunctionName();
055            }
056            return hasTypeParameterListBeforeFunctionNameByTree();
057        }
058    
059        private boolean hasTypeParameterListBeforeFunctionNameByTree() {
060            JetTypeParameterList typeParameterList = getTypeParameterList();
061            if (typeParameterList == null) {
062                return false;
063            }
064            PsiElement nameIdentifier = getNameIdentifier();
065            if (nameIdentifier == null) {
066                return false;
067            }
068            return nameIdentifier.getTextOffset() > typeParameterList.getTextOffset();
069        }
070    
071        @Override
072        public boolean hasBlockBody() {
073            KotlinFunctionStub stub = getStub();
074            if (stub != null) {
075                return stub.hasBlockBody();
076            }
077            return getEqualsToken() == null;
078        }
079    
080        @NotNull
081        public PsiElement getFunToken() {
082            PsiElement element = findChildByType(JetTokens.FUN_KEYWORD);
083            assert element != null : "'fun' must be present: " + JetPsiUtil.getElementTextWithContext(this);
084            return element;
085        }
086    
087        @Override
088        @Nullable
089        public PsiElement getEqualsToken() {
090            return findChildByType(JetTokens.EQ);
091        }
092    
093        @Override
094        @Nullable
095        public JetExpression getInitializer() {
096            return PsiTreeUtil.getNextSiblingOfType(getEqualsToken(), JetExpression.class);
097        }
098    
099        @Override
100        public boolean hasInitializer() {
101            return getInitializer() != null;
102        }
103    
104        @Override
105        public ItemPresentation getPresentation() {
106            return ItemPresentationProviders.getItemPresentation(this);
107        }
108    
109        @Override
110        @Nullable
111        public JetParameterList getValueParameterList() {
112            return getStubOrPsiChild(JetStubElementTypes.VALUE_PARAMETER_LIST);
113        }
114    
115        @Override
116        @NotNull
117        public List<JetParameter> getValueParameters() {
118            JetParameterList list = getValueParameterList();
119            return list != null ? list.getParameters() : Collections.<JetParameter>emptyList();
120        }
121    
122        @Override
123        @Nullable
124        public JetExpression getBodyExpression() {
125            return findChildByClass(JetExpression.class);
126        }
127    
128        @Override
129        public boolean hasBody() {
130            KotlinFunctionStub stub = getStub();
131            if (stub != null) {
132                return stub.hasBody();
133            }
134            return getBodyExpression() != null;
135        }
136    
137        @Override
138        public boolean hasDeclaredReturnType() {
139            return getTypeReference() != null;
140        }
141    
142        @Override
143        @Nullable
144        public JetTypeReference getReceiverTypeReference() {
145            KotlinFunctionStub stub = getStub();
146            if (stub != null) {
147                if (!stub.isExtension()) {
148                    return null;
149                }
150                List<JetTypeReference> childTypeReferences = getStubOrPsiChildrenAsList(JetStubElementTypes.TYPE_REFERENCE);
151                if (!childTypeReferences.isEmpty()) {
152                    return childTypeReferences.get(0);
153                }
154                else {
155                    return null;
156                }
157            }
158            return getReceiverTypeRefByTree();
159        }
160    
161        @Nullable
162        private JetTypeReference getReceiverTypeRefByTree() {
163            PsiElement child = getFirstChild();
164            while (child != null) {
165                IElementType tt = child.getNode().getElementType();
166                if (tt == JetTokens.LPAR || tt == JetTokens.COLON) break;
167                if (child instanceof JetTypeReference) {
168                    return (JetTypeReference) child;
169                }
170                child = child.getNextSibling();
171            }
172    
173            return null;
174        }
175    
176        @Override
177        @Nullable
178        public JetTypeReference getTypeReference() {
179            KotlinFunctionStub stub = getStub();
180            if (stub != null) {
181                List<JetTypeReference> typeReferences = getStubOrPsiChildrenAsList(JetStubElementTypes.TYPE_REFERENCE);
182                int returnTypeIndex = stub.isExtension() ? 1 : 0;
183                if (returnTypeIndex >= typeReferences.size()) {
184                    return null;
185                }
186                return typeReferences.get(returnTypeIndex);
187            }
188            return TypeRefHelpersPackage.getTypeReference(this);
189        }
190    
191        @Override
192        @Nullable
193        public JetTypeReference setTypeReference(@Nullable JetTypeReference typeRef) {
194            return TypeRefHelpersPackage.setTypeReference(this, getValueParameterList(), typeRef);
195        }
196    
197        @Nullable
198        @Override
199        public PsiElement getColon() {
200            return findChildByType(JetTokens.COLON);
201        }
202    
203        @Override
204        public boolean isLocal() {
205            PsiElement parent = getParent();
206            return !(parent instanceof JetFile || parent instanceof JetClassBody);
207        }
208    
209        @Override
210        public boolean shouldChangeModificationCount(PsiElement place) {
211            // Suppress Java check for out-of-block
212            return false;
213        }
214    }