CaseStatement.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Structures / CaseStatement.cs / 1305376 / CaseStatement.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

 
using System.Data.Common.Utils;
using System.Linq;
using System.Text;
using System.Collections.Generic; 
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Metadata.Edm; 
using System.Diagnostics; 

namespace System.Data.Mapping.ViewGeneration.Structures 
{

    // effects: A class to denote a case statement
    // CASE 
    // WHEN  THEN 
    // WHEN  THEN  
    // ... 
    // ENDCASE
    internal class CaseStatement : InternalBase 
    {

        #region Constructors
        ///  
        /// effects: Creates a case statement for the field "memberPath"
        /// with no clauses 
        ///  
        internal CaseStatement(MemberPath memberPath)
        { 
            m_memberPath = memberPath;
            m_clauses = new List();
        }
        #endregion 

        #region Fields 
        private MemberPath m_memberPath; // The field 
        private List m_clauses; // All the WHEN THENs
        private ProjectedSlot m_elseValue; // Value for the else clause 

        #endregion

        #region Properties 
        internal MemberPath MemberPath
        { 
            get { return m_memberPath; } 
        }
 
        // CHANGE_[....]_IMPROVE: This can actually be removed if we refactor
        // CqlGenerator a little bit more
        internal List Clauses
        { 
            get { return m_clauses; }
        } 
 
        internal ProjectedSlot ElseValue
        { 
            get { return m_elseValue; }
        }
        #endregion
 
        #region Methods
        // effects: Converts all "relevant" ProjectedSlots in this (including 
        // this) to aliased slots for CqlBlock "block". A slot type is 
        // relevant if it is not a constant. "outputPath" is the output path
        // of this case statement in the final view 
        internal CaseStatement MakeCaseWithAliasedSlots(CqlBlock block, MemberPath outputPath, int slotNum)
        {
            // Go through the whenthens and else and make a new case statement with
            // aliased slots as needed 
            CaseStatement result = new CaseStatement(m_memberPath);
            foreach (WhenThen whenThen in m_clauses) 
            { 
                WhenThen newClause = whenThen.ReplaceWithAliasedSlot(block, outputPath, slotNum);
                result.m_clauses.Add(newClause); 
            }
            if (m_elseValue != null)
            {
                result.m_elseValue = m_elseValue.MakeAliasedSlot(block, outputPath, slotNum); 
            }
            return result; 
        } 

        // requires: condition has not been added to this 
        // effects: Adds an expression of the form WHEN  THEN
        //  for this variable
        internal void AddWhenThen(BoolExpression condition, ProjectedSlot value)
        { 
            // We should probably validate that the expression is not there?
            Debug.Assert(value != null); 
            condition.ExpensiveSimplify(); 
            m_clauses.Add(new WhenThen(condition, value));
        } 

        // effects: returns true if the case statement requires the value of the member
        // in THEN  or ELSE 
        internal bool DependsOnMemberValue 
        {
            get 
            { 
                if (m_elseValue is MemberProjectedSlot)
                { 
                    return true;
                }
                foreach (WhenThen whenThen in m_clauses)
                { 
                    if (whenThen.Value is MemberProjectedSlot)
                    { 
                        return true; 
                    }
                } 
                return false;
            }
        }
 
        internal IEnumerable InstantiatedTypes
        { 
            get 
            {
                foreach (WhenThen whenThen in m_clauses) 
                {
                    EdmType type;
                    if (TryGetInstantiatedType(whenThen.Value, out type))
                    { 
                        yield return type;
                    } 
                } 
                EdmType elseType;
                if (TryGetInstantiatedType(m_elseValue, out elseType)) 
                {
                    yield return elseType;
                }
            } 
        }
        private bool TryGetInstantiatedType(ProjectedSlot slot, out EdmType type) 
        { 
            type = null;
            ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot; 
            if (constantSlot != null)
            {
                TypeConstant typeConstant = constantSlot.CellConstant as TypeConstant;
                if (typeConstant != null) 
                {
                    type = typeConstant.CdmType; 
                    return true; 
                }
            } 
            return false;
        }

 
        // effects: Simplifies the case statement so that unnecessary when
        // thens for nulls/undefined values are eliminated. Also, adds an 
        // else clause if possible. 
        internal void Simplify()
        { 
            // TODDO_[....]_LOW: Make this call idempotent?
            Debug.Assert(m_elseValue == null, "Already been simplified -- call is not idempotent yet");
            List clauses = new List();
            // remove all WHEN clauses where the value gets set to "undefined" 
            // We eliminate the last clause for now - we could determine the
            // "most complicated" WHEN clause and eliminate it 
            bool eliminatedNullClauses = false; 
            foreach (WhenThen clause in m_clauses)
            { 
                ConstantProjectedSlot constantSlot = clause.Value as ConstantProjectedSlot;
                // If null or undefined, remove it
                if (constantSlot != null && (constantSlot.CellConstant.IsNull() || constantSlot.CellConstant.IsUndefined()))
                { 
                    eliminatedNullClauses = true;
                } 
                else 
                {
                    clauses.Add(clause); 
                    if (clause.Condition.IsTrue)
                    {
                        // none of subsequent case statements will be evaluated - ignore them
                        break; 
                    }
                } 
            } 

            if (eliminatedNullClauses && clauses.Count == 0) 
            {
                // There is nothing left -- we should add a null as the value
                m_elseValue = new ConstantProjectedSlot(Constant.Null);
            } 

            // If we eliminated some undefined or null clauses, we do not want an else clause 
            if (clauses.Count > 0 && false == eliminatedNullClauses) 
            {
                // turn the last WHEN clause into an ELSE 
                int lastIndex = clauses.Count - 1;
                m_elseValue = clauses[lastIndex].Value;
                clauses.RemoveAt(lastIndex);
            } 
            m_clauses = clauses;
        } 
 

        // effects: Given a caseStatement, generates the CQL for 
        // it. blockAlias is used whenever the value in the WHEN or THEN is not an aliased slot
        internal StringBuilder AsCql(StringBuilder builder, IEnumerable withStatements, string blockAlias, int indentLevel)
        {
            // If the variable is a relation end, we will gets it scope Extent, e.g., CPerson1 for an 
            // the CPerson end of CPersonAddress1
            StringUtil.IndentNewLine(builder, indentLevel + 1); 
 
            if (Clauses.Count == 0)
            { 
                // this is just a single ELSE: no condition at all
                CaseSlotValueAsCql(builder, ElseValue, MemberPath, blockAlias);
                AppendWithBlock(builder, withStatements, blockAlias, indentLevel, ElseValue);
                return builder; 
            }
 
            // Generate the Case WHEN .. THEN ..., WHEN ... THEN ..., END 
            builder.Append("CASE");
            foreach (CaseStatement.WhenThen clause in Clauses) 
            {
                StringUtil.IndentNewLine(builder, indentLevel + 2);
                builder.Append("WHEN ");
                clause.Condition.AsCql(builder, blockAlias); 
                builder.Append(" THEN ");
                CaseSlotValueAsCql(builder, clause.Value, MemberPath, blockAlias); 
                AppendWithBlock(builder, withStatements, blockAlias, indentLevel + 2, clause.Value); 
            }
 
            if (ElseValue != null)
            {
                StringUtil.IndentNewLine(builder, indentLevel + 2);
                builder.Append("ELSE "); 
                CaseSlotValueAsCql(builder, ElseValue, MemberPath, blockAlias);
                AppendWithBlock(builder, withStatements, blockAlias, indentLevel + 2, ElseValue); 
            } 
            StringUtil.IndentNewLine(builder, indentLevel + 1);
            builder.Append("END"); 
            return builder;
        }

        private void AppendWithBlock(StringBuilder builder, IEnumerable withStatements, string blockAlias, int indentLevel, ProjectedSlot projectedSlot) 
        {
            if ((withStatements != null) && (withStatements.Count() > 0)) 
            { 

                ConstantProjectedSlot typeConstantSlot = projectedSlot as ConstantProjectedSlot; 
                if (typeConstantSlot != null)
                {
                    TypeConstant typeConstant = typeConstantSlot.CellConstant as TypeConstant;
                    if (typeConstant != null) 
                    {
                        AppendWithBlock(builder, withStatements, typeConstant.CdmType, blockAlias, indentLevel); 
                    } 
                    else
                    { 
                        Debug.Assert(false, "with block should be there for type constant only");
                    }
                }
                else 
                {
                    Debug.Assert(false, "with block should be there for type constant slot only"); 
                } 
            }
        } 

        private void AppendWithBlock(StringBuilder builder, IEnumerable withStatements, EdmType fromType,
            string blockAlias, int indentLevel)
        { 
            bool appendWithKeyword = true;
            foreach (WithStatement withStatement in withStatements) 
            { 
                //Add With statement for the types that participate in the association
                if (withStatement.EntityTypeForFromEnd.IsAssignableFrom(fromType)) 
                {
                    if (appendWithKeyword)
                    {
                        appendWithKeyword = false; 
                        builder.Append(" WITH ");
                    } 
 
                    withStatement.AsCql(builder, blockAlias, indentLevel);
                } 
            }

        }
 
        // requires: This method is called by CaseSlotValuetoCql and
        // slot is not a boolean slot 
        // effects: Given a slot and its corresponding outputMember, modifies 
        // builder to contain the slot value
        private static StringBuilder CaseSlotValueAsCql(StringBuilder builder, ProjectedSlot slot, 
                                               MemberPath outputMember, string blockAlias)
        {
            // CHANGE_[....]_DESIGN: We will never have THEN as a boolean slot
            Debug.Assert(slot is MemberProjectedSlot || slot is AliasedSlot || slot is ConstantProjectedSlot, 
                         "Case statement THEN can only have constants or members");
            slot.AsCql(builder, outputMember, blockAlias, 1); 
            return builder; 
        }
 
        // Repeat of AsCql but don't have all the info
        internal override void ToCompactString(StringBuilder builder)
        {
            // We have to duplicate the functionality of AsCql here since 
            // ToCompactString can get called at times when the THEN parts contain
            // Undefined values -- in that case, a Debug.Assert in CellConstant's AsCql 
            // fires off 

            builder.AppendLine("CASE"); 
            foreach (WhenThen clause in m_clauses)
            {
                builder.Append("    WHEN ");
                clause.Condition.ToCompactString(builder); 
                builder.Append(" THEN ");
                clause.Value.ToCompactString(builder); 
                builder.AppendLine(); 
            }
            if (m_elseValue != null) 
            {
                builder.Append("    ELSE ");
                m_elseValue.ToCompactString(builder);
                builder.AppendLine(); 
            }
            builder.Append("END AS "); 
            m_memberPath.ToCompactString(builder); 
        }
        #endregion 

        // effects: A class that stores WHEN  THEN 
        internal class WhenThen : InternalBase
        { 

            #region Constructor 
            // effects: Creates WHEN  THEN  
            internal WhenThen(BoolExpression condition, ProjectedSlot value)
            { 
                m_condition = condition;
                m_value = value;
            }
            #endregion 

            #region Fields 
            private BoolExpression m_condition; 
            private ProjectedSlot m_value;
            #endregion 

            #region Properties
            /// 
            /// effects: Returns the when condition 
            /// 
            internal BoolExpression Condition 
            { 
                get { return m_condition; }
            } 

            /// 
            /// effects: Returns the value for the when case
            ///  
            internal ProjectedSlot Value
            { 
                get { return m_value; } 
            }
            #endregion 

            #region String Methods
            internal WhenThen ReplaceWithAliasedSlot(CqlBlock block, MemberPath outputPath, int slotNum)
            { 
                // Change the THEN part
                ProjectedSlot newValue = m_value.MakeAliasedSlot(block, outputPath, slotNum); 
                return new WhenThen(m_condition, newValue); 
            }
 
            internal override void ToCompactString(StringBuilder builder)
            {
                builder.Append("WHEN ");
                m_condition.ToCompactString(builder); 
                builder.Append("THEN ");
                m_value.ToCompactString(builder); 
            } 
            #endregion
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

 
using System.Data.Common.Utils;
using System.Linq;
using System.Text;
using System.Collections.Generic; 
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Metadata.Edm; 
using System.Diagnostics; 

namespace System.Data.Mapping.ViewGeneration.Structures 
{

    // effects: A class to denote a case statement
    // CASE 
    // WHEN  THEN 
    // WHEN  THEN  
    // ... 
    // ENDCASE
    internal class CaseStatement : InternalBase 
    {

        #region Constructors
        ///  
        /// effects: Creates a case statement for the field "memberPath"
        /// with no clauses 
        ///  
        internal CaseStatement(MemberPath memberPath)
        { 
            m_memberPath = memberPath;
            m_clauses = new List();
        }
        #endregion 

        #region Fields 
        private MemberPath m_memberPath; // The field 
        private List m_clauses; // All the WHEN THENs
        private ProjectedSlot m_elseValue; // Value for the else clause 

        #endregion

        #region Properties 
        internal MemberPath MemberPath
        { 
            get { return m_memberPath; } 
        }
 
        // CHANGE_[....]_IMPROVE: This can actually be removed if we refactor
        // CqlGenerator a little bit more
        internal List Clauses
        { 
            get { return m_clauses; }
        } 
 
        internal ProjectedSlot ElseValue
        { 
            get { return m_elseValue; }
        }
        #endregion
 
        #region Methods
        // effects: Converts all "relevant" ProjectedSlots in this (including 
        // this) to aliased slots for CqlBlock "block". A slot type is 
        // relevant if it is not a constant. "outputPath" is the output path
        // of this case statement in the final view 
        internal CaseStatement MakeCaseWithAliasedSlots(CqlBlock block, MemberPath outputPath, int slotNum)
        {
            // Go through the whenthens and else and make a new case statement with
            // aliased slots as needed 
            CaseStatement result = new CaseStatement(m_memberPath);
            foreach (WhenThen whenThen in m_clauses) 
            { 
                WhenThen newClause = whenThen.ReplaceWithAliasedSlot(block, outputPath, slotNum);
                result.m_clauses.Add(newClause); 
            }
            if (m_elseValue != null)
            {
                result.m_elseValue = m_elseValue.MakeAliasedSlot(block, outputPath, slotNum); 
            }
            return result; 
        } 

        // requires: condition has not been added to this 
        // effects: Adds an expression of the form WHEN  THEN
        //  for this variable
        internal void AddWhenThen(BoolExpression condition, ProjectedSlot value)
        { 
            // We should probably validate that the expression is not there?
            Debug.Assert(value != null); 
            condition.ExpensiveSimplify(); 
            m_clauses.Add(new WhenThen(condition, value));
        } 

        // effects: returns true if the case statement requires the value of the member
        // in THEN  or ELSE 
        internal bool DependsOnMemberValue 
        {
            get 
            { 
                if (m_elseValue is MemberProjectedSlot)
                { 
                    return true;
                }
                foreach (WhenThen whenThen in m_clauses)
                { 
                    if (whenThen.Value is MemberProjectedSlot)
                    { 
                        return true; 
                    }
                } 
                return false;
            }
        }
 
        internal IEnumerable InstantiatedTypes
        { 
            get 
            {
                foreach (WhenThen whenThen in m_clauses) 
                {
                    EdmType type;
                    if (TryGetInstantiatedType(whenThen.Value, out type))
                    { 
                        yield return type;
                    } 
                } 
                EdmType elseType;
                if (TryGetInstantiatedType(m_elseValue, out elseType)) 
                {
                    yield return elseType;
                }
            } 
        }
        private bool TryGetInstantiatedType(ProjectedSlot slot, out EdmType type) 
        { 
            type = null;
            ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot; 
            if (constantSlot != null)
            {
                TypeConstant typeConstant = constantSlot.CellConstant as TypeConstant;
                if (typeConstant != null) 
                {
                    type = typeConstant.CdmType; 
                    return true; 
                }
            } 
            return false;
        }

 
        // effects: Simplifies the case statement so that unnecessary when
        // thens for nulls/undefined values are eliminated. Also, adds an 
        // else clause if possible. 
        internal void Simplify()
        { 
            // TODDO_[....]_LOW: Make this call idempotent?
            Debug.Assert(m_elseValue == null, "Already been simplified -- call is not idempotent yet");
            List clauses = new List();
            // remove all WHEN clauses where the value gets set to "undefined" 
            // We eliminate the last clause for now - we could determine the
            // "most complicated" WHEN clause and eliminate it 
            bool eliminatedNullClauses = false; 
            foreach (WhenThen clause in m_clauses)
            { 
                ConstantProjectedSlot constantSlot = clause.Value as ConstantProjectedSlot;
                // If null or undefined, remove it
                if (constantSlot != null && (constantSlot.CellConstant.IsNull() || constantSlot.CellConstant.IsUndefined()))
                { 
                    eliminatedNullClauses = true;
                } 
                else 
                {
                    clauses.Add(clause); 
                    if (clause.Condition.IsTrue)
                    {
                        // none of subsequent case statements will be evaluated - ignore them
                        break; 
                    }
                } 
            } 

            if (eliminatedNullClauses && clauses.Count == 0) 
            {
                // There is nothing left -- we should add a null as the value
                m_elseValue = new ConstantProjectedSlot(Constant.Null);
            } 

            // If we eliminated some undefined or null clauses, we do not want an else clause 
            if (clauses.Count > 0 && false == eliminatedNullClauses) 
            {
                // turn the last WHEN clause into an ELSE 
                int lastIndex = clauses.Count - 1;
                m_elseValue = clauses[lastIndex].Value;
                clauses.RemoveAt(lastIndex);
            } 
            m_clauses = clauses;
        } 
 

        // effects: Given a caseStatement, generates the CQL for 
        // it. blockAlias is used whenever the value in the WHEN or THEN is not an aliased slot
        internal StringBuilder AsCql(StringBuilder builder, IEnumerable withStatements, string blockAlias, int indentLevel)
        {
            // If the variable is a relation end, we will gets it scope Extent, e.g., CPerson1 for an 
            // the CPerson end of CPersonAddress1
            StringUtil.IndentNewLine(builder, indentLevel + 1); 
 
            if (Clauses.Count == 0)
            { 
                // this is just a single ELSE: no condition at all
                CaseSlotValueAsCql(builder, ElseValue, MemberPath, blockAlias);
                AppendWithBlock(builder, withStatements, blockAlias, indentLevel, ElseValue);
                return builder; 
            }
 
            // Generate the Case WHEN .. THEN ..., WHEN ... THEN ..., END 
            builder.Append("CASE");
            foreach (CaseStatement.WhenThen clause in Clauses) 
            {
                StringUtil.IndentNewLine(builder, indentLevel + 2);
                builder.Append("WHEN ");
                clause.Condition.AsCql(builder, blockAlias); 
                builder.Append(" THEN ");
                CaseSlotValueAsCql(builder, clause.Value, MemberPath, blockAlias); 
                AppendWithBlock(builder, withStatements, blockAlias, indentLevel + 2, clause.Value); 
            }
 
            if (ElseValue != null)
            {
                StringUtil.IndentNewLine(builder, indentLevel + 2);
                builder.Append("ELSE "); 
                CaseSlotValueAsCql(builder, ElseValue, MemberPath, blockAlias);
                AppendWithBlock(builder, withStatements, blockAlias, indentLevel + 2, ElseValue); 
            } 
            StringUtil.IndentNewLine(builder, indentLevel + 1);
            builder.Append("END"); 
            return builder;
        }

        private void AppendWithBlock(StringBuilder builder, IEnumerable withStatements, string blockAlias, int indentLevel, ProjectedSlot projectedSlot) 
        {
            if ((withStatements != null) && (withStatements.Count() > 0)) 
            { 

                ConstantProjectedSlot typeConstantSlot = projectedSlot as ConstantProjectedSlot; 
                if (typeConstantSlot != null)
                {
                    TypeConstant typeConstant = typeConstantSlot.CellConstant as TypeConstant;
                    if (typeConstant != null) 
                    {
                        AppendWithBlock(builder, withStatements, typeConstant.CdmType, blockAlias, indentLevel); 
                    } 
                    else
                    { 
                        Debug.Assert(false, "with block should be there for type constant only");
                    }
                }
                else 
                {
                    Debug.Assert(false, "with block should be there for type constant slot only"); 
                } 
            }
        } 

        private void AppendWithBlock(StringBuilder builder, IEnumerable withStatements, EdmType fromType,
            string blockAlias, int indentLevel)
        { 
            bool appendWithKeyword = true;
            foreach (WithStatement withStatement in withStatements) 
            { 
                //Add With statement for the types that participate in the association
                if (withStatement.EntityTypeForFromEnd.IsAssignableFrom(fromType)) 
                {
                    if (appendWithKeyword)
                    {
                        appendWithKeyword = false; 
                        builder.Append(" WITH ");
                    } 
 
                    withStatement.AsCql(builder, blockAlias, indentLevel);
                } 
            }

        }
 
        // requires: This method is called by CaseSlotValuetoCql and
        // slot is not a boolean slot 
        // effects: Given a slot and its corresponding outputMember, modifies 
        // builder to contain the slot value
        private static StringBuilder CaseSlotValueAsCql(StringBuilder builder, ProjectedSlot slot, 
                                               MemberPath outputMember, string blockAlias)
        {
            // CHANGE_[....]_DESIGN: We will never have THEN as a boolean slot
            Debug.Assert(slot is MemberProjectedSlot || slot is AliasedSlot || slot is ConstantProjectedSlot, 
                         "Case statement THEN can only have constants or members");
            slot.AsCql(builder, outputMember, blockAlias, 1); 
            return builder; 
        }
 
        // Repeat of AsCql but don't have all the info
        internal override void ToCompactString(StringBuilder builder)
        {
            // We have to duplicate the functionality of AsCql here since 
            // ToCompactString can get called at times when the THEN parts contain
            // Undefined values -- in that case, a Debug.Assert in CellConstant's AsCql 
            // fires off 

            builder.AppendLine("CASE"); 
            foreach (WhenThen clause in m_clauses)
            {
                builder.Append("    WHEN ");
                clause.Condition.ToCompactString(builder); 
                builder.Append(" THEN ");
                clause.Value.ToCompactString(builder); 
                builder.AppendLine(); 
            }
            if (m_elseValue != null) 
            {
                builder.Append("    ELSE ");
                m_elseValue.ToCompactString(builder);
                builder.AppendLine(); 
            }
            builder.Append("END AS "); 
            m_memberPath.ToCompactString(builder); 
        }
        #endregion 

        // effects: A class that stores WHEN  THEN 
        internal class WhenThen : InternalBase
        { 

            #region Constructor 
            // effects: Creates WHEN  THEN  
            internal WhenThen(BoolExpression condition, ProjectedSlot value)
            { 
                m_condition = condition;
                m_value = value;
            }
            #endregion 

            #region Fields 
            private BoolExpression m_condition; 
            private ProjectedSlot m_value;
            #endregion 

            #region Properties
            /// 
            /// effects: Returns the when condition 
            /// 
            internal BoolExpression Condition 
            { 
                get { return m_condition; }
            } 

            /// 
            /// effects: Returns the value for the when case
            ///  
            internal ProjectedSlot Value
            { 
                get { return m_value; } 
            }
            #endregion 

            #region String Methods
            internal WhenThen ReplaceWithAliasedSlot(CqlBlock block, MemberPath outputPath, int slotNum)
            { 
                // Change the THEN part
                ProjectedSlot newValue = m_value.MakeAliasedSlot(block, outputPath, slotNum); 
                return new WhenThen(m_condition, newValue); 
            }
 
            internal override void ToCompactString(StringBuilder builder)
            {
                builder.Append("WHEN ");
                m_condition.ToCompactString(builder); 
                builder.Append("THEN ");
                m_value.ToCompactString(builder); 
            } 
            #endregion
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK