/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2014-2014 TiVo Inc.
* Copyright 2014-2014 AS3Boyan
* Copyright 2014-2014 Elias Ku
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.plugins.haxe.lang.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.model.HaxeMethodModel;
import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import org.apache.log4j.Level;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author: Srikanth.Ganapavarapu
*/
public abstract class HaxeMethodPsiMixinImpl extends AbstractHaxeNamedComponent implements HaxeMethodPsiMixin {
private static final Logger LOG = Logger.getInstance("#com.intellij.plugins.haxe.lang.psi.impl.HaxeMethodPsiMixinImpl");
static {
LOG.info("Loaded HaxeMethodPsiMixinImpl");
LOG.setLevel(Level.DEBUG);
}
public HaxeMethodPsiMixinImpl(ASTNode node) {
super(node);
}
@Override
@Nullable @NonNls
public String getName() {
String name = super.getName();
if (null == name) {
if (getText().contains(" function new(")) {
return HaxeTokenTypes.ONEW.toString();
}
else {
final PsiIdentifier nameIdentifier = getNameIdentifier();
if (nameIdentifier != null) {
name = nameIdentifier.getText();
}
}
}
return (name != null) ? name : "<unnamed>";
}
private HaxeMethodModel _model = null;
public HaxeMethodModel getModel() {
if (_model == null) _model = new HaxeMethodModel(this);
return _model;
}
@Nullable
@Override
public List<HaxeDeclarationAttribute> getDeclarationAttributeList() {
// Not all function types have one of these... If they do, the
// subclass (via the generator) will override this method.
return null;
}
@Nullable
public HaxeReturnStatement getReturnStatement() {
// Not all function types have one of these... If they do, the
// subclass (via the generator) will override this method.
return findChildByClass(HaxeReturnStatement.class);
}
@Nullable
public HaxeTypeTag getTypeTag() {
// Not all function types have one of these... If they do, the
// subclass (via the generator) will override this method.
return findChildByClass(HaxeTypeTag.class);
}
@Nullable
@Override
public PsiType getReturnType() {
if (isConstructor()) {
return null;
}
// A HaxeFunctionType is a PSI Element.
// HaxeFunctionType type = getTypeTag().getFunctionType(); // type could be null
/* TODO: : 'public PsiType getReturnType()': translate above objects into PsiType */
return null;
}
@Nullable
@Override
public PsiTypeElement getReturnTypeElement() {
/* TODO: : 'public PsiType getReturnType()': translate below objects into PsiTypeElement */
// return getTypeTag();
return null;
}
@Nullable
public HaxeThrowStatement getThrowStatement() {
// Not all function types have one of these... If they do, the
// subclass (via the generator) will override this method.
return null;
}
@NotNull
@Override
public PsiReferenceList getThrowsList() {
HaxeThrowStatement ts = getThrowStatement();
return new HaxePsiReferenceList(this.getContainingClass(),
(ts == null ? new HaxeDummyASTNode("ThrowsList") : ts.getNode()),
null);
}
@Nullable
public HaxeBlockStatement getBlockStatement() {
// Not all function types have one of these... If they do, the
// subclass (via the generator) will override this method.
return null;
}
@Nullable
@Override
public PsiCodeBlock getBody() {
HaxeBlockStatement bs = getBlockStatement();
if (bs == null) {
return null;
}
return (PsiCodeBlock)bs.getNode().getPsi(PsiCodeBlock.class);
}
@Override
public boolean isConstructor() {
if (super.getName()==null &&
getComponentName()==null &&
getText().contains("function new(") &&
!isStatic() &&
!isOverride()) {
return true;
}
return false;
}
@Nullable
@Override
public PsiDocComment getDocComment() {
// TODO: Fix 'public PsiDocComment getDocComment()'
//PsiComment psiComment = HaxeResolveUtil.findDocumentation(this);
//return ((psiComment != null)? new HaxePsiDocComment(this, psiComment) : null);
return null;
}
@Override
public boolean isVarArgs() {
// In Haxe, the method is set to VarArgs at runtime, via a function call.
// We would need the ability to know if a particular run sequence has
// called such a function. I don't think we can pull that off without
// the compiler's help.
// TODO: Use compiler completion to detect variable arguments usage.
return false;
}
@Override
public boolean isDeprecated() {
return false;
}
@Override
public boolean hasTypeParameters() {
return PsiImplUtil.hasTypeParameters(this);
}
@NotNull
@Override
public PsiTypeParameter[] getTypeParameters() {
// Type parameters are those inside of the type designation (e.g.
// inside the '<' and '>').
return PsiImplUtil.getTypeParameters(this);
}
@Nullable
@Override
public PsiClass getContainingClass() {
return PsiTreeUtil.getParentOfType(this, HaxeClass.class, true);
}
@NotNull
@Override
public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) {
// XXX: PsiMethod uses a cache for substitutors.
return MethodSignatureBackedByPsiMethod.create(this, substitutor);
}
@Nullable
@Override
public PsiIdentifier getNameIdentifier() {
final HaxeComponentName componentName = getComponentName();
return componentName != null ? componentName.getIdentifier() : null;
}
@NotNull
@Override
public PsiMethod[] findSuperMethods() {
return HaxeMethodUtils.findSuperMethods(this);
}
@NotNull
@Override
public PsiMethod[] findSuperMethods(boolean checkAccess) {
return HaxeMethodUtils.findSuperMethods(this);
}
@NotNull
@Override
public PsiMethod[] findSuperMethods(PsiClass parentClass) {
return HaxeMethodUtils.findSuperMethods(this, parentClass);
}
@NotNull
@Override
public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) {
return HaxeMethodUtils.findSuperMethodSignaturesIncludingStatic(this);
}
@Deprecated
@Nullable
@Override
public PsiMethod findDeepestSuperMethod() {
return HaxeMethodUtils.findDeepestSuperMethod(this);
}
@NotNull
@Override
public PsiMethod[] findDeepestSuperMethods() {
return HaxeMethodUtils.findDeepestSuperMethods(this);
}
@Nullable
@Override
public PsiTypeParameterList getTypeParameterList() {
// Type parameters are those inside of the type designation (e.g. inside the '<' and '>').
HaxeTypeParam param = null;
final HaxeTypeTag tag = (HaxeTypeTag) findChildByType(HaxeTokenTypes.TYPE_TAG);
if (tag != null) {
final HaxeTypeOrAnonymous toa = tag.getTypeOrAnonymous();
final HaxeType type = (toa != null) ? toa.getType() : null;
param = (type != null) ? type.getTypeParam() : null;// XXX: Java<->Haxe list & type inversion -- See BNF.
}
return param;
}
@NotNull
@Override
public HaxeModifierList getModifierList() {
//
// Note Haxe's rules for visibility:
// (from http://haxe.org/manual/class-field-visibility.html)
//
// Omitting the visibility modifier usually defaults the visibility to private,
// but there are exceptions where it becomes public instead:
//
// - If the class is declared as extern.
// - If the field id declared on an interface.
// - If the field overrides a public field.
//
// Trivia: Protected
//
// Haxe has no notion of a protected keyword known from Java, C++ and
// other object-oriented languages. However, its private behavior is
// equal to those language's protected behavior, so Haxe actually
// lacks their real private behavior.
//
HaxeModifierList list = super.getModifierList();
if (null == list) {
list = new HaxeModifierListImpl(this.getNode());
}
// -- below modifiers need to be set individually
// because, they cannot be enforced through macro-list
if (super.isStatic()) {
list.setModifierProperty(HaxePsiModifier.STATIC, true);
}
if (super.isPublic()) {
list.setModifierProperty(HaxePsiModifier.PUBLIC, true);
}
else {
list.setModifierProperty(HaxePsiModifier.PRIVATE, true);
}
return list;
}
@Override
public boolean hasModifierProperty(@HaxePsiModifier.ModifierConstant @NonNls @NotNull String name) {
return this.getModifierList().hasModifierProperty(name);
}
@NotNull
@Override
public HierarchicalMethodSignature getHierarchicalMethodSignature() {
return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
}
@NotNull
@Override
public PsiParameterList getParameterList() {
final HaxeParameterList list = PsiTreeUtil.getChildOfType(this, HaxeParameterList.class);
return ((list != null) ? list : new HaxeParameterListImpl(new HaxeDummyASTNode("Dummy parameter list")));
}
@NotNull
@Override
public SearchScope getUseScope() {
if(this instanceof HaxeLocalFunctionDeclaration) {
final PsiElement outerBlock = UsefulPsiTreeUtil.getParentOfType(this, HaxeBlockStatement.class);
if(outerBlock != null) {
return new LocalSearchScope(outerBlock);
}
}
return super.getUseScope();
}
}