/*
* 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.ide.msil.representation.builder;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.PairFunction;
import consulo.annotations.RequiredReadAction;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpArrayTypeRef;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpEmptyGenericWrapperTypeRef;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpGenericWrapperTypeRef;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpTypeRefByQName;
import consulo.dotnet.DotNetTypes;
import consulo.dotnet.resolve.DotNetTypeRef;
import consulo.internal.dotnet.asm.signature.ArrayShapeSignature;
import consulo.internal.dotnet.asm.signature.ArrayTypeSignature;
import consulo.internal.dotnet.asm.signature.ClassTypeSignature;
import consulo.internal.dotnet.asm.signature.TypeSignature;
import consulo.internal.dotnet.asm.signature.TypeSignatureWithGenericParameters;
import consulo.internal.dotnet.asm.signature.ValueTypeSignature;
import consulo.internal.dotnet.msil.decompiler.textBuilder.util.StubBlockUtil;
import consulo.internal.dotnet.msil.decompiler.util.MsilHelper;
import consulo.msil.lang.psi.MsilCustomAttribute;
import consulo.msil.lang.stubbing.MsilCustomAttributeArgumentList;
import consulo.msil.lang.stubbing.MsilCustomAttributeStubber;
import consulo.msil.lang.stubbing.values.MsiCustomAttributeValue;
import consulo.msil.lang.stubbing.values.MsilCustomAttributeEnumValue;
/**
* @author VISTALL
* @since 21.03.14
*/
public class CSharpAttributeStubBuilder
{
private static final Logger LOGGER = Logger.getInstance(CSharpAttributeStubBuilder.class);
@RequiredReadAction
public static void append(StringBuilder builder, MsilCustomAttribute attribute)
{
MsilCustomAttributeArgumentList attributeArgumentList = MsilCustomAttributeStubber.build(attribute);
List<MsiCustomAttributeValue> constructorArguments = attributeArgumentList.getConstructorArguments();
Map<String, MsiCustomAttributeValue> namedArguments = attributeArgumentList.getNamedArguments();
if(constructorArguments.isEmpty() && namedArguments.isEmpty())
{
return;
}
builder.append("(");
for(int i = 0; i < constructorArguments.size(); i++)
{
if(i != 0)
{
builder.append(", ");
}
appendValue(builder, constructorArguments.get(i), attribute);
}
if(!constructorArguments.isEmpty() && !namedArguments.isEmpty())
{
builder.append(", ");
}
boolean first = false;
for(Map.Entry<String, MsiCustomAttributeValue> entry : namedArguments.entrySet())
{
if(!first)
{
first = true;
}
else
{
builder.append(", ");
}
builder.append(entry.getKey()).append(" = ");
appendValue(builder, entry.getValue(), attribute);
}
builder.append(")");
}
@RequiredReadAction
private static void appendValue(StringBuilder builder, MsiCustomAttributeValue value, final PsiElement scope)
{
if(value instanceof MsilCustomAttributeEnumValue)
{
final DotNetTypeRef typeRef = toTypeRef(scope, value.getTypeSignature(), true);
assert typeRef != null;
List<String> values = ((MsilCustomAttributeEnumValue) value).getValues();
StubBlockUtil.join(builder, values, new PairFunction<StringBuilder, String, Void>()
{
@Nullable
@Override
@RequiredReadAction
public Void fun(StringBuilder builder, String s)
{
CSharpStubBuilderVisitor.appendTypeRef(scope, builder, typeRef);
builder.append(".").append(s);
return null;
}
}, " | ");
}
else
{
Object realValue = value.getValue();
if(realValue instanceof CharSequence)
{
builder.append("\"");
builder.append((CharSequence) realValue);
builder.append("\"");
}
else if(realValue instanceof TypeSignature)
{
DotNetTypeRef typeRef = toTypeRef(scope, (TypeSignature) realValue, true);
assert typeRef != null;
builder.append("typeof(");
CSharpStubBuilderVisitor.appendTypeRef(scope, builder, typeRef);
builder.append(")");
}
else
{
builder.append(realValue);
}
}
}
@Nullable
@RequiredReadAction
private static DotNetTypeRef toTypeRef(PsiElement scope, TypeSignature typeSignature, boolean firstEnter)
{
if(typeSignature == null)
{
return null;
}
if(typeSignature == TypeSignature.BOOLEAN)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Boolean);
}
else if(typeSignature == TypeSignature.STRING)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.String);
}
else if(typeSignature == TypeSignature.U1)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Byte);
}
else if(typeSignature == TypeSignature.I1)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.SByte);
}
if(typeSignature == TypeSignature.U2)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.UInt16);
}
else if(typeSignature == TypeSignature.I2)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Int16);
}
else if(typeSignature == TypeSignature.U4)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.UInt32);
}
else if(typeSignature == TypeSignature.I4)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Int32);
}
else if(typeSignature == TypeSignature.U8)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.UInt64);
}
else if(typeSignature == TypeSignature.I8)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Int64);
}
else if(typeSignature == TypeSignature.R4)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Single);
}
else if(typeSignature == TypeSignature.R8)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Double);
}
else if(typeSignature == TypeSignature.CHAR)
{
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Char);
}
else if(typeSignature instanceof TypeSignatureWithGenericParameters)
{
TypeSignatureWithGenericParameters typeSignatureWithGenericParameters = (TypeSignatureWithGenericParameters) typeSignature;
List<TypeSignature> genericArguments = typeSignatureWithGenericParameters.getGenericArguments();
DotNetTypeRef[] innerTypeRefs = new DotNetTypeRef[genericArguments.size()];
for(int i = 0; i < innerTypeRefs.length; i++)
{
innerTypeRefs[i] = toTypeRef(scope, genericArguments.get(i), false);
}
return new CSharpGenericWrapperTypeRef(toTypeRef(scope, typeSignatureWithGenericParameters.getSignature(), false), innerTypeRefs);
}
else if(typeSignature instanceof ArrayTypeSignature)
{
ArrayShapeSignature arrayShape = ((ArrayTypeSignature) typeSignature).getArrayShape();
return new CSharpArrayTypeRef(scope, toTypeRef(scope, ((ArrayTypeSignature) typeSignature).getElementType(), false), arrayShape.getRank());
}
else if(typeSignature instanceof ValueTypeSignature)
{
return new CSharpTypeRefByQName(scope, ((ValueTypeSignature) typeSignature).getValueType().getFullName());
}
else if(typeSignature instanceof ClassTypeSignature)
{
String fullName = ((ClassTypeSignature) typeSignature).getClassType().getFullName();
CSharpTypeRefByQName innerTypeRef = new CSharpTypeRefByQName(scope, fullName);
if(firstEnter)
{
if(StringUtil.containsChar(fullName, MsilHelper.GENERIC_MARKER_IN_NAME))
{
return new CSharpEmptyGenericWrapperTypeRef(innerTypeRef);
}
}
return innerTypeRef;
}
else
{
LOGGER.error("Unknown how convert: " + typeSignature.toString() + ":0x" + Integer.toHexString(typeSignature.getType()));
return new CSharpTypeRefByQName(scope, DotNetTypes.System.Object);
}
}
}