001/*
002 * Copyright 2010-2013 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
017package org.jetbrains.jet.lang.psi;
018
019import com.intellij.lang.ASTNode;
020import com.intellij.navigation.ItemPresentation;
021import com.intellij.navigation.ItemPresentationProviders;
022import com.intellij.psi.PsiElement;
023import com.intellij.psi.stubs.IStubElementType;
024import com.intellij.psi.tree.IElementType;
025import com.intellij.psi.util.PsiTreeUtil;
026import org.jetbrains.annotations.NotNull;
027import org.jetbrains.annotations.Nullable;
028import org.jetbrains.jet.JetNodeTypes;
029import org.jetbrains.jet.lang.psi.stubs.PsiJetFunctionStub;
030import org.jetbrains.jet.lang.psi.stubs.elements.JetStubElementTypes;
031import org.jetbrains.jet.lang.resolve.name.FqName;
032import org.jetbrains.jet.lang.resolve.name.Name;
033import org.jetbrains.jet.lexer.JetTokens;
034
035import java.util.Collections;
036import java.util.List;
037
038public class JetNamedFunction extends JetTypeParameterListOwnerStub<PsiJetFunctionStub> implements JetFunction, JetWithExpressionInitializer {
039    public JetNamedFunction(@NotNull ASTNode node) {
040        super(node);
041    }
042
043    public JetNamedFunction(@NotNull PsiJetFunctionStub stub) {
044        super(stub, JetStubElementTypes.FUNCTION);
045    }
046
047    @Override
048    public void accept(@NotNull JetVisitorVoid visitor) {
049        visitor.visitNamedFunction(this);
050    }
051
052    @Override
053    public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
054        return visitor.visitNamedFunction(this, data);
055    }
056
057    public boolean hasTypeParameterListBeforeFunctionName() {
058        JetTypeParameterList typeParameterList = getTypeParameterList();
059        if (typeParameterList == null) {
060            return false;
061        }
062        PsiElement nameIdentifier = getNameIdentifier();
063        if (nameIdentifier == null) {
064            return false;
065        }
066        return nameIdentifier.getTextOffset() > typeParameterList.getTextOffset();
067    }
068
069    @Override
070    public boolean hasBlockBody() {
071        return getEqualsToken() == null;
072    }
073
074    @Nullable
075    public PsiElement getEqualsToken() {
076        return findChildByType(JetTokens.EQ);
077    }
078
079    @Override
080    @Nullable
081    public JetExpression getInitializer() {
082        return PsiTreeUtil.getNextSiblingOfType(getEqualsToken(), JetExpression.class);
083    }
084
085    /**
086    * Returns full qualified name for function "package_fqn.function_name"
087    * Not null for top level functions unless syntax errors are present.
088    * @return
089    */
090    @Nullable
091    public FqName getFqName() {
092        PsiJetFunctionStub stub = getStub();
093        if (stub != null) {
094            return stub.getTopFQName();
095        }
096
097        PsiElement parent = getParent();
098        if (parent instanceof JetFile) {
099            // fqname is different in scripts
100            if (((JetFile) parent).getNamespaceHeader() == null) {
101                return null;
102            }
103            JetFile jetFile = (JetFile) parent;
104            FqName fileFQN = JetPsiUtil.getFQName(jetFile);
105            Name nameAsName = getNameAsName();
106            if (nameAsName != null) {
107                return fileFQN.child(nameAsName);
108            }
109        }
110
111        return null;
112    }
113
114    @NotNull
115    @Override
116    public IStubElementType getElementType() {
117        return JetStubElementTypes.FUNCTION;
118    }
119    
120    @Override
121    public ItemPresentation getPresentation() {
122        return ItemPresentationProviders.getItemPresentation(this);
123    }
124
125    @Override
126    @Nullable
127    public JetParameterList getValueParameterList() {
128        return (JetParameterList) findChildByType(JetNodeTypes.VALUE_PARAMETER_LIST);
129    }
130
131    @Override
132    @NotNull
133    public List<JetParameter> getValueParameters() {
134        JetParameterList list = getValueParameterList();
135        return list != null ? list.getParameters() : Collections.<JetParameter>emptyList();
136    }
137
138    @Override
139    @Nullable
140    public JetExpression getBodyExpression() {
141        return findChildByClass(JetExpression.class);
142    }
143
144    @Override
145    public boolean hasDeclaredReturnType() {
146        return getReturnTypeRef() != null;
147    }
148
149    @Override
150    @Nullable
151    public JetTypeReference getReceiverTypeRef() {
152        PsiElement child = getFirstChild();
153        while (child != null) {
154            IElementType tt = child.getNode().getElementType();
155            if (tt == JetTokens.LPAR || tt == JetTokens.COLON) break;
156            if (child instanceof JetTypeReference) {
157                return (JetTypeReference) child;
158            }
159            child = child.getNextSibling();
160        }
161
162        return null;
163    }
164
165    @Override
166    @Nullable
167    public JetTypeReference getReturnTypeRef() {
168        boolean colonPassed = false;
169        PsiElement child = getFirstChild();
170        while (child != null) {
171            IElementType tt = child.getNode().getElementType();
172            if (tt == JetTokens.COLON) {
173                colonPassed = true;
174            }
175            if (colonPassed && child instanceof JetTypeReference) {
176                return (JetTypeReference) child;
177            }
178            child = child.getNextSibling();
179        }
180
181        return null;
182    }
183
184    @NotNull
185    @Override
186    public JetElement asElement() {
187        return this;
188    }
189
190    @Override
191    public boolean isLocal() {
192        PsiElement parent = getParent();
193        return !(parent instanceof JetFile || parent instanceof JetClassBody);
194    }
195}