/* * Copyright 2000-2013 JetBrains s.r.o. * Copyright 2014-2015 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.model; import com.intellij.plugins.haxe.HaxeComponentType; import com.intellij.plugins.haxe.lang.psi.*; import com.intellij.plugins.haxe.model.type.HaxeTypeResolver; import com.intellij.plugins.haxe.model.type.ResultHolder; import com.intellij.plugins.haxe.model.type.SpecificHaxeClassReference; import com.intellij.plugins.haxe.model.type.SpecificTypeReference; import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiIdentifier; import org.apache.commons.lang.NotImplementedException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public class HaxeClassModel { public HaxeClass haxeClass; public HaxeClassModel(HaxeClass haxeClass) { this.haxeClass = haxeClass; } public HaxeClassReferenceModel getParentClassReference() { List<HaxeType> list = haxeClass.getHaxeExtendsList(); if (list.size() == 0) return null; return new HaxeClassReferenceModel(list.get(0)); } static public boolean isValidClassName(String name) { return name.substring(0, 1).equals(name.substring(0, 1).toUpperCase()); } public HaxeClassModel getParentClass() { final HaxeClassReferenceModel reference = this.getParentClassReference(); return (reference != null) ? reference.getHaxeClass() : null; } public List<HaxeClassReferenceModel> getInterfaceExtendingInterfaces() { List<HaxeType> list = haxeClass.getHaxeExtendsList(); List<HaxeClassReferenceModel> out = new ArrayList<HaxeClassReferenceModel>(); for (HaxeType type : list) { out.add(new HaxeClassReferenceModel(type)); } return out; } public List<HaxeClassReferenceModel> getImplementingInterfaces() { List<HaxeType> list = haxeClass.getHaxeImplementsList(); List<HaxeClassReferenceModel> out = new ArrayList<HaxeClassReferenceModel>(); for (HaxeType type : list) { out.add(new HaxeClassReferenceModel(type)); } return out; } public boolean isExtern() { return haxeClass.isExtern(); } public boolean isClass() { return !this.isAbstract() && (HaxeComponentType.typeOf(haxeClass) == HaxeComponentType.CLASS); } public boolean isInterface() { return HaxeComponentType.typeOf(haxeClass) == HaxeComponentType.INTERFACE; } public boolean isEnum() { return HaxeComponentType.typeOf(haxeClass) == HaxeComponentType.ENUM; } public boolean isTypedef() { return HaxeComponentType.typeOf(haxeClass) == HaxeComponentType.TYPEDEF; } public boolean isAbstract() { return haxeClass instanceof HaxeAbstractClassDeclaration; } // @TODO: Create AbstractHaxeClassModel extending this class for these methods? // @TODO: this should be properly parsed in haxe.bnf so searching for the underlying type is not required @Nullable public HaxeTypeOrAnonymous getAbstractUnderlyingType() { if (!isAbstract()) return null; PsiElement[] children = getPsi().getChildren(); // FIX: support list of metas before ComponentName for(int i = 0; i < children.length; ++i) { PsiElement child = children[i]; if(child instanceof HaxeComponentName) { if(i + 1 < children.length) { child = children[i + 1]; if(child instanceof HaxeTypeOrAnonymous) { return (HaxeTypeOrAnonymous)child; } } break; } } return null; } // @TODO: this should be properly parsed in haxe.bnf so searching for to is not required public List<HaxeType> getAbstractToList() { if (!isAbstract()) return Collections.emptyList(); List<HaxeType> types = new LinkedList<HaxeType>(); for (HaxeIdentifier id : UsefulPsiTreeUtil.getChildren(haxeClass, HaxeIdentifier.class)) { if (id.getText().equals("to")) { PsiElement sibling = UsefulPsiTreeUtil.getNextSiblingNoSpaces(id); if (sibling instanceof HaxeType) { types.add((HaxeType)sibling); } } } return types; } // @TODO: this should be properly parsed in haxe.bnf so searching for from is not required public List<HaxeType> getAbstractFromList() { if (!isAbstract()) return Collections.emptyList(); List<HaxeType> types = new LinkedList<HaxeType>(); for (HaxeIdentifier id : UsefulPsiTreeUtil.getChildren(haxeClass, HaxeIdentifier.class)) { if (id.getText().equals("from")) { PsiElement sibling = UsefulPsiTreeUtil.getNextSiblingNoSpaces(id); if (sibling instanceof HaxeType) { types.add((HaxeType)sibling); } } } return types; } public boolean hasMethod(String name) { return getMethod(name) != null; } public boolean hasMethodSelf(String name) { HaxeMethodModel method = getMethod(name); if (method == null) return false; return (method.getDeclaringClass() == this); } public HaxeMethodModel getMethodSelf(String name) { HaxeMethodModel method = getMethod(name); if (method == null) return null; return (method.getDeclaringClass() == this) ? method : null; } public HaxeMethodModel getConstructorSelf() { return getMethodSelf("new"); } public HaxeMethodModel getConstructor() { return getMethod("new"); } public boolean hasConstructor() { return getConstructor() != null; } public HaxeMethodModel getParentConstructor() { HaxeClassReferenceModel parentClass = getParentClassReference(); if (parentClass == null) return null; return parentClass.getHaxeClass().getMethod("new"); } public HaxeMemberModel getMember(String name) { final HaxeMethodModel method = getMethod(name); final HaxeFieldModel field = getField(name); return (method != null) ? method : field; } public List<HaxeMemberModel> getMembers() { LinkedList<HaxeMemberModel> members = new LinkedList<HaxeMemberModel>(); for (HaxeMethodModel method : getMethods()) members.add(method); for (HaxeFieldModel field : getFields()) members.add(field); return members; } @NotNull public List<HaxeMemberModel> getMembersSelf() { LinkedList<HaxeMemberModel> members = new LinkedList<HaxeMemberModel>(); HaxeClassBody body = UsefulPsiTreeUtil.getChild(haxeClass, HaxeClassBody.class); if (body != null) { for (PsiElement element : body.getChildren()) { if (element instanceof HaxeMethod || element instanceof HaxeVarDeclaration) { HaxeMemberModel model = HaxeMemberModel.fromPsi(element); if (model != null) { members.add(model); } } } } return members; } public HaxeFieldModel getField(String name) { HaxeVarDeclaration name1 = (HaxeVarDeclaration)haxeClass.findHaxeFieldByName(name); return name1 != null ? new HaxeFieldModel(name1) : null; } public HaxeMethodModel getMethod(String name) { HaxeMethodPsiMixin name1 = (HaxeMethodPsiMixin)haxeClass.findHaxeMethodByName(name); return name1 != null ? name1.getModel() : null; } public List<HaxeMethodModel> getMethods() { List<HaxeMethodModel> models = new ArrayList<HaxeMethodModel>(); for (HaxeMethod method : haxeClass.getHaxeMethods()) { models.add(method.getModel()); } return models; } public List<HaxeMethodModel> getMethodsSelf() { List<HaxeMethodModel> models = new ArrayList<HaxeMethodModel>(); for (HaxeMethod method : haxeClass.getHaxeMethods()) { if (method.getContainingClass() == this.haxeClass) models.add(method.getModel()); } return models; } public List<HaxeMethodModel> getAncestorMethods() { List<HaxeMethodModel> models = new ArrayList<HaxeMethodModel>(); for (HaxeMethod method : haxeClass.getHaxeMethods()) { if (method.getContainingClass() != this.haxeClass) models.add(method.getModel()); } return models; } @NotNull public HaxeClass getPsi() { return haxeClass; } @Nullable public HaxeClassBody getBodyPsi() { return (haxeClass instanceof HaxeClassDeclaration) ? ((HaxeClassDeclaration)haxeClass).getClassBody() : null; } @Nullable public PsiIdentifier getNamePsi() { return haxeClass.getNameIdentifier(); } private HaxeDocumentModel _document = null; @NotNull public HaxeDocumentModel getDocument() { if (_document == null) _document = new HaxeDocumentModel(haxeClass); return _document; } public String getName() { return haxeClass.getName(); } public void addMethodsFromPrototype(List<HaxeMethodModel> methods) { throw new NotImplementedException("Not implemented HaxeClassMethod.addMethodsFromPrototype() : check HaxeImplementMethodHandler"); } public List<HaxeFieldModel> getFields() { HaxeClassBody body = UsefulPsiTreeUtil.getChild(haxeClass, HaxeClassBody.class); LinkedList<HaxeFieldModel> out = new LinkedList<HaxeFieldModel>(); if (body != null) { for (HaxeVarDeclaration declaration : UsefulPsiTreeUtil.getChildren(body, HaxeVarDeclaration.class)) { out.add(new HaxeFieldModel(declaration)); } } return out; } public List<HaxeFieldModel> getFieldsSelf() { HaxeClassBody body = UsefulPsiTreeUtil.getChild(haxeClass, HaxeClassBody.class); LinkedList<HaxeFieldModel> out = new LinkedList<HaxeFieldModel>(); if (body != null) { for (HaxeVarDeclaration declaration : UsefulPsiTreeUtil.getChildren(body, HaxeVarDeclaration.class)) { if (declaration.getContainingClass() == this.haxeClass) { out.add(new HaxeFieldModel(declaration)); } } } return out; } public Set<HaxeClassModel> getCompatibleTypes() { final Set<HaxeClassModel> output = new LinkedHashSet<HaxeClassModel>(); writeCompatibleTypes(output); return output; } public void writeCompatibleTypes(Set<HaxeClassModel> output) { // Own output.add(this); final HaxeClassModel parentClass = this.getParentClass(); // Parent classes if (parentClass != null) { if (!output.contains(parentClass)) { parentClass.writeCompatibleTypes(output); } } // Interfaces for (HaxeClassReferenceModel model : this.getImplementingInterfaces()) { if (model == null) continue; final HaxeClassModel aInterface = model.getHaxeClass(); if (aInterface == null) continue; if (!output.contains(aInterface)) { aInterface.writeCompatibleTypes(output); } } // @CHECK abstract FROM for (HaxeType type : getAbstractFromList()) { final ResultHolder aTypeRef = HaxeTypeResolver.getTypeFromType(type); SpecificHaxeClassReference classType = aTypeRef.getClassType(); if (classType != null) { classType.getHaxeClassModel().writeCompatibleTypes(output); } } // @CHECK abstract TO for (HaxeType type : getAbstractToList()) { final ResultHolder aTypeRef = HaxeTypeResolver.getTypeFromType(type); SpecificHaxeClassReference classType = aTypeRef.getClassType(); if (classType != null) { classType.getHaxeClassModel().writeCompatibleTypes(output); } } } public List<HaxeGenericParamModel> getGenericParams() { List<HaxeGenericParamModel> out = new LinkedList<HaxeGenericParamModel>(); if (getPsi().getGenericParam() != null) { int index = 0; for (HaxeGenericListPart part : getPsi().getGenericParam().getGenericListPartList()) { out.add(new HaxeGenericParamModel(part, index)); index++; } } return out; } public void addField(String name, SpecificTypeReference type) { this.getDocument().addTextAfterElement(getBodyPsi(), "\npublic var " + name + ":" + type.toStringWithoutConstant() + ";\n"); } public void addMethod(String name) { this.getDocument().addTextAfterElement(getBodyPsi(), "\npublic function " + name + "() {\n}\n"); } }