/*
* 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.source.resolve.type;
import java.math.BigInteger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joou.UByte;
import org.joou.UInteger;
import org.joou.ULong;
import org.joou.UShort;
import consulo.annotations.RequiredReadAction;
import consulo.csharp.lang.psi.CSharpTokenSets;
import consulo.csharp.lang.psi.CSharpTypeDeclaration;
import consulo.csharp.lang.psi.impl.CSharpTypeUtil;
import consulo.csharp.lang.psi.impl.source.CSharpConstantExpressionImpl;
import consulo.csharp.lang.psi.impl.source.resolve.CSharpConstantBaseTypeRef;
import consulo.dotnet.DotNetTypes;
import consulo.dotnet.resolve.DotNetTypeRef;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
/**
* @author VISTALL
* @since 31.08.14
*/
public class CSharpConstantTypeRef extends CSharpConstantBaseTypeRef
{
public CSharpConstantTypeRef(CSharpConstantExpressionImpl element, @NotNull DotNetTypeRef defaultTypeRef)
{
super(element, defaultTypeRef);
}
@RequiredReadAction
public static boolean isNumberLiteral(CSharpConstantExpressionImpl expression)
{
IElementType literalType = expression.getLiteralType();
return literalType == CSharpTokenSets.INTEGER_LITERAL ||
literalType == CSharpTokenSets.UINTEGER_LITERAL ||
literalType == CSharpTokenSets.ULONG_LITERAL ||
literalType == CSharpTokenSets.FLOAT_LITERAL ||
literalType == CSharpTokenSets.DOUBLE_LITERAL ||
literalType == CSharpTokenSets.LONG_LITERAL;
}
@Nullable
@RequiredReadAction
public static DotNetTypeRef testNumberConstant(@NotNull CSharpConstantExpressionImpl expression,
@NotNull String prefix,
@NotNull DotNetTypeRef another,
@NotNull PsiElement scope)
{
if(isNumberLiteral(expression))
{
PsiElement element = another.resolve().getElement();
String qName = element instanceof CSharpTypeDeclaration ? ((CSharpTypeDeclaration) element).getVmQName() : null;
if(qName == null)
{
return null;
}
Object value;
try
{
value = expression.getValue(prefix);
}
catch(Exception e)
{
return null;
}
DotNetTypeRef anotherRef = testNumber(value, qName, another, scope);
if(anotherRef != null)
{
return anotherRef;
}
else
{
return null;
}
}
return null;
}
@Nullable
@RequiredReadAction
public static DotNetTypeRef testNumber(@Nullable Object value, @NotNull String qName, @NotNull DotNetTypeRef another, @NotNull PsiElement scope)
{
if(value instanceof Number)
{
Number numberValue = (Number) value;
if(testInteger(DotNetTypes.System.Byte, qName, numberValue, UByte.MIN_VALUE, UByte.MAX_VALUE))
{
return another;
}
if(testInteger(DotNetTypes.System.SByte, qName, numberValue, Byte.MIN_VALUE, Byte.MAX_VALUE))
{
return another;
}
if(testInteger(DotNetTypes.System.Int16, qName, numberValue, Short.MIN_VALUE, Short.MAX_VALUE))
{
return another;
}
if(testInteger(DotNetTypes.System.UInt16, qName, numberValue, UShort.MIN_VALUE, UShort.MAX_VALUE))
{
return another;
}
if(testInteger(DotNetTypes.System.Int32, qName, numberValue, Integer.MIN_VALUE, Integer.MAX_VALUE))
{
return another;
}
if(testInteger(DotNetTypes.System.UInt32, qName, numberValue, UInteger.MIN_VALUE, UInteger.MAX_VALUE))
{
return another;
}
if(testInteger(DotNetTypes.System.Int64, qName, numberValue, Long.MIN_VALUE, Long.MAX_VALUE))
{
return another;
}
if(testBigInteger(DotNetTypes.System.UInt64, qName, numberValue, ULong.MIN_VALUE, ULong.MAX_VALUE))
{
return another;
}
CSharpTypeRefByQName enumTypeRef = new CSharpTypeRefByQName(scope, DotNetTypes.System.Enum);
if(CSharpTypeUtil.isInheritable(enumTypeRef, another, scope) && numberValue.longValue() == 0)
{
return another;
}
if(testDouble(DotNetTypes.System.Single, qName, numberValue, Float.MIN_VALUE, Float.MAX_VALUE))
{
if(numberValue instanceof Double)
{
double fraction = numberValue.doubleValue() % 1.0d;
if(fraction == 0)
{
return another;
}
}
}
if(testDouble(DotNetTypes.System.Double, qName, numberValue, Double.MIN_VALUE, Double.MAX_VALUE))
{
return another;
}
}
return null;
}
private static boolean testInteger(@NotNull String leftTypeQName, String qName, @NotNull Number value, long min, long max)
{
return testBigInteger(leftTypeQName, qName, value, BigInteger.valueOf(min), BigInteger.valueOf(max));
}
private static boolean testBigInteger(@NotNull String leftTypeQName, String qName, @NotNull Number value, BigInteger min, BigInteger max)
{
if(!(leftTypeQName.equals(qName)))
{
return false;
}
if(!(value instanceof BigInteger))
{
return false;
}
BigInteger bigInteger = (BigInteger) value;
int toMax = bigInteger.compareTo(max);
int toMin = bigInteger.compareTo(min);
return toMax <= 0 && toMin >= 0;
}
private static boolean testDouble(@NotNull String leftTypeQName, String qName, @NotNull Number value, double min, double max)
{
if(!(leftTypeQName.equals(qName)))
{
return false;
}
double l = value.doubleValue();
return l <= max && l >= min;
}
}