/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.lang.psi.impl.msil; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.NullableLazyValue; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import consulo.annotations.RequiredDispatchThread; import consulo.annotations.RequiredReadAction; import consulo.annotations.RequiredWriteAction; import consulo.csharp.lang.psi.CSharpElementVisitor; import consulo.csharp.lang.psi.CSharpGenericConstraint; import consulo.csharp.lang.psi.CSharpGenericConstraintList; import consulo.csharp.lang.psi.CSharpTypeDeclaration; import consulo.csharp.lang.psi.impl.light.CSharpLightGenericConstraintList; import consulo.csharp.lang.psi.impl.source.CSharpTypeDeclarationImplUtil; import consulo.dotnet.DotNetTypes; import consulo.dotnet.psi.DotNetGenericParameter; import consulo.dotnet.psi.DotNetGenericParameterList; import consulo.dotnet.psi.DotNetInheritUtil; import consulo.dotnet.psi.DotNetModifier; import consulo.dotnet.psi.DotNetModifierList; import consulo.dotnet.psi.DotNetNamedElement; import consulo.dotnet.psi.DotNetTypeList; import consulo.dotnet.psi.DotNetVariable; import consulo.dotnet.psi.DotNetXXXAccessor; import consulo.dotnet.resolve.DotNetTypeRef; import consulo.internal.dotnet.msil.decompiler.util.MsilHelper; import consulo.msil.lang.psi.MsilClassEntry; import consulo.msil.lang.psi.MsilEventEntry; import consulo.msil.lang.psi.MsilFieldEntry; import consulo.msil.lang.psi.MsilMethodEntry; import consulo.msil.lang.psi.MsilPropertyEntry; import consulo.msil.lang.psi.MsilTokens; import consulo.msil.lang.psi.MsilXXXAcessor; /** * @author VISTALL * @since 22.05.14 */ public class MsilClassAsCSharpTypeDefinition extends MsilElementWrapper<MsilClassEntry> implements CSharpTypeDeclaration { private NotNullLazyValue<DotNetNamedElement[]> myMembersValue = new NotNullLazyValue<DotNetNamedElement[]>() { @NotNull @Override @RequiredDispatchThread protected DotNetNamedElement[] compute() { MsilClassAsCSharpTypeDefinition parentThis = MsilClassAsCSharpTypeDefinition.this; DotNetNamedElement[] temp = myOriginal.getMembers(); List<DotNetNamedElement> copy = new ArrayList<DotNetNamedElement>(temp.length); Collections.addAll(copy, temp); List<DotNetNamedElement> list = new ArrayList<DotNetNamedElement>(temp.length); boolean isEnum = isEnum(); List<String> bannedFieldNames = new ArrayList<String>(); for(DotNetNamedElement element : temp) { if(element instanceof MsilFieldEntry) { String name = element.getName(); if(name == null) { continue; } if(StringUtil.containsAnyChar(name, "<>") || Comparing.equal(name, "value__") && isEnum) { bannedFieldNames.add(name); } } else if(element instanceof MsilEventEntry) { bannedFieldNames.add(element.getName()); } } for(DotNetNamedElement element : temp) { if(element instanceof MsilPropertyEntry) { DotNetXXXAccessor[] accessors = ((MsilPropertyEntry) element).getAccessors(); List<Pair<DotNetXXXAccessor, MsilMethodEntry>> pairs = new ArrayList<Pair<DotNetXXXAccessor, MsilMethodEntry>>(2); for(DotNetXXXAccessor accessor : accessors) { if(accessor instanceof MsilXXXAcessor) { MsilMethodEntry methodEntry = ((MsilXXXAcessor) accessor).resolveToMethod(); if(methodEntry != null) { pairs.add(Pair.create(accessor, methodEntry)); copy.remove(methodEntry); } } } if(!pairs.isEmpty()) { Pair<DotNetXXXAccessor, MsilMethodEntry> value = pairs.get(0); if(value.getFirst().getAccessorKind() == DotNetXXXAccessor.Kind.GET && value.getSecond().getParameters().length == 1 || value.getFirst().getAccessorKind() == DotNetXXXAccessor.Kind.SET && value.getSecond().getParameters().length == 2) { list.add(new MsilPropertyAsCSharpIndexMethodDeclaration(parentThis, (MsilPropertyEntry) element, pairs)); continue; } } list.add(new MsilPropertyAsCSharpPropertyDeclaration(parentThis, (MsilPropertyEntry) element, pairs)); } else if(element instanceof MsilEventEntry) { DotNetXXXAccessor[] accessors = ((MsilEventEntry) element).getAccessors(); List<Pair<DotNetXXXAccessor, MsilMethodEntry>> pairs = new ArrayList<Pair<DotNetXXXAccessor, MsilMethodEntry>>(2); for(DotNetXXXAccessor accessor : accessors) { if(accessor instanceof MsilXXXAcessor) { MsilMethodEntry methodEntry = ((MsilXXXAcessor) accessor).resolveToMethod(); if(methodEntry != null) { pairs.add(Pair.create(accessor, methodEntry)); copy.remove(methodEntry); } } } list.add(new MsilEventAsCSharpEventDeclaration(parentThis, (MsilEventEntry) element, pairs)); } else if(element instanceof MsilFieldEntry) { String name = element.getName(); if(bannedFieldNames.contains(name)) { continue; } if(isEnum) { list.add(new MsilFieldAsCSharpEnumConstantDeclaration(parentThis, (DotNetVariable) element)); } else { list.add(new MsilFieldAsCSharpFieldDeclaration(parentThis, (DotNetVariable) element)); } } else if(element instanceof MsilClassEntry) { list.add((DotNetNamedElement) MsilToCSharpUtil.wrap(element, parentThis, myGenericParameterContext.gemmate())); } } for(DotNetNamedElement member : copy) { if(member instanceof MsilMethodEntry) { String nameFromBytecode = ((MsilMethodEntry) member).getNameFromBytecode(); if(Comparing.equal(nameFromBytecode, MsilHelper.STATIC_CONSTRUCTOR_NAME) || StringUtil.startsWith(nameFromBytecode, "<")) { continue; } if(MsilHelper.CONSTRUCTOR_NAME.equals(nameFromBytecode)) { list.add(new MsilMethodAsCSharpConstructorDeclaration(parentThis, MsilClassAsCSharpTypeDefinition.this, (MsilMethodEntry) member, false)); } else if(Comparing.equal(nameFromBytecode, "op_Implicit") || Comparing.equal(nameFromBytecode, "op_Explicit")) { list.add(new MsilMethodAsCSharpConversionMethodDeclaration(parentThis, (MsilMethodEntry) member)); } else { boolean isDeConstructor = Comparing.equal(nameFromBytecode, "Finalize") && ((MsilMethodEntry) member).hasModifier(MsilTokens.PROTECTED_KEYWORD); if(isDeConstructor) { list.add(new MsilMethodAsCSharpConstructorDeclaration(parentThis, MsilClassAsCSharpTypeDefinition.this, (MsilMethodEntry) member, true)); } else { list.add(new MsilMethodAsCSharpMethodDeclaration(parentThis, null, new GenericParameterContext(null), (MsilMethodEntry) member)); } } } } return list.isEmpty() ? DotNetNamedElement.EMPTY_ARRAY : list.toArray(new DotNetNamedElement[list.size()]); } }; private final GenericParameterContext myGenericParameterContext; private final MsilModifierListToCSharpModifierList myModifierList; private final DotNetGenericParameterList myGenericParameterList; private final NullableLazyValue<CSharpLightGenericConstraintList> myGenericConstraintListValue = new NullableLazyValue<CSharpLightGenericConstraintList>() { @Nullable @Override @RequiredReadAction protected CSharpLightGenericConstraintList compute() { return MsilAsCSharpBuildUtil.buildConstraintList(getGenericParameterList()); } }; private final NotNullLazyValue<DotNetTypeRef[]> myExtendTypeRefsValue; private final NotNullLazyValue<DotNetTypeRef> myTypeRefForEnumConstantsValue; private Boolean myIsStruct; private Boolean myIsEnum; private Boolean myIsInterface; @RequiredReadAction public MsilClassAsCSharpTypeDefinition(@Nullable PsiElement parent, MsilClassEntry classEntry, @NotNull GenericParameterContext genericParameterContext) { super(parent, classEntry); myGenericParameterContext = genericParameterContext; myModifierList = new MsilModifierListToCSharpModifierList(this, classEntry.getModifierList()); DotNetGenericParameterList genericParameterList = classEntry.getGenericParameterList(); myGenericParameterList = MsilGenericParameterListAsCSharpGenericParameterList.build(this, genericParameterList, genericParameterContext); myExtendTypeRefsValue = NotNullLazyValue.createValue(() -> { String vmQName = getVmQName(); // hack if(DotNetTypes.System.Object.equals(vmQName)) { return DotNetTypeRef.EMPTY_ARRAY; } DotNetTypeRef[] extendTypeRefs = myOriginal.getExtendTypeRefs(); if(extendTypeRefs.length == 0) { return DotNetTypeRef.EMPTY_ARRAY; } DotNetTypeRef[] typeRefs = new DotNetTypeRef[extendTypeRefs.length]; for(int i = 0; i < typeRefs.length; i++) { typeRefs[i] = MsilToCSharpUtil.extractToCSharp(extendTypeRefs[i], myOriginal); } return typeRefs; }); myTypeRefForEnumConstantsValue = NotNullLazyValue.createValue(() -> MsilToCSharpUtil.extractToCSharp(myOriginal.getTypeRefForEnumConstants(), myOriginal)); } @Override public void accept(@NotNull CSharpElementVisitor visitor) { visitor.visitTypeDeclaration(this); } @Override public PsiFile getContainingFile() { return myOriginal.getContainingFile(); } @RequiredReadAction @Override public String getVmQName() { return myOriginal.getVmQName(); } @RequiredReadAction @Nullable @Override public String getVmName() { return myOriginal.getVmName(); } @Override public boolean isEquivalentTo(PsiElement another) { return CSharpTypeDeclarationImplUtil.isEquivalentTo(this, another); } @RequiredReadAction @Override public PsiElement getLeftBrace() { return null; } @RequiredReadAction @Override public PsiElement getRightBrace() { return null; } @Nullable @Override public CSharpGenericConstraintList getGenericConstraintList() { return myGenericConstraintListValue.getValue(); } @NotNull @Override public CSharpGenericConstraint[] getGenericConstraints() { CSharpGenericConstraintList genericConstraintList = getGenericConstraintList(); return genericConstraintList == null ? CSharpGenericConstraint.EMPTY_ARRAY : genericConstraintList.getGenericConstraints(); } @Override public boolean isInterface() { if(myIsInterface != null) { return myIsInterface; } return myIsInterface = myOriginal.isInterface(); } @Override public boolean isStruct() { if(myIsStruct != null) { return myIsStruct; } return myIsStruct = myOriginal.isStruct(); } @Override public boolean isEnum() { if(myIsEnum != null) { return myIsEnum; } return myIsEnum = myOriginal.isEnum(); } @Override public boolean isNested() { return myOriginal.isNested(); } @Nullable @Override public DotNetTypeList getExtendList() { return null; } @RequiredReadAction @NotNull @Override public DotNetTypeRef[] getExtendTypeRefs() { return myExtendTypeRefsValue.getValue(); } @RequiredReadAction @Override public boolean isInheritor(@NotNull String other, boolean deep) { return DotNetInheritUtil.isInheritor(this, other, deep); } @NotNull @RequiredReadAction @Override public DotNetTypeRef getTypeRefForEnumConstants() { return myTypeRefForEnumConstantsValue.getValue(); } @Nullable @Override public DotNetGenericParameterList getGenericParameterList() { return myGenericParameterList; } @NotNull @Override public DotNetGenericParameter[] getGenericParameters() { return myGenericParameterList == null ? DotNetGenericParameter.EMPTY_ARRAY : myGenericParameterList.getParameters(); } @Override public int getGenericParametersCount() { return myGenericParameterList == null ? 0 : myGenericParameterList.getGenericParametersCount(); } @RequiredReadAction @NotNull @Override public DotNetNamedElement[] getMembers() { return myMembersValue.getValue(); } @RequiredReadAction @Override public boolean hasModifier(@NotNull DotNetModifier modifier) { return myModifierList.hasModifier(modifier); } @RequiredReadAction @Nullable @Override public DotNetModifierList getModifierList() { return myModifierList; } @RequiredReadAction @Nullable @Override public String getPresentableParentQName() { return myOriginal.getPresentableParentQName(); } @RequiredReadAction @Override public String getName() { return MsilHelper.cutGenericMarker(myOriginal.getName()); } @RequiredReadAction @Nullable @Override public String getPresentableQName() { return MsilHelper.cutGenericMarker(myOriginal.getPresentableQName()); } @Override public String toString() { return myOriginal.toString(); } @RequiredReadAction @Nullable @Override public PsiElement getNameIdentifier() { return myOriginal.getNameIdentifier(); } @RequiredWriteAction @Override public PsiElement setName(@NonNls @NotNull String s) throws IncorrectOperationException { return null; } @Nullable @Override protected Class<? extends PsiElement> getNavigationElementClass() { return CSharpTypeDeclaration.class; } }