Exemplo n.º 1
0
 internal override SqlExpression VisitBinaryOperator(SqlBinary bo)
 {
     if (IsCompareToValue(bo))
     {
         SqlMethodCall call = (SqlMethodCall)bo.Left;
         if (IsCompareToMethod(call))
         {
             int iValue = System.Convert.ToInt32(this.Eval(bo.Right), Globalization.CultureInfo.InvariantCulture);
             bo = this.MakeCompareTo(call.Object, call.Arguments[0], bo.NodeType, iValue) ?? bo;
         }
         else if (IsCompareMethod(call))
         {
             int iValue = System.Convert.ToInt32(this.Eval(bo.Right), Globalization.CultureInfo.InvariantCulture);
             bo = this.MakeCompareTo(call.Arguments[0], call.Arguments[1], bo.NodeType, iValue) ?? bo;
         }
     }
     else if (IsVbCompareStringEqualsValue(bo))
     {
         SqlMethodCall call   = (SqlMethodCall)bo.Left;
         int           iValue = System.Convert.ToInt32(this.Eval(bo.Right), Globalization.CultureInfo.InvariantCulture);
         //in VB, comparing a string with Nothing means comparing with ""
         SqlValue strValue = call.Arguments[1] as SqlValue;
         if (strValue != null && strValue.Value == null)
         {
             SqlValue emptyStr = new SqlValue(strValue.ClrType, strValue.SqlType, String.Empty, strValue.IsClientSpecified, strValue.SourceExpression);
             bo = this.MakeCompareTo(call.Arguments[0], emptyStr, bo.NodeType, iValue) ?? bo;
         }
         else
         {
             bo = this.MakeCompareTo(call.Arguments[0], call.Arguments[1], bo.NodeType, iValue) ?? bo;
         }
     }
     return(base.VisitBinaryOperator(bo));
 }
Exemplo n.º 2
0
 internal virtual SqlExpression VisitMethodCall(SqlMethodCall mc)
 {
     mc.Object = this.VisitExpression(mc.Object);
     for (int i = 0, n = mc.Arguments.Count; i < n; i++)
     {
         mc.Arguments[i] = this.VisitExpression(mc.Arguments[i]);
     }
     return(mc);
 }
 internal override SqlExpression VisitMethodCall(SqlMethodCall mc)
 {
     SqlExpression[] args = new SqlExpression[mc.Arguments.Count];
     for (int i = 0, n = mc.Arguments.Count; i < n; i++)
     {
         args[i] = this.VisitExpression(mc.Arguments[i]);
     }
     return(new SqlMethodCall(mc.ClrType, mc.SqlType, mc.Method, this.VisitExpression(mc.Object), args, mc.SourceExpression));
 }
 internal override SqlExpression VisitMethodCall(SqlMethodCall mc)
 {
     mc.Object = this.VisitExpression(mc.Object);
     System.Reflection.ParameterInfo[] pis = mc.Method.GetParameters();
     for (int i = 0, n = mc.Arguments.Count; i < n; i++)
     {
         mc.Arguments[i] = this.VisitNamedExpression(mc.Arguments[i], pis[i].Name);
     }
     return(mc);
 }
Exemplo n.º 5
0
            internal override SqlExpression VisitMethodCall(SqlMethodCall mc)
            {
                var array = new SqlExpression[mc.Arguments.Count];
                var i     = 0;

                for (var count = mc.Arguments.Count; i < count; i++)
                {
                    array[i] = VisitExpression(mc.Arguments[i]);
                }
                return(new SqlMethodCall(mc.ClrType, mc.SqlType, mc.Method, VisitExpression(mc.Object), array, mc.SourceExpression));
            }
Exemplo n.º 6
0
        private static bool IsCompareToValue(SqlBinary bo)
        {
            if (IsComparison(bo.NodeType) &&
                bo.Left.NodeType == SqlNodeType.MethodCall &&
                bo.Right.NodeType == SqlNodeType.Value)
            {
                SqlMethodCall call = (SqlMethodCall)bo.Left;
                return(IsCompareToMethod(call) || IsCompareMethod(call));
            }

            return(false);
        }
Exemplo n.º 7
0
 private SqlExpression TranslateVbIIF(SqlMethodCall mc)
 {
     //Check to see if the types can be implicitly converted from one to another.
     if (mc.Arguments[1].ClrType == mc.Arguments[2].ClrType)
     {
         List <SqlWhen> whens = new List <SqlWhen>(1);
         whens.Add(new SqlWhen(mc.Arguments[0], mc.Arguments[1]));
         SqlExpression @else = mc.Arguments[2];
         while (@else.NodeType == SqlNodeType.SearchedCase)
         {
             SqlSearchedCase sc = (SqlSearchedCase)@else;
             whens.AddRange(sc.Whens);
             @else = sc.Else;
         }
         return(sql.SearchedCase(whens.ToArray(), @else, mc.SourceExpression));
     }
     else
     {
         throw Error.IifReturnTypesMustBeEqual(mc.Arguments[1].ClrType.Name, mc.Arguments[2].ClrType.Name);
     }
 }
Exemplo n.º 8
0
        internal static bool CanConvert(SqlNode node)
        {
            SqlBinary bo = node as SqlBinary;

            if (bo != null && (IsCompareToValue(bo) || IsVbCompareStringEqualsValue(bo)))
            {
                return(true);
            }
            SqlMember sm = node as SqlMember;

            if (sm != null && IsSupportedMember(sm))
            {
                return(true);
            }
            SqlMethodCall mc = node as SqlMethodCall;

            if (mc != null && (IsSupportedMethod(mc) || IsSupportedVbHelperMethod(mc)))
            {
                return(true);
            }
            return(false);
        }
Exemplo n.º 9
0
        private static bool IsSupportedMethod(SqlMethodCall mc)
        {
            if (mc.Method.IsStatic)
            {
                switch (mc.Method.Name)
                {
                case "op_Equality":
                case "op_Inequality":
                case "op_LessThan":
                case "op_LessThanOrEqual":
                case "op_GreaterThan":
                case "op_GreaterThanOrEqual":
                case "op_Multiply":
                case "op_Division":
                case "op_Subtraction":
                case "op_Addition":
                case "op_Modulus":
                case "op_BitwiseAnd":
                case "op_BitwiseOr":
                case "op_ExclusiveOr":
                case "op_UnaryNegation":
                case "op_OnesComplement":
                case "op_False":
                    return(true);

                case "Equals":
                    return(mc.Arguments.Count == 2);

                case "Concat":
                    return(mc.Method.DeclaringType == typeof(string));
                }
            }
            else
            {
                return(mc.Method.Name == "Equals" && mc.Arguments.Count == 1 ||
                       mc.Method.Name == "GetType" && mc.Arguments.Count == 0);
            }
            return(false);
        }
Exemplo n.º 10
0
            private SqlExpression ExpandTogether(List <SqlExpression> exprs)
            {
                switch (exprs[0].NodeType)
                {
                case SqlNodeType.MethodCall: {
                    SqlMethodCall[] mcs = new SqlMethodCall[exprs.Count];
                    for (int i = 0; i < mcs.Length; ++i)
                    {
                        mcs[i] = (SqlMethodCall)exprs[i];
                    }

                    List <SqlExpression> expandedArgs = new List <SqlExpression>();

                    for (int i = 0; i < mcs[0].Arguments.Count; ++i)
                    {
                        List <SqlExpression> args = new List <SqlExpression>();
                        for (int j = 0; j < mcs.Length; ++j)
                        {
                            args.Add(mcs[j].Arguments[i]);
                        }
                        SqlExpression expanded = this.ExpandTogether(args);
                        expandedArgs.Add(expanded);
                    }
                    return(factory.MethodCall(mcs[0].Method, mcs[0].Object, expandedArgs.ToArray(), mcs[0].SourceExpression));
                }

                case SqlNodeType.ClientCase: {
                    // Are they all the same?
                    SqlClientCase[] scs = new SqlClientCase[exprs.Count];
                    scs[0] = (SqlClientCase)exprs[0];
                    for (int i = 1; i < scs.Length; ++i)
                    {
                        scs[i] = (SqlClientCase)exprs[i];
                    }

                    // Expand expressions together.
                    List <SqlExpression> expressions = new List <SqlExpression>();
                    for (int i = 0; i < scs.Length; ++i)
                    {
                        expressions.Add(scs[i].Expression);
                    }
                    SqlExpression expression = this.ExpandTogether(expressions);

                    // Expand individual expressions together.
                    List <SqlClientWhen> whens = new List <SqlClientWhen>();
                    for (int i = 0; i < scs[0].Whens.Count; ++i)
                    {
                        List <SqlExpression> scos = new List <SqlExpression>();
                        for (int j = 0; j < scs.Length; ++j)
                        {
                            SqlClientWhen when = scs[j].Whens[i];
                            scos.Add(when.Value);
                        }
                        whens.Add(new SqlClientWhen(scs[0].Whens[i].Match, this.ExpandTogether(scos)));
                    }

                    return(new SqlClientCase(scs[0].ClrType, expression, whens, scs[0].SourceExpression));
                }

                case SqlNodeType.TypeCase: {
                    // Are they all the same?
                    SqlTypeCase[] tcs = new SqlTypeCase[exprs.Count];
                    tcs[0] = (SqlTypeCase)exprs[0];
                    for (int i = 1; i < tcs.Length; ++i)
                    {
                        tcs[i] = (SqlTypeCase)exprs[i];
                    }

                    // Expand discriminators together.
                    List <SqlExpression> discriminators = new List <SqlExpression>();
                    for (int i = 0; i < tcs.Length; ++i)
                    {
                        discriminators.Add(tcs[i].Discriminator);
                    }
                    SqlExpression discriminator = this.ExpandTogether(discriminators);
                    // Write expanded discriminators back in.
                    for (int i = 0; i < tcs.Length; ++i)
                    {
                        tcs[i].Discriminator = discriminators[i];
                    }
                    // Expand individual type bindings together.
                    List <SqlTypeCaseWhen> whens = new List <SqlTypeCaseWhen>();
                    for (int i = 0; i < tcs[0].Whens.Count; ++i)
                    {
                        List <SqlExpression> scos = new List <SqlExpression>();
                        for (int j = 0; j < tcs.Length; ++j)
                        {
                            SqlTypeCaseWhen when = tcs[j].Whens[i];
                            scos.Add(when.TypeBinding);
                        }
                        SqlExpression expanded = this.ExpandTogether(scos);
                        whens.Add(new SqlTypeCaseWhen(tcs[0].Whens[i].Match, expanded));
                    }

                    return(factory.TypeCase(tcs[0].ClrType, tcs[0].RowType, discriminator, whens, tcs[0].SourceExpression));
                }

                case SqlNodeType.New: {
                    // first verify all are similar client objects...
                    SqlNew[] cobs = new SqlNew[exprs.Count];
                    cobs[0] = (SqlNew)exprs[0];
                    for (int i = 1, n = exprs.Count; i < n; i++)
                    {
                        if (exprs[i] == null || exprs[i].NodeType != SqlNodeType.New)
                        {
                            throw Error.UnionIncompatibleConstruction();
                        }
                        cobs[i] = (SqlNew)exprs[1];
                        if (cobs[i].Members.Count != cobs[0].Members.Count)
                        {
                            throw Error.UnionDifferentMembers();
                        }
                        for (int m = 0, mn = cobs[0].Members.Count; m < mn; m++)
                        {
                            if (cobs[i].Members[m].Member != cobs[0].Members[m].Member)
                            {
                                throw Error.UnionDifferentMemberOrder();
                            }
                        }
                    }
                    SqlMemberAssign[] bindings = new SqlMemberAssign[cobs[0].Members.Count];
                    for (int m = 0, mn = bindings.Length; m < mn; m++)
                    {
                        List <SqlExpression> mexprs = new List <SqlExpression>();
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            mexprs.Add(cobs[i].Members[m].Expression);
                        }
                        bindings[m] = new SqlMemberAssign(cobs[0].Members[m].Member, this.ExpandTogether(mexprs));
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            cobs[i].Members[m].Expression = mexprs[i];
                        }
                    }
                    SqlExpression[] arguments = new SqlExpression[cobs[0].Args.Count];
                    for (int m = 0, mn = arguments.Length; m < mn; ++m)
                    {
                        List <SqlExpression> mexprs = new List <SqlExpression>();
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            mexprs.Add(cobs[i].Args[m]);
                        }
                        arguments[m] = ExpandTogether(mexprs);
                    }
                    return(factory.New(cobs[0].MetaType, cobs[0].Constructor, arguments, cobs[0].ArgMembers, bindings, exprs[0].SourceExpression));
                }

                case SqlNodeType.Link: {
                    SqlLink[] links = new SqlLink[exprs.Count];
                    links[0] = (SqlLink)exprs[0];
                    for (int i = 1, n = exprs.Count; i < n; i++)
                    {
                        if (exprs[i] == null || exprs[i].NodeType != SqlNodeType.Link)
                        {
                            throw Error.UnionIncompatibleConstruction();
                        }
                        links[i] = (SqlLink)exprs[i];
                        if (links[i].KeyExpressions.Count != links[0].KeyExpressions.Count ||
                            links[i].Member != links[0].Member ||
                            (links[i].Expansion != null) != (links[0].Expansion != null))
                        {
                            throw Error.UnionIncompatibleConstruction();
                        }
                    }
                    SqlExpression[]      kexprs = new SqlExpression[links[0].KeyExpressions.Count];
                    List <SqlExpression> lexprs = new List <SqlExpression>();
                    for (int k = 0, nk = links[0].KeyExpressions.Count; k < nk; k++)
                    {
                        lexprs.Clear();
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            lexprs.Add(links[i].KeyExpressions[k]);
                        }
                        kexprs[k] = this.ExpandTogether(lexprs);
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            links[i].KeyExpressions[k] = lexprs[i];
                        }
                    }
                    SqlExpression expansion = null;
                    if (links[0].Expansion != null)
                    {
                        lexprs.Clear();
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            lexprs.Add(links[i].Expansion);
                        }
                        expansion = this.ExpandTogether(lexprs);
                        for (int i = 0, n = exprs.Count; i < n; i++)
                        {
                            links[i].Expansion = lexprs[i];
                        }
                    }
                    return(new SqlLink(links[0].Id, links[0].RowType, links[0].ClrType, links[0].SqlType, links[0].Expression, links[0].Member, kexprs, expansion, links[0].SourceExpression));
                }

                case SqlNodeType.Value: {
                    /*
                     * ExprSet of all literals of the same value reduce to just a single literal.
                     */
                    SqlValue val0 = (SqlValue)exprs[0];
                    for (int i = 1; i < exprs.Count; ++i)
                    {
                        SqlValue val = (SqlValue)exprs[i];
                        if (!object.Equals(val.Value, val0.Value))
                        {
                            return(this.ExpandIntoExprSet(exprs));
                        }
                    }
                    return(val0);
                }

                case SqlNodeType.OptionalValue: {
                    if (exprs[0].SqlType.CanBeColumn)
                    {
                        goto default;
                    }
                    List <SqlExpression> hvals = new List <SqlExpression>(exprs.Count);
                    List <SqlExpression> vals  = new List <SqlExpression>(exprs.Count);
                    for (int i = 0, n = exprs.Count; i < n; i++)
                    {
                        if (exprs[i] == null || exprs[i].NodeType != SqlNodeType.OptionalValue)
                        {
                            throw Error.UnionIncompatibleConstruction();
                        }
                        SqlOptionalValue sov = (SqlOptionalValue)exprs[i];
                        hvals.Add(sov.HasValue);
                        vals.Add(sov.Value);
                    }
                    return(new SqlOptionalValue(this.ExpandTogether(hvals), this.ExpandTogether(vals)));
                }

                case SqlNodeType.OuterJoinedValue: {
                    if (exprs[0].SqlType.CanBeColumn)
                    {
                        goto default;
                    }
                    List <SqlExpression> values = new List <SqlExpression>(exprs.Count);
                    for (int i = 0, n = exprs.Count; i < n; i++)
                    {
                        if (exprs[i] == null || exprs[i].NodeType != SqlNodeType.OuterJoinedValue)
                        {
                            throw Error.UnionIncompatibleConstruction();
                        }
                        SqlUnary su = (SqlUnary)exprs[i];
                        values.Add(su.Operand);
                    }
                    return(factory.Unary(SqlNodeType.OuterJoinedValue, this.ExpandTogether(values)));
                }

                case SqlNodeType.DiscriminatedType: {
                    SqlDiscriminatedType sdt0 = (SqlDiscriminatedType)exprs[0];
                    List <SqlExpression> foos = new List <SqlExpression>(exprs.Count);
                    foos.Add(sdt0.Discriminator);
                    for (int i = 1, n = exprs.Count; i < n; i++)
                    {
                        SqlDiscriminatedType sdtN = (SqlDiscriminatedType)exprs[i];
                        if (sdtN.TargetType != sdt0.TargetType)
                        {
                            throw Error.UnionIncompatibleConstruction();
                        }
                        foos.Add(sdtN.Discriminator);
                    }
                    return(factory.DiscriminatedType(this.ExpandTogether(foos), ((SqlDiscriminatedType)exprs[0]).TargetType));
                }

                case SqlNodeType.ClientQuery:
                case SqlNodeType.Multiset:
                case SqlNodeType.Element:
                case SqlNodeType.Grouping:
                    throw Error.UnionWithHierarchy();

                default:
                    return(this.ExpandIntoExprSet(exprs));
                }
            }
Exemplo n.º 11
0
 private static bool IsSupportedVbHelperMethod(SqlMethodCall mc)
 {
     return(IsVbIIF(mc));
 }
Exemplo n.º 12
0
 private static bool IsVbCompareString(SqlMethodCall call)
 {
     return(call.Method.IsStatic &&
            call.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators" &&
            call.Method.Name == "CompareString");
 }
Exemplo n.º 13
0
 private static bool IsCompareMethod(SqlMethodCall call)
 {
     return(call.Method.IsStatic && call.Method.Name == "Compare" && call.Arguments.Count > 1 && call.Method.ReturnType == typeof(int));
 }
Exemplo n.º 14
0
            internal override SqlExpression VisitMethodCall(SqlMethodCall mc)
            {
                mc.Object = this.VisitExpression(mc.Object);
                for (int i = 0, n = mc.Arguments.Count; i < n; i++)
                {
                    mc.Arguments[i] = this.VisitExpression(mc.Arguments[i]);
                }
                if (mc.Method.IsStatic)
                {
                    if (mc.Method.Name == "Equals" && mc.Arguments.Count == 2)
                    {
                        return(sql.Binary(SqlNodeType.EQ2V, mc.Arguments[0], mc.Arguments[1], mc.Method));
                    }
                    else if (mc.Method.DeclaringType == typeof(string) && mc.Method.Name == "Concat")
                    {
                        SqlClientArray       arr   = mc.Arguments[0] as SqlClientArray;
                        List <SqlExpression> exprs = null;
                        if (arr != null)
                        {
                            exprs = arr.Expressions;
                        }
                        else
                        {
                            exprs = mc.Arguments;
                        }
                        if (exprs.Count == 0)
                        {
                            return(sql.ValueFromObject("", false, mc.SourceExpression));
                        }
                        else
                        {
                            SqlExpression sum;
                            if (exprs[0].SqlType.IsString || exprs[0].SqlType.IsChar)
                            {
                                sum = exprs[0];
                            }
                            else
                            {
                                sum = sql.ConvertTo(typeof(string), exprs[0]);
                            }
                            for (int i = 1; i < exprs.Count; i++)
                            {
                                if (exprs[i].SqlType.IsString || exprs[i].SqlType.IsChar)
                                {
                                    sum = sql.Concat(sum, exprs[i]);
                                }
                                else
                                {
                                    sum = sql.Concat(sum, sql.ConvertTo(typeof(string), exprs[i]));
                                }
                            }
                            return(sum);
                        }
                    }
                    else if (IsVbIIF(mc))
                    {
                        return(TranslateVbIIF(mc));
                    }
                    else
                    {
                        switch (mc.Method.Name)
                        {
                        case "op_Equality":
                            return(sql.Binary(SqlNodeType.EQ, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_Inequality":
                            return(sql.Binary(SqlNodeType.NE, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_LessThan":
                            return(sql.Binary(SqlNodeType.LT, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_LessThanOrEqual":
                            return(sql.Binary(SqlNodeType.LE, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_GreaterThan":
                            return(sql.Binary(SqlNodeType.GT, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_GreaterThanOrEqual":
                            return(sql.Binary(SqlNodeType.GE, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_Multiply":
                            return(sql.Binary(SqlNodeType.Mul, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_Division":
                            return(sql.Binary(SqlNodeType.Div, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_Subtraction":
                            return(sql.Binary(SqlNodeType.Sub, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_Addition":
                            return(sql.Binary(SqlNodeType.Add, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_Modulus":
                            return(sql.Binary(SqlNodeType.Mod, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_BitwiseAnd":
                            return(sql.Binary(SqlNodeType.BitAnd, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_BitwiseOr":
                            return(sql.Binary(SqlNodeType.BitOr, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_ExclusiveOr":
                            return(sql.Binary(SqlNodeType.BitXor, mc.Arguments[0], mc.Arguments[1], mc.Method, mc.ClrType));

                        case "op_UnaryNegation":
                            return(sql.Unary(SqlNodeType.Negate, mc.Arguments[0], mc.Method, mc.SourceExpression));

                        case "op_OnesComplement":
                            return(sql.Unary(SqlNodeType.BitNot, mc.Arguments[0], mc.Method, mc.SourceExpression));

                        case "op_False":
                            return(sql.Unary(SqlNodeType.Not, mc.Arguments[0], mc.Method, mc.SourceExpression));
                        }
                    }
                }
                else
                {
                    if (mc.Method.Name == "Equals" && mc.Arguments.Count == 1)
                    {
                        return(sql.Binary(SqlNodeType.EQ, mc.Object, mc.Arguments[0]));
                    }
                    else if (mc.Method.Name == "GetType" && mc.Arguments.Count == 0)
                    {
                        MetaType mt = TypeSource.GetSourceMetaType(mc.Object, this.model);
                        if (mt.HasInheritance)
                        {
                            Type discriminatorType             = mt.Discriminator.Type;
                            SqlDiscriminatorOf discriminatorOf = new SqlDiscriminatorOf(mc.Object, discriminatorType, this.sql.TypeProvider.From(discriminatorType), mc.SourceExpression);
                            return(this.VisitExpression(sql.DiscriminatedType(discriminatorOf, mt)));
                        }
                        return(this.VisitExpression(sql.StaticType(mt, mc.SourceExpression)));
                    }
                }
                return(mc);
            }
Exemplo n.º 15
0
 private static bool IsVbIIF(SqlMethodCall mc)
 {
     return(mc.Method.IsStatic &&
            mc.Method.DeclaringType.FullName == "Microsoft.VisualBasic.Interaction" && mc.Method.Name == "IIf");
 }
Exemplo n.º 16
0
 internal override SqlExpression VisitMethodCall(SqlMethodCall mc)
 {
     // eventually we may support this type of stuff given the SQL CLR, but for now it is illegal
     throw Error.MethodHasNoSupportConversionToSql(mc.Method.Name);
 }