class CastUtils {

    public static long parseInRange(String st, long max, long min, TExecutionContext context)
    {
        Object truncated;

        // first attempt
        try
        {
            return CastUtils.getInRange(max, min, Long.parseLong(st), context);
        }
        catch (NumberFormatException e) // This could be an overflow, but there is no way to know
        {
            truncated = CastUtils.truncateNonDigits(st, context);
        }

        // second attemp
        if (truncated instanceof String)
        {
            String truncatedStr = (String)truncated;
            try
            {
                return CastUtils.getInRange(max, min, Long.parseLong(truncatedStr), context);
            }
            catch (NumberFormatException e) // overflow
            {
                context.reportOverflow(e.getMessage());
                
                // check wether the number is too big or too small
                char first = truncatedStr.charAt(0);
                if (first == '-')
                    return getInRange(max, min, Long.MIN_VALUE, context);
                else
                    return getInRange(max, min, Long.MAX_VALUE, context);
            }
        }
        else // must be a BigDecimal object
        {
            BigDecimal num = (BigDecimal)truncated;
            
            // check overflow
            if (num.compareTo(MAX_LONG) > 0)
            {
                context.reportTruncate(st, Long.toString(max));
                return max;
            }
            else if (num.compareTo(MIN_LONG) < 0)
            {
                context.reportTruncate(st, Long.toString(min));
                return min;
            }

            try
            {
                return getInRange(max, min, num.longValueExact(), context);
            }
            catch (ArithmeticException e) // has non-zero fractional parts
            {
                long ret = num.setScale(0, RoundingMode.HALF_UP).longValue();
                context.reportTruncate(st, Long.toString(ret));
                return getInRange(max, min, ret, context);
            }
        }
    }

}