package org.fandev.lang.fan.psi.impl.statements.typedefs;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.util.Iconable;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.impl.ElementBase;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.ui.RowIcon;
import com.intellij.util.VisibilityIcons;
import com.intellij.util.IncorrectOperationException;
import org.fandev.lang.fan.FanElementTypes;
import org.fandev.lang.fan.FanTokenTypes;
import org.fandev.lang.fan.psi.FanFile;
import org.fandev.lang.fan.psi.api.modifiers.FanModifierList;
import org.fandev.lang.fan.psi.api.statements.typeDefs.*;
import org.fandev.lang.fan.psi.api.statements.typeDefs.members.FanField;
import org.fandev.lang.fan.psi.api.statements.typeDefs.members.FanSlot;
import org.fandev.lang.fan.psi.api.statements.typeDefs.members.FanMethod;
import org.fandev.lang.fan.psi.api.types.FanCodeReferenceElement;
import org.fandev.lang.fan.psi.impl.FanBaseElementImpl;
import org.fandev.lang.fan.psi.impl.FanClassReferenceType;
import org.fandev.lang.fan.psi.impl.FanFileImpl;
import org.fandev.lang.fan.psi.impl.synthetic.FanLightIdentifier;
import org.fandev.lang.fan.psi.stubs.FanTypeDefinitionStub;
import org.fandev.utils.PsiUtil;
import org.fandev.index.FanIndex;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dror Bereznitsky
* @date Jan 9, 2009 11:42:19 PM
*/
public abstract class FanTypeDefinitionImpl extends FanBaseElementImpl<FanTypeDefinitionStub>
implements FanTypeDefinition {
protected FanField[] fanFields;
protected FanMethod[] fanMethods;
protected FanSlot[] fanSlots;
private final static Logger logger = Logger.getInstance("org.fandev.lang.fan.psi.impl.statements.typedefs.FanTypeDefinitionImpl");
public FanTypeDefinitionImpl(final FanTypeDefinitionStub stubElement, @NotNull final IStubElementType iStubElementType) {
super(stubElement, iStubElementType);
}
public FanTypeDefinitionImpl(final ASTNode astNode) {
super(astNode);
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
public String getPresentableText() {
return getName();
}
@Nullable
public String getLocationString() {
final PsiFile file = getContainingFile();
if (file instanceof FanFile) {
final FanFile fanFile = (FanFile) file;
return fanFile.getPodName().length() > 0 ? "(" + fanFile.getPodName() + ")" : "";
}
return "";
}
@Nullable
public Icon getIcon(final boolean open) {
return FanTypeDefinitionImpl.this
.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS);
}
@Nullable
public TextAttributesKey getTextAttributesKey() {
return null;
}
};
}
@NotNull
public PsiClassType[] getExtendsListTypes() {
final List<PsiClassType> extendsTypes = getReferenceListTypes(getInheritanceClause());
return extendsTypes.toArray(new PsiClassType[0]);
}
@Nullable
public FanInheritanceClause getInheritanceClause() {
return findChildByClass(FanInheritanceClause.class);
}
@NotNull
public PsiClassType[] getImplementsListTypes() {
return new PsiClassType[0];
}
public PsiClass getSuperClass() {
final PsiClassType[] superTypes = getSuperTypes();
for (final PsiClassType psiClassType : superTypes) {
final FanTypeDefinition fanType = (FanTypeDefinition) psiClassType.resolve();
if (fanType instanceof FanClassDefinition) {
return fanType;
}
}
if (!"Obj".equals(getName())) {
return getFanObjType();
}
return null;
}
public FanTypeDefinition getSuperType() {
return (FanTypeDefinition) getSuperClass();
}
@NotNull
public PsiClass[] getSupers() {
final PsiClassType[] superTypes = getSuperTypes();
final List<PsiClass> result = new ArrayList<PsiClass>();
for (final PsiClassType superType : superTypes) {
final PsiClass superClass = superType.resolve();
if (superClass != null) {
result.add(superClass);
}
}
return result.toArray(new PsiClass[0]);
}
@NotNull
public PsiClassType[] getSuperTypes() {
return getExtendsListTypes();
}
public PsiReferenceList getExtendsList() {
return null;
}
public PsiReferenceList getImplementsList() {
return null;
}
private static List<PsiClassType> getReferenceListTypes(@Nullable FanReferenceList list) {
final ArrayList<PsiClassType> types = new ArrayList<PsiClassType>();
if (list != null) {
for (final FanCodeReferenceElement ref : list.getReferenceElements()) {
types.add(new FanClassReferenceType(ref));
}
}
return types;
}
@NotNull
public String getPodName() {
final PsiFile psiFile = getContainingFile();
if (psiFile instanceof FanFile) {
return ((FanFile) psiFile).getPodName();
}
logger.warn("Fantom type '" + getName() + "' is not in a fan file !");
return "NotFanFile";
}
public int getTextOffset() {
final PsiIdentifier identifier = getNameIdentifier();
return identifier == null ? 0 : identifier.getTextRange().getStartOffset();
}
@Override
public String getName() {
final PsiIdentifier id = this.getNameIdentifier();
return id != null ? id.getText() : "NO NAME";
}
@Nullable
public PsiIdentifier getNameIdentifier() {
PsiElement element = null;
try {
element = findChildByType(FanElementTypes.NAME_ELEMENT);
} catch (Exception e) {
logger.error("Error looking for name element for " + this, e);
}
if (element != null) {
return new FanLightIdentifier(getManager(), getContainingFile(), element.getTextRange());
}
return null;
}
public String getQualifiedName() {
return getPodName() + "::" + getName();
}
public PsiModifierList getModifierList() {
return findChildByClass(FanModifierList.class);
}
public boolean hasModifierProperty(@Modifier final String name) {
return getModifierList().hasModifierProperty(name);
}
@Nullable
public Icon getIcon(final int flags) {
final Icon icon = getIconInner();
final boolean isLocked = (flags & ICON_FLAG_READ_STATUS) != 0 && !isWritable();
final RowIcon rowIcon = ElementBase.createLayeredIcon(icon, PsiUtil.getFlags(this, isLocked));
VisibilityIcons.setVisibilityIcon(getModifierList(), rowIcon);
return rowIcon;
}
@NotNull
public FanMethod[] getFanMethods() {
if (fanMethods == null) {
final FanSlot[] fanSlots = getSlots();
final List<FanMethod> list = new ArrayList<FanMethod>();
for (final FanSlot fanSlot : fanSlots) {
if (fanSlot instanceof FanMethod) {
list.add((FanMethod) fanSlot);
}
}
fanMethods = list.toArray(new FanMethod[0]);
}
return fanMethods;
}
@NotNull
public FanField[] getFanFields() {
if (fanFields == null) {
final FanSlot[] fanSlots = getSlots();
final List<FanField> list = new ArrayList<FanField>();
for (final FanSlot fanSlot : fanSlots) {
if (fanSlot instanceof FanField) {
list.add((FanField) fanSlot);
}
}
fanFields = list.toArray(new FanField[0]);
}
return fanFields;
}
@NotNull
public FanSlot[] getSlots() {
if (fanSlots == null) {
final List<FanSlot> slots = new ArrayList<FanSlot>();
final PsiElement element = findChildByType(getBodyElementType());
if (element != null) {
final PsiElement[] bodyEls = element.getChildren();
for (final PsiElement bodyEl : bodyEls) {
if (bodyEl instanceof FanSlot) {
slots.add((FanSlot) bodyEl);
}
}
}
fanSlots = slots.toArray(new FanSlot[0]);
}
return fanSlots;
}
public FanField getFieldByName(final String name) {
final FanField[] fields = getFanFields();
for (final FanField field : fields) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
public FanMethod getMethodByName(final String name) {
final FanMethod[] methods = getFanMethods();
for (final FanMethod method : methods) {
if (method.getName().equals(name)) {
return method;
}
}
return null;
}
@NotNull
public FanSlot[] getSlots(final String modifier) {
final List<FanSlot> slots = new ArrayList<FanSlot>();
for (final FanSlot slot : getSlots()) {
if (slot.hasModifierProperty(modifier)) {
slots.add(slot);
}
}
return slots.toArray(new FanSlot[0]);
}
@NotNull
public FanMethod[] getFanMethods(final String modifier) {
final List<FanMethod> methods = new ArrayList<FanMethod>();
for (final FanMethod method : getFanMethods()) {
if (method.hasModifierProperty(modifier)) {
methods.add(method);
}
}
return methods.toArray(new FanMethod[0]);
}
@NotNull
public FanField[] getFanFields(final String modifier) {
final List<FanField> fields = new ArrayList<FanField>();
for (final FanField field : getFanFields()) {
if (field.hasModifierProperty(modifier)) {
fields.add(field);
}
}
return fields.toArray(new FanField[0]);
}
public String getJavaQualifiedName() {
return "fan." + getPodName() + "." + getName();
}
public FanTypeDefinitionBody getBodyElement() {
return (FanTypeDefinitionBody) findChildByType(getBodyElementType());
}
public PsiElement addMemberDeclaration(@NotNull final PsiElement decl, final PsiElement anchorBefore)
throws IncorrectOperationException {
final FanTypeDefinitionBody body = getBodyElement();
if (body == null) {
throw new IncorrectOperationException("Type definition without a body");
}
ASTNode anchorNode;
if (anchorBefore != null) {
anchorNode = anchorBefore.getNode();
} else {
final PsiElement child = body.getLastChild();
assert child != null;
anchorNode = child.getNode();
}
final ASTNode bodyNode = body.getNode();
bodyNode.addChild(decl.getNode(), anchorNode);
bodyNode.addLeaf(FanTokenTypes.WHITE_SPACE, " ", decl.getNode()); //add whitespaces before and after to hack over incorrect auto reformat
bodyNode.addLeaf(FanTokenTypes.WHITE_SPACE, " ", anchorNode);
return decl;
}
protected abstract Icon getIconInner();
protected abstract IElementType getBodyElementType();
}