public async Task<ISqlSchemaMappingCompilerOutput> CompileAsync(
            ISqlSchemaMappingCompilerInput input,
            ICompilerMessageBuilder messageProvider = null)
        {
            if(ReferenceEquals(input, null))
                throw new ArgumentNullException(nameof(input));

            messageProvider = messageProvider ?? new CompilerMessageBuilder();

            try
            {
                this.m_output = new SqlSchemaMappingCompilerOutput();
                var args = new XArgs(this.m_output, new Policies(), new CompilerMessageBuilder());
                
                                
                this.m_input = new SqlSchemaMappingCompilerInputAdapter(input, messageProvider, this.m_output);
                if (this.m_output.Errors.Any()) return this.m_output;

                this.m_output.DataRowInterfaces.AddRange(m_input.DataRowInterfaces);
                this.m_output.OperationRequestContracts.AddRange(m_input.OperationResponseInterfaces);
                this.m_output.OperationResponseContracts.AddRange(m_input.OperationResponseInterfaces);
                
                await this.CompileTableTypesAsync(args);

                return this.m_output;
            }
            finally
            {
                this.m_input = null;
                this.m_output = null;
                this.m_interfaceNames.Clear();
            }            
        }
        public SqlSchemaMappingCompilerInputAdapter(
            ISqlSchemaMappingCompilerInput other, 
            ICompilerMessageBuilder compilerMessageBuilder,
            IListener listener)
        {
            if(ReferenceEquals(other, null))
                throw new ArgumentNullException(nameof(other));
            if (ReferenceEquals(compilerMessageBuilder, null))
                throw new ArgumentNullException(nameof(compilerMessageBuilder));

            var interfaceNames = new HashSet<string>();
            var databaseObjectNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            var policies = new Policies();

            {// Data row contracts
                var contracts = other.DataRowInterfaces.ToDenseCollection();
                for (int interfaceIndex = 0; interfaceIndex < contracts.Count; ++interfaceIndex)
                {
                    var contract = contracts[interfaceIndex];
                    var item = new PropertySet(contract, listener, policies, compilerMessageBuilder, interfaceIndex);
                    this.DataRowInterfaces.Add(item);
                    if (!interfaceNames.Add(item.InterfaceName))
                        listener.Error(compilerMessageBuilder.DuplicateInterfaceNameInDataRowInterfaces(item.InterfaceName));
                    this.m_ixInterfaceByInterfaceName[item.InterfaceName] = item;
                    this.m_ixDataRowInterfaceByInterfaceName[item.InterfaceName] = item;
                }
            }

            {// Operation request contracts
                var contracts = other.OperationRequestInterfaces.ToDenseCollection();
                for (int interfaceIndex = 0; interfaceIndex < contracts.Count; ++interfaceIndex)
                {
                    var contract = contracts[interfaceIndex];
                    var item = new PropertySet(contract, listener, policies, compilerMessageBuilder, interfaceIndex);
                    this.OperationRequestInterfaces.Add(item);
                    if (!interfaceNames.Add(item.InterfaceName))
                        listener.Error(compilerMessageBuilder.DuplicateInterfaceNameInOperationRequestInterfaces(item.InterfaceName));
                    this.m_ixInterfaceByInterfaceName[item.InterfaceName] = item;
                    this.m_ixOperationRequestInterfaceByInterfaceName[item.InterfaceName] = item;
                    foreach (var tableValueProperty in item.Properties.OfType<TableValueProperty>())
                    {
                        if (!this.m_ixDataRowInterfaceByInterfaceName.ContainsKey(tableValueProperty.DataRowInterfaceName))
                        {
                            listener.Error(
                                compilerMessageBuilder.TableValueInterfaceNotDefined(
                                    item.InterfaceName,
                                    tableValueProperty.DataRowInterfaceName,
                                    tableValueProperty.PropertyName));
                        }
                    }
                }
            }

            {// Operation response contracts
                var contracts = other.OperationResponseInterfaces.ToDenseCollection();
                for (int interfaceIndex = 0; interfaceIndex < contracts.Count; ++interfaceIndex)
                {
                    var contract = contracts[interfaceIndex];
                    var item = new PropertySet(contract, listener, policies, compilerMessageBuilder, interfaceIndex);
                    this.OperationResponseInterfaces.Add(item);
                    if (!interfaceNames.Add(item.InterfaceName))
                        listener.Error(compilerMessageBuilder.DuplicateInterfaceNameInOperationResponseInterfaces(item.InterfaceName));
                    this.m_ixInterfaceByInterfaceName[item.InterfaceName] = item;
                    this.m_ixOperationResponseInterfaceByInterfaceName[item.InterfaceName] = item;
                }
            }

            foreach (var userSelector in other.TableTypeSelectors)
            {
                var selector =new SqlTableTypeSelector(userSelector, listener, policies, compilerMessageBuilder);
                TableTypeSelectors.Add(selector);
                if (false == databaseObjectNames.Add(selector.FullName))
                {
                    listener.Error(compilerMessageBuilder.DuplicateDatabaseObjectSelector(selector.FullName));
                    continue;
                }

                foreach (var dataRowMapping in selector.DataRowMappings)
                {
                    if (!this.m_ixDataRowInterfaceByInterfaceName.ContainsKey(dataRowMapping.InterfaceName))
                    {
                        listener.Error(compilerMessageBuilder.TableValueInterfaceReferedByTableTypeMappingNotDefined(selector.FullName, dataRowMapping.InterfaceName));
                        continue;
                    }

                    var contract = this.m_ixDataRowInterfaceByInterfaceName[dataRowMapping.InterfaceName];
                    dataRowMapping.Interface = contract;

                    var propertyNames = new HashSet<string>(contract.Properties.Select(m=> m.PropertyName), StringComparer.OrdinalIgnoreCase);

                    foreach (var mapping in dataRowMapping.Mappings)
                    {
                        if (false == propertyNames.Contains(mapping.PropertyName))
                        {
                            listener.Error(compilerMessageBuilder.InterfacePropertyNotFound(mapping.PropertyName));
                        }
                    }
                }
            }
        }