Function.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / EntityModel / SchemaObjectModel / Function.cs / 1 / Function.cs

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

using System; 
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics; 
using System.Globalization;
using System.Xml; 
using System.Xml.Schema; 
using System.Data;
using System.IO; 
using System.Data.Metadata.Edm;
using System.Data.Entity;

namespace System.Data.EntityModel.SchemaObjectModel 
{
    ///  
    /// class representing the Schema element in the schema 
    /// 
    internal class Function : SchemaType 
    {
        #region Instance Fields
        // if adding properties also add to InitializeObject()!
        private bool _isAggregate = false; 
        private bool _isBuiltIn = false;
        private bool _isNiladicFunction = false; 
        private bool _isComposable = true; 
        private string _unresolvedEntitySet = null;
        private FunctionCommandText _commandText = null; 
        private string _storeFunctionName = null;
        private SchemaType _type = null;
        private string _unresolvedType = null;
        // both are not specified 
        private SchemaElementLookUpTable _parameters = null;
        private ReturnType _returnType = null; 
        private CollectionKind _collectionKind = CollectionKind.None; 
        private ParameterTypeSemantics _parameterTypeSemantics;
        private EntityContainer _container = null; // container is known only for function imports 
        private EntityContainerEntitySet _entitySet = null;
        private string _schema;

        private string _functionStrongName; 
        #endregion
 
        #region Static Fields 

        private static System.Text.RegularExpressions.Regex s_typeParser = new System.Text.RegularExpressions.Regex(@"^(?((Collection)|(Ref)))\s*\(\s*(?\S*)\s*\)$", System.Text.RegularExpressions.RegexOptions.Compiled); 

        /// 
        ///
        ///  
        /// 
        ///  
        internal static TypeModifier RemoveTypeModifier(ref string type) 
        {
            System.Text.RegularExpressions.Match match = s_typeParser.Match(type); 
            if (match.Success)
            {
                type = match.Groups["typeName"].Value;
                switch (match.Groups["modifier"].Value) 
                {
                    case "Collection": 
                        return TypeModifier.Array; 
                    default:
                        Debug.Assert(false, "Unexpected modifier: " + match.Groups["modifier"].Value); 
                        break;
                }
            }
            return TypeModifier.None; 
        }
        #endregion 
 
        #region Public Methods
        ///  
        /// ctor for a schema function
        /// 
        public Function(Schema parentElement)
            : 
            base(parentElement)
        { 
        } 

        ///  
        /// ctor for a function import, which is scoped to the entity container
        /// 
        /// Parent container; must not be null.
        protected Function(EntityContainer container) 
            :
            base(container.Schema) 
        { 
            Debug.Assert(null != container);
            _container = container; 

            // override defaults for function imports
            _isComposable = false;
        } 
        #endregion
 
        #region Public Properties 

        public bool IsAggregate 
        {
            get
            {
                return _isAggregate; 
            }
            internal set 
            { 
                _isAggregate = value;
            } 
        }

        public bool IsBuiltIn
        { 
            get
            { 
                return _isBuiltIn; 
            }
            internal set 
            {
                _isBuiltIn = value;
            }
        } 

        public bool IsNiladicFunction 
        { 
            get
            { 
                return _isNiladicFunction;
            }
            internal set
            { 
                _isNiladicFunction = value;
            } 
        } 

        public bool IsComposable 
        {
            get
            {
                return _isComposable; 
            }
            internal set 
            { 
                _isComposable = value;
            } 
        }

        public string CommandText
        { 
            get
            { 
                if (_commandText != null) 
                {
                    return _commandText.CommandText; 
                }
                return null;
            }
        } 

        public ParameterTypeSemantics ParameterTypeSemantics 
        { 
            get
            { 
                return _parameterTypeSemantics;
            }
            internal set
            { 
                _parameterTypeSemantics = value;
            } 
        } 

        public string StoreFunctionName 
        {
            get
            {
                return _storeFunctionName; 
            }
            internal set 
            { 
                Debug.Assert(value != null, "StoreFunctionName should never be set null value");
                _storeFunctionName = value; 
            }
        }

        public SchemaType Type 
        {
            get 
            { 
                if (null != this.ReturnType)
                { 
                    return this.ReturnType.Type;
                }
                else return this._type;
            } 
        }
 
        public ReturnType ReturnType 
        {
            get 
            {
                return this._returnType;
            }
        } 

        public SchemaElementLookUpTable Parameters 
        { 
            get
            { 
                if (_parameters == null)
                {
                    _parameters = new SchemaElementLookUpTable();
                } 
                return _parameters;
            } 
        } 

        public CollectionKind CollectionKind 
        {
            get
            {
                return _collectionKind; 
            }
            internal set 
            { 
                _collectionKind = value;
            } 
        }

        public override string Identity
        { 
            get
            { 
                if (String.IsNullOrEmpty(_functionStrongName)) 
                {
                    string name = this.FQName; 
                    System.Text.StringBuilder stringBuilder = new Text.StringBuilder(name);
                    bool first = true;
                    stringBuilder.Append('(');
                    foreach (Parameter parameter in this.Parameters) 
                    {
                        if (!first) 
                        { 
                            stringBuilder.Append(',');
                        } 
                        else
                        {
                            first = false;
                        } 
                        stringBuilder.Append(Helper.ToString(parameter.ParameterDirection));
                        stringBuilder.Append(' '); 
                        // we don't include the facets in the identity, since we are *not* 
                        // taking them into consideration inside the
                        // RankFunctionParameters method of TypeResolver.cs 
                        stringBuilder.Append(parameter.Type.FQName);
                    }
                    stringBuilder.Append(')');
                    _functionStrongName = stringBuilder.ToString(); 
                }
                return _functionStrongName; 
            } 
        }
 
        public virtual bool IsFunctionImport { get { return false; } }

        public EntityContainerEntitySet EntitySet { get { return _entitySet; } }
 
        public string DbSchema
        { 
            get 
            {
                return _schema; 
            }
        }

        #endregion 

        #region Protected Properties 
        protected override bool HandleElement(XmlReader reader) 
        {
            if (base.HandleElement(reader)) 
            {
                return true;
            }
            else if (CanHandleElement(reader, XmlConstants.CommandText)) 
            {
                HandleCommandTextFunctionElment(reader); 
                return true; 
            }
            else if (CanHandleElement(reader, XmlConstants.Parameter)) 
            {
                HandleParameterElement(reader);
                return true;
            } 
            else if (CanHandleElement(reader, XmlConstants.ReturnTypeElement))
            { 
                if (ParentElement.Schema.DataModel == SchemaDataModelOption.ProviderManifestModel) 
                {
                    HandleReturnTypeElement(reader); 
                }
                else
                {
                    SkipThroughElement(reader); 
                }
                return true; 
            } 
            return false;
        } 

        protected override bool HandleAttribute(XmlReader reader)
        {
            if (base.HandleAttribute(reader)) 
            {
                return true; 
            } 
            else if (CanHandleAttribute(reader, XmlConstants.ReturnType))
            { 
                HandleReturnTypeAttribute(reader);
                return true;
            }
            else if (CanHandleAttribute(reader, XmlConstants.AggregateAttribute)) 
            {
                HandleAggregateAttribute(reader); 
                return true; 
            }
            else if (CanHandleAttribute(reader, XmlConstants.BuiltInAttribute)) 
            {
                HandleBuiltInAttribute(reader);
                return true;
            } 
            else if (CanHandleAttribute(reader, XmlConstants.StoreFunctionName))
            { 
                HandleStoreFunctionNameAttribute(reader); 
                return true;
            } 
            else if (CanHandleAttribute(reader, XmlConstants.NiladicFunction))
            {
                HandleNiladicFunctionAttribute(reader);
                return true; 
            }
            else if (CanHandleAttribute(reader, XmlConstants.IsComposable)) 
            { 
                HandleIsComposableFunctionAttribute(reader);
                return true; 
            }
            else if (CanHandleAttribute(reader, XmlConstants.ParameterTypeSemantics))
            {
                HandleParameterTypeSemanticsAttribute(reader); 
                return true;
            } 
            else if (CanHandleAttribute(reader, XmlConstants.Schema)) 
            {
                HandleDbSchemaAttribute(reader); 
                return true;
            }
            else if (CanHandleAttribute(reader, XmlConstants.EntitySet))
            { 
                HandleEntitySetAttribute(reader);
                return true; 
            } 

 
            return false;
        }
        #endregion
 
        #region Internal Methods
 
        internal override void ResolveTopLevelNames() 
        {
            base.ResolveTopLevelNames(); 

            if (ReturnType != null)
            {
                Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderManifestModel, 
                                "Only ProviderManifest will allow ReturnType elements");
                ReturnType.ResolveTopLevelNames(); 
            } 

            Debug.Assert(this._type == null, "This must be resolved exactly once"); 

            if (null != UnresolvedReturnType)
            {
                Debug.Assert(Schema.DataModel != SchemaDataModelOption.ProviderManifestModel, 
                                "ProviderManifest only has ReturnType as an element");
                if (Schema.ResolveTypeName(this, UnresolvedReturnType, out _type)) 
                { 
                    if (this.IsFunctionImport)
                    { 
                        // must be either undefined, a collection of scalars, or a collection of entities
                        if (!MeetsFunctionImportReturnTypeRequirements(_type))
                        {
                            AddError(ErrorCode.FunctionImportUnsupportedReturnType, 
                                EdmSchemaErrorSeverity.Error,
                                this, 
                                Schema.EdmVersion == XmlConstants.EdmVersionForV1 ? System.Data.Entity.Strings.FunctionImportWithUnsupportedReturnTypeV1(this.Name) : 
                                                                                    System.Data.Entity.Strings.FunctionImportWithUnsupportedReturnTypeV1_1(this.Name)
                                ); 
                        }
                    }
                    else
                    { 
                        if (!(_type is ScalarType))
                        { 
                            if (Schema.DataModel != SchemaDataModelOption.ProviderManifestModel) 
                            {
                                AddError(ErrorCode.FunctionWithNonScalarTypeNotSupported, 
                                         EdmSchemaErrorSeverity.Error,
                                         this,
                                         System.Data.Entity.Strings.FunctionWithNonScalarTypeNotSupported(_type.FQName, this.FQName));
                            } 
                            else
                            { 
                                AddError(ErrorCode.FunctionWithNonScalarTypeNotSupported, 
                                         EdmSchemaErrorSeverity.Error,
                                         this, 
                                         System.Data.Entity.Strings.FunctionWithNonEdmTypeNotSupported(_type.FQName, this.FQName));
                            }
                        }
                    } 
                }
            } 
 
            if (IsFunctionImport)
            { 
                Debug.Assert(null != _container, "function imports must know container");
                // resolve entity set
                if (null == _entitySet && null != _unresolvedEntitySet)
                { 
                    _entitySet = _container.FindEntitySet(_unresolvedEntitySet);
 
                    if (null == _entitySet) 
                    {
                        AddError(ErrorCode.FunctionImportUnknownEntitySet, 
                            EdmSchemaErrorSeverity.Error,
                            System.Data.Entity.Strings.FunctionImportUnknownEntitySet(_unresolvedEntitySet, this.Name));
                    }
                } 
            }
 
            foreach (Parameter parameter in this.Parameters) 
            {
                parameter.ResolveTopLevelNames(); 
            }
        }

        private bool MeetsFunctionImportReturnTypeRequirements(SchemaType type) 
        {
            if (_type is ScalarType && _collectionKind == CollectionKind.Bag) return true; 
            if (_type is SchemaEntityType && _collectionKind == CollectionKind.Bag) return true; 

            if (Schema.EdmVersion == XmlConstants.EdmVersionForV1_1) 
            {
                if (_type is ScalarType && _collectionKind == CollectionKind.None) return true;
                if (_type is SchemaEntityType && _collectionKind == CollectionKind.None) return true;
                if (_type is SchemaComplexType && _collectionKind == CollectionKind.None) return true; 
                if (_type is SchemaComplexType && _collectionKind == CollectionKind.Bag) return true;
            } 
 
            return false;
        } 


        /// 
        /// Perform local validation on function definition. 
        /// 
        internal override void Validate() 
        { 
            base.Validate();
 
            if ( (null != _returnType && null != this._type))
            {
                AddError(ErrorCode.AmbiguousFunctionReturnType, EdmSchemaErrorSeverity.Error,
                    System.Data.Entity.Strings.AmbiguousFunctionReturnType(this.Name, XmlConstants.ReturnType)); 
            }
 
            if (_commandText != null) 
            {
                _commandText.Validate(); 
            }

            if (null == this.Type)
            { 
                // only non-composable types can omit return type
                if (this.IsComposable) 
                { 
                    AddError(ErrorCode.ComposableFunctionWithoutReturnType, EdmSchemaErrorSeverity.Error,
                        Strings.ComposableFunctionMustDeclareReturnType); 
                }
            }
            else
            { 
                // non-composable functions may not declare a return type (except for function imports)
                if (!this.IsComposable && !this.IsFunctionImport) 
                { 
                    AddError(ErrorCode.NonComposableFunctionWithReturnType, EdmSchemaErrorSeverity.Error,
                        Strings.NonComposableFunctionMustNotDeclareReturnType); 
                }
            }

            if (IsAggregate) 
            {
 
                // Make sure that the function has exactly one parameter and that takes 
                // a collection type
                if (Parameters.Count != 1) 
                {
                    AddError(ErrorCode.InvalidNumberOfParametersForAggregateFunction,
                             EdmSchemaErrorSeverity.Error,
                             this, 
                             System.Data.Entity.Strings.InvalidNumberOfParametersForAggregateFunction(FQName));
                } 
                else if (Parameters.GetElementAt(0).CollectionKind == CollectionKind.None) 
                {
                    // Since we have already checked that there should be exactly one parameter, it should be safe to get the 
                    // first parameter for the function
                    Parameter param = Parameters.GetElementAt(0);

                    AddError(ErrorCode.InvalidParameterTypeForAggregateFunction, 
                             EdmSchemaErrorSeverity.Error,
                             this, 
                             System.Data.Entity.Strings.InvalidParameterTypeForAggregateFunction(param.Name, FQName)); 
                }
 
            }


            if (!this.IsComposable) 
            {
                // non-composable functions do not permit aggregate, build-in, or niladic attribute 
                if (this.IsAggregate || 
                    this.IsNiladicFunction ||
                    this.IsBuiltIn) 
                {
                    AddError(ErrorCode.NonComposableFunctionAttributesNotValid, EdmSchemaErrorSeverity.Error,
                        Strings.NonComposableFunctionHasDisallowedAttribute);
                } 
            }
 
            if (null != this.CommandText) 
            {
                // functions with command text are not composable 
                if (this.IsComposable)
                {
                    AddError(ErrorCode.ComposableFunctionWithCommandText, EdmSchemaErrorSeverity.Error,
                        Strings.CommandTextFunctionsNotComposable); 
                }
 
                // functions with command text cannot declare store function name 
                if (null != this.StoreFunctionName)
                { 
                    AddError(ErrorCode.FunctionDeclaresCommandTextAndStoreFunctionName, EdmSchemaErrorSeverity.Error,
                        Strings.CommandTextFunctionsCannotDeclareStoreFunctionName);
                }
            } 

            if (this.IsFunctionImport) 
            { 
                // if entity type, verify specification of entity set and that the type is appropriate
                // for the entity set 
                SchemaEntityType entityType = this.Type as SchemaEntityType;
                if (null != entityType)
                {
                    if (null == this.EntitySet) 
                    {
                        AddError(ErrorCode.FunctionImportReturnsEntitiesButDoesNotSpecifyEntitySet, 
                            EdmSchemaErrorSeverity.Error, 
                            System.Data.Entity.Strings.FunctionImportReturnEntitiesButDoesNotSpecifyEntitySet(this.Name));
                    } 
                    else if (null != this.EntitySet.EntityType &&
                        !entityType.IsOfType(this.EntitySet.EntityType))
                    {
                        AddError(ErrorCode.FunctionImportEntityTypeDoesNotMatchEntitySet, 
                            EdmSchemaErrorSeverity.Error,
                            System.Data.Entity.Strings.FunctionImportEntityTypeDoesNotMatchEntitySet( 
                            this.FQName, this.EntitySet.EntityType.FQName, this.EntitySet.Name, this.ParentElement.FQName)); 
                    }
                } 

                if (null == entityType && null != EntitySet)
                {
                    AddError(ErrorCode.FunctionImportSpecifiesEntitySetButDoesNotReturnEntityType, 
                        EdmSchemaErrorSeverity.Error,
                        System.Data.Entity.Strings.FunctionImportSpecifiesEntitySetButNotEntityType(this.Name, this.ParentElement.FQName)); 
                } 
            }
        } 

        internal override SchemaElement Clone(SchemaElement parentElement)
        {
            Debug.Assert(IsFunctionImport, "we only support clone for FunctionImports"); 
            Function function = new FunctionImportElement((EntityContainer)parentElement);
            function._isAggregate = _isAggregate; 
            function._isBuiltIn = _isBuiltIn; 
            function._isNiladicFunction = _isNiladicFunction;
            function._isComposable = _isComposable; 
            function._entitySet = _entitySet;
            function._commandText = _commandText;
            function._storeFunctionName = _storeFunctionName;
            function._type = _type; 
            function._returnType = _returnType;
            function._collectionKind = _collectionKind; 
            function._parameterTypeSemantics = _parameterTypeSemantics; 
            function._schema = _schema;
            function.Name = this.Name; 

            // Clone all the parameters
            foreach (Parameter parameter in this.Parameters)
            { 
                AddErrorKind error = function.Parameters.TryAdd((Parameter)parameter.Clone(function));
                Debug.Assert(error == AddErrorKind.Succeeded, "Since we are cloning a validated function, this should never fail"); 
            } 

            return function; 
        }
        #endregion

        #region Internal Properties 
        /// 
        /// 
        ///  
        /// 
        internal string UnresolvedReturnType 
        {
            get
            {
                return _unresolvedType; 
            }
            set 
            { 
                _unresolvedType = value;
            } 
        }
        #endregion //Internal Properties

        #region Private Methods 

        ///  
        /// The method that is called when a DbSchema attribute is encountered. 
        /// 
        /// An XmlReader positioned at the Type attribute. 
        private void HandleDbSchemaAttribute(XmlReader reader)
        {
            Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderDataModel, "We shouldn't see this attribute unless we are parsing ssdl");
            Debug.Assert(reader != null); 

            _schema = reader.Value; 
        } 

        ///  
        /// Handler for the Version attribute
        /// 
        /// xml reader currently positioned at Version attribute
        private void HandleAggregateAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
            bool isAggregate = false; 
            HandleBoolAttribute(reader, ref isAggregate);
            IsAggregate = isAggregate; 
        }

        /// 
        /// Handler for the Namespace attribute 
        /// 
        /// xml reader currently positioned at Namespace attribute 
        private void HandleBuiltInAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
            bool isBuiltIn = false;
            HandleBoolAttribute(reader, ref isBuiltIn);
            IsBuiltIn = isBuiltIn;
        } 

        ///  
        /// Handler for the Alias attribute 
        /// 
        /// xml reader currently positioned at Alias attribute 
        private void HandleStoreFunctionNameAttribute(XmlReader reader)
        {
            Debug.Assert(reader != null);
            string value = reader.Value.ToString(); 
            if (!String.IsNullOrEmpty(value))
            { 
                value = value.Trim(); 
                StoreFunctionName = value;
            } 
        }

        /// 
        /// Handler for the NiladicFunction attribute 
        /// 
        /// xml reader currently positioned at Namespace attribute 
        private void HandleNiladicFunctionAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
            bool isNiladicFunction = false;
            HandleBoolAttribute(reader, ref isNiladicFunction);
            IsNiladicFunction = isNiladicFunction;
        } 

        ///  
        /// Handler for the IsComposable attribute 
        /// 
        /// xml reader currently positioned at Namespace attribute 
        private void HandleIsComposableFunctionAttribute(XmlReader reader)
        {
            Debug.Assert(reader != null);
            bool isComposable = true; 
            HandleBoolAttribute(reader, ref isComposable);
            IsComposable = isComposable; 
        } 

        private void HandleCommandTextFunctionElment(XmlReader reader) 
        {
            Debug.Assert(reader != null);

            FunctionCommandText commandText = new FunctionCommandText(this); 
            commandText.Parse(reader);
            _commandText = commandText; 
        } 

        private void HandleReturnTypeAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null);
            Debug.Assert(UnresolvedReturnType == null);
 
            string type;
            if (!Utils.GetString(Schema, reader, out type)) 
                return; 

            switch (RemoveTypeModifier(ref type)) 
            {
                case TypeModifier.Array:
                    CollectionKind = CollectionKind.Bag;
                    break; 
                case TypeModifier.None:
                    break; 
                default: 
                    Debug.Assert(false, "RemoveTypeModifier already checks for this");
                    break; 
            }

            if (!Utils.ValidateDottedName(Schema, reader, type))
                return; 

            UnresolvedReturnType = type; 
        } 

        ///  
        /// Handler for the Parameter Element
        /// 
        /// xml reader currently positioned at Parameter Element
        private void HandleParameterElement(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
 
            Parameter parameter = new Parameter(this);
 
            parameter.Parse(reader);

            Parameters.Add(parameter, true, Strings.ParameterNameAlreadyDefinedDuplicate);
        } 

        ///  
        /// Handler for the ReturnType element 
        /// 
        /// xml reader currently positioned at ReturnType element 
        private void HandleReturnTypeElement(XmlReader reader)
        {
            Debug.Assert(reader != null);
 
            ReturnType returnType = new ReturnType(this);
 
            returnType.Parse(reader); 

            this._returnType = returnType; 

        }

 
        /// 
        /// Handles ParameterTypeSemantics attribute 
        ///  
        /// 
        private void HandleParameterTypeSemanticsAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null);

            string value = reader.Value; 

            if (String.IsNullOrEmpty(value)) 
            { 
                return;
            } 

            value = value.Trim();

            if (!String.IsNullOrEmpty(value)) 
            {
                switch (value) 
                { 
                    case "ExactMatchOnly":
                        ParameterTypeSemantics = ParameterTypeSemantics.ExactMatchOnly; 
                        break;
                    case "AllowImplicitPromotion":
                        ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitPromotion;
                        break; 
                    case "AllowImplicitConversion":
                        ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion; 
                        break; 
                    default:
                        // don't try to use the name of the function, because we are still parsing the 
                        // attributes, and we may not be to the name attribute yet.
                        AddError(ErrorCode.InvalidValueForParameterTypeSemantics, EdmSchemaErrorSeverity.Error, reader,
                            System.Data.Entity.Strings.InvalidValueForParameterTypeSemanticsAttribute(
                                          value)); 

                        break; 
                } 
            }
        } 

        /// 
        /// Handler for the EntitySet attribute
        ///  
        /// xml reader currently positioned at EntitySet attribute
        private void HandleEntitySetAttribute(XmlReader reader) 
        { 
            Debug.Assert(reader != null);
            string entitySetName; 
            if (Utils.GetString(Schema, reader, out entitySetName))
            {
                _unresolvedEntitySet = entitySetName;
            } 
        }
 
        #endregion 
    }
} 

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

using System; 
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics; 
using System.Globalization;
using System.Xml; 
using System.Xml.Schema; 
using System.Data;
using System.IO; 
using System.Data.Metadata.Edm;
using System.Data.Entity;

namespace System.Data.EntityModel.SchemaObjectModel 
{
    ///  
    /// class representing the Schema element in the schema 
    /// 
    internal class Function : SchemaType 
    {
        #region Instance Fields
        // if adding properties also add to InitializeObject()!
        private bool _isAggregate = false; 
        private bool _isBuiltIn = false;
        private bool _isNiladicFunction = false; 
        private bool _isComposable = true; 
        private string _unresolvedEntitySet = null;
        private FunctionCommandText _commandText = null; 
        private string _storeFunctionName = null;
        private SchemaType _type = null;
        private string _unresolvedType = null;
        // both are not specified 
        private SchemaElementLookUpTable _parameters = null;
        private ReturnType _returnType = null; 
        private CollectionKind _collectionKind = CollectionKind.None; 
        private ParameterTypeSemantics _parameterTypeSemantics;
        private EntityContainer _container = null; // container is known only for function imports 
        private EntityContainerEntitySet _entitySet = null;
        private string _schema;

        private string _functionStrongName; 
        #endregion
 
        #region Static Fields 

        private static System.Text.RegularExpressions.Regex s_typeParser = new System.Text.RegularExpressions.Regex(@"^(?((Collection)|(Ref)))\s*\(\s*(?\S*)\s*\)$", System.Text.RegularExpressions.RegexOptions.Compiled); 

        /// 
        ///
        ///  
        /// 
        ///  
        internal static TypeModifier RemoveTypeModifier(ref string type) 
        {
            System.Text.RegularExpressions.Match match = s_typeParser.Match(type); 
            if (match.Success)
            {
                type = match.Groups["typeName"].Value;
                switch (match.Groups["modifier"].Value) 
                {
                    case "Collection": 
                        return TypeModifier.Array; 
                    default:
                        Debug.Assert(false, "Unexpected modifier: " + match.Groups["modifier"].Value); 
                        break;
                }
            }
            return TypeModifier.None; 
        }
        #endregion 
 
        #region Public Methods
        ///  
        /// ctor for a schema function
        /// 
        public Function(Schema parentElement)
            : 
            base(parentElement)
        { 
        } 

        ///  
        /// ctor for a function import, which is scoped to the entity container
        /// 
        /// Parent container; must not be null.
        protected Function(EntityContainer container) 
            :
            base(container.Schema) 
        { 
            Debug.Assert(null != container);
            _container = container; 

            // override defaults for function imports
            _isComposable = false;
        } 
        #endregion
 
        #region Public Properties 

        public bool IsAggregate 
        {
            get
            {
                return _isAggregate; 
            }
            internal set 
            { 
                _isAggregate = value;
            } 
        }

        public bool IsBuiltIn
        { 
            get
            { 
                return _isBuiltIn; 
            }
            internal set 
            {
                _isBuiltIn = value;
            }
        } 

        public bool IsNiladicFunction 
        { 
            get
            { 
                return _isNiladicFunction;
            }
            internal set
            { 
                _isNiladicFunction = value;
            } 
        } 

        public bool IsComposable 
        {
            get
            {
                return _isComposable; 
            }
            internal set 
            { 
                _isComposable = value;
            } 
        }

        public string CommandText
        { 
            get
            { 
                if (_commandText != null) 
                {
                    return _commandText.CommandText; 
                }
                return null;
            }
        } 

        public ParameterTypeSemantics ParameterTypeSemantics 
        { 
            get
            { 
                return _parameterTypeSemantics;
            }
            internal set
            { 
                _parameterTypeSemantics = value;
            } 
        } 

        public string StoreFunctionName 
        {
            get
            {
                return _storeFunctionName; 
            }
            internal set 
            { 
                Debug.Assert(value != null, "StoreFunctionName should never be set null value");
                _storeFunctionName = value; 
            }
        }

        public SchemaType Type 
        {
            get 
            { 
                if (null != this.ReturnType)
                { 
                    return this.ReturnType.Type;
                }
                else return this._type;
            } 
        }
 
        public ReturnType ReturnType 
        {
            get 
            {
                return this._returnType;
            }
        } 

        public SchemaElementLookUpTable Parameters 
        { 
            get
            { 
                if (_parameters == null)
                {
                    _parameters = new SchemaElementLookUpTable();
                } 
                return _parameters;
            } 
        } 

        public CollectionKind CollectionKind 
        {
            get
            {
                return _collectionKind; 
            }
            internal set 
            { 
                _collectionKind = value;
            } 
        }

        public override string Identity
        { 
            get
            { 
                if (String.IsNullOrEmpty(_functionStrongName)) 
                {
                    string name = this.FQName; 
                    System.Text.StringBuilder stringBuilder = new Text.StringBuilder(name);
                    bool first = true;
                    stringBuilder.Append('(');
                    foreach (Parameter parameter in this.Parameters) 
                    {
                        if (!first) 
                        { 
                            stringBuilder.Append(',');
                        } 
                        else
                        {
                            first = false;
                        } 
                        stringBuilder.Append(Helper.ToString(parameter.ParameterDirection));
                        stringBuilder.Append(' '); 
                        // we don't include the facets in the identity, since we are *not* 
                        // taking them into consideration inside the
                        // RankFunctionParameters method of TypeResolver.cs 
                        stringBuilder.Append(parameter.Type.FQName);
                    }
                    stringBuilder.Append(')');
                    _functionStrongName = stringBuilder.ToString(); 
                }
                return _functionStrongName; 
            } 
        }
 
        public virtual bool IsFunctionImport { get { return false; } }

        public EntityContainerEntitySet EntitySet { get { return _entitySet; } }
 
        public string DbSchema
        { 
            get 
            {
                return _schema; 
            }
        }

        #endregion 

        #region Protected Properties 
        protected override bool HandleElement(XmlReader reader) 
        {
            if (base.HandleElement(reader)) 
            {
                return true;
            }
            else if (CanHandleElement(reader, XmlConstants.CommandText)) 
            {
                HandleCommandTextFunctionElment(reader); 
                return true; 
            }
            else if (CanHandleElement(reader, XmlConstants.Parameter)) 
            {
                HandleParameterElement(reader);
                return true;
            } 
            else if (CanHandleElement(reader, XmlConstants.ReturnTypeElement))
            { 
                if (ParentElement.Schema.DataModel == SchemaDataModelOption.ProviderManifestModel) 
                {
                    HandleReturnTypeElement(reader); 
                }
                else
                {
                    SkipThroughElement(reader); 
                }
                return true; 
            } 
            return false;
        } 

        protected override bool HandleAttribute(XmlReader reader)
        {
            if (base.HandleAttribute(reader)) 
            {
                return true; 
            } 
            else if (CanHandleAttribute(reader, XmlConstants.ReturnType))
            { 
                HandleReturnTypeAttribute(reader);
                return true;
            }
            else if (CanHandleAttribute(reader, XmlConstants.AggregateAttribute)) 
            {
                HandleAggregateAttribute(reader); 
                return true; 
            }
            else if (CanHandleAttribute(reader, XmlConstants.BuiltInAttribute)) 
            {
                HandleBuiltInAttribute(reader);
                return true;
            } 
            else if (CanHandleAttribute(reader, XmlConstants.StoreFunctionName))
            { 
                HandleStoreFunctionNameAttribute(reader); 
                return true;
            } 
            else if (CanHandleAttribute(reader, XmlConstants.NiladicFunction))
            {
                HandleNiladicFunctionAttribute(reader);
                return true; 
            }
            else if (CanHandleAttribute(reader, XmlConstants.IsComposable)) 
            { 
                HandleIsComposableFunctionAttribute(reader);
                return true; 
            }
            else if (CanHandleAttribute(reader, XmlConstants.ParameterTypeSemantics))
            {
                HandleParameterTypeSemanticsAttribute(reader); 
                return true;
            } 
            else if (CanHandleAttribute(reader, XmlConstants.Schema)) 
            {
                HandleDbSchemaAttribute(reader); 
                return true;
            }
            else if (CanHandleAttribute(reader, XmlConstants.EntitySet))
            { 
                HandleEntitySetAttribute(reader);
                return true; 
            } 

 
            return false;
        }
        #endregion
 
        #region Internal Methods
 
        internal override void ResolveTopLevelNames() 
        {
            base.ResolveTopLevelNames(); 

            if (ReturnType != null)
            {
                Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderManifestModel, 
                                "Only ProviderManifest will allow ReturnType elements");
                ReturnType.ResolveTopLevelNames(); 
            } 

            Debug.Assert(this._type == null, "This must be resolved exactly once"); 

            if (null != UnresolvedReturnType)
            {
                Debug.Assert(Schema.DataModel != SchemaDataModelOption.ProviderManifestModel, 
                                "ProviderManifest only has ReturnType as an element");
                if (Schema.ResolveTypeName(this, UnresolvedReturnType, out _type)) 
                { 
                    if (this.IsFunctionImport)
                    { 
                        // must be either undefined, a collection of scalars, or a collection of entities
                        if (!MeetsFunctionImportReturnTypeRequirements(_type))
                        {
                            AddError(ErrorCode.FunctionImportUnsupportedReturnType, 
                                EdmSchemaErrorSeverity.Error,
                                this, 
                                Schema.EdmVersion == XmlConstants.EdmVersionForV1 ? System.Data.Entity.Strings.FunctionImportWithUnsupportedReturnTypeV1(this.Name) : 
                                                                                    System.Data.Entity.Strings.FunctionImportWithUnsupportedReturnTypeV1_1(this.Name)
                                ); 
                        }
                    }
                    else
                    { 
                        if (!(_type is ScalarType))
                        { 
                            if (Schema.DataModel != SchemaDataModelOption.ProviderManifestModel) 
                            {
                                AddError(ErrorCode.FunctionWithNonScalarTypeNotSupported, 
                                         EdmSchemaErrorSeverity.Error,
                                         this,
                                         System.Data.Entity.Strings.FunctionWithNonScalarTypeNotSupported(_type.FQName, this.FQName));
                            } 
                            else
                            { 
                                AddError(ErrorCode.FunctionWithNonScalarTypeNotSupported, 
                                         EdmSchemaErrorSeverity.Error,
                                         this, 
                                         System.Data.Entity.Strings.FunctionWithNonEdmTypeNotSupported(_type.FQName, this.FQName));
                            }
                        }
                    } 
                }
            } 
 
            if (IsFunctionImport)
            { 
                Debug.Assert(null != _container, "function imports must know container");
                // resolve entity set
                if (null == _entitySet && null != _unresolvedEntitySet)
                { 
                    _entitySet = _container.FindEntitySet(_unresolvedEntitySet);
 
                    if (null == _entitySet) 
                    {
                        AddError(ErrorCode.FunctionImportUnknownEntitySet, 
                            EdmSchemaErrorSeverity.Error,
                            System.Data.Entity.Strings.FunctionImportUnknownEntitySet(_unresolvedEntitySet, this.Name));
                    }
                } 
            }
 
            foreach (Parameter parameter in this.Parameters) 
            {
                parameter.ResolveTopLevelNames(); 
            }
        }

        private bool MeetsFunctionImportReturnTypeRequirements(SchemaType type) 
        {
            if (_type is ScalarType && _collectionKind == CollectionKind.Bag) return true; 
            if (_type is SchemaEntityType && _collectionKind == CollectionKind.Bag) return true; 

            if (Schema.EdmVersion == XmlConstants.EdmVersionForV1_1) 
            {
                if (_type is ScalarType && _collectionKind == CollectionKind.None) return true;
                if (_type is SchemaEntityType && _collectionKind == CollectionKind.None) return true;
                if (_type is SchemaComplexType && _collectionKind == CollectionKind.None) return true; 
                if (_type is SchemaComplexType && _collectionKind == CollectionKind.Bag) return true;
            } 
 
            return false;
        } 


        /// 
        /// Perform local validation on function definition. 
        /// 
        internal override void Validate() 
        { 
            base.Validate();
 
            if ( (null != _returnType && null != this._type))
            {
                AddError(ErrorCode.AmbiguousFunctionReturnType, EdmSchemaErrorSeverity.Error,
                    System.Data.Entity.Strings.AmbiguousFunctionReturnType(this.Name, XmlConstants.ReturnType)); 
            }
 
            if (_commandText != null) 
            {
                _commandText.Validate(); 
            }

            if (null == this.Type)
            { 
                // only non-composable types can omit return type
                if (this.IsComposable) 
                { 
                    AddError(ErrorCode.ComposableFunctionWithoutReturnType, EdmSchemaErrorSeverity.Error,
                        Strings.ComposableFunctionMustDeclareReturnType); 
                }
            }
            else
            { 
                // non-composable functions may not declare a return type (except for function imports)
                if (!this.IsComposable && !this.IsFunctionImport) 
                { 
                    AddError(ErrorCode.NonComposableFunctionWithReturnType, EdmSchemaErrorSeverity.Error,
                        Strings.NonComposableFunctionMustNotDeclareReturnType); 
                }
            }

            if (IsAggregate) 
            {
 
                // Make sure that the function has exactly one parameter and that takes 
                // a collection type
                if (Parameters.Count != 1) 
                {
                    AddError(ErrorCode.InvalidNumberOfParametersForAggregateFunction,
                             EdmSchemaErrorSeverity.Error,
                             this, 
                             System.Data.Entity.Strings.InvalidNumberOfParametersForAggregateFunction(FQName));
                } 
                else if (Parameters.GetElementAt(0).CollectionKind == CollectionKind.None) 
                {
                    // Since we have already checked that there should be exactly one parameter, it should be safe to get the 
                    // first parameter for the function
                    Parameter param = Parameters.GetElementAt(0);

                    AddError(ErrorCode.InvalidParameterTypeForAggregateFunction, 
                             EdmSchemaErrorSeverity.Error,
                             this, 
                             System.Data.Entity.Strings.InvalidParameterTypeForAggregateFunction(param.Name, FQName)); 
                }
 
            }


            if (!this.IsComposable) 
            {
                // non-composable functions do not permit aggregate, build-in, or niladic attribute 
                if (this.IsAggregate || 
                    this.IsNiladicFunction ||
                    this.IsBuiltIn) 
                {
                    AddError(ErrorCode.NonComposableFunctionAttributesNotValid, EdmSchemaErrorSeverity.Error,
                        Strings.NonComposableFunctionHasDisallowedAttribute);
                } 
            }
 
            if (null != this.CommandText) 
            {
                // functions with command text are not composable 
                if (this.IsComposable)
                {
                    AddError(ErrorCode.ComposableFunctionWithCommandText, EdmSchemaErrorSeverity.Error,
                        Strings.CommandTextFunctionsNotComposable); 
                }
 
                // functions with command text cannot declare store function name 
                if (null != this.StoreFunctionName)
                { 
                    AddError(ErrorCode.FunctionDeclaresCommandTextAndStoreFunctionName, EdmSchemaErrorSeverity.Error,
                        Strings.CommandTextFunctionsCannotDeclareStoreFunctionName);
                }
            } 

            if (this.IsFunctionImport) 
            { 
                // if entity type, verify specification of entity set and that the type is appropriate
                // for the entity set 
                SchemaEntityType entityType = this.Type as SchemaEntityType;
                if (null != entityType)
                {
                    if (null == this.EntitySet) 
                    {
                        AddError(ErrorCode.FunctionImportReturnsEntitiesButDoesNotSpecifyEntitySet, 
                            EdmSchemaErrorSeverity.Error, 
                            System.Data.Entity.Strings.FunctionImportReturnEntitiesButDoesNotSpecifyEntitySet(this.Name));
                    } 
                    else if (null != this.EntitySet.EntityType &&
                        !entityType.IsOfType(this.EntitySet.EntityType))
                    {
                        AddError(ErrorCode.FunctionImportEntityTypeDoesNotMatchEntitySet, 
                            EdmSchemaErrorSeverity.Error,
                            System.Data.Entity.Strings.FunctionImportEntityTypeDoesNotMatchEntitySet( 
                            this.FQName, this.EntitySet.EntityType.FQName, this.EntitySet.Name, this.ParentElement.FQName)); 
                    }
                } 

                if (null == entityType && null != EntitySet)
                {
                    AddError(ErrorCode.FunctionImportSpecifiesEntitySetButDoesNotReturnEntityType, 
                        EdmSchemaErrorSeverity.Error,
                        System.Data.Entity.Strings.FunctionImportSpecifiesEntitySetButNotEntityType(this.Name, this.ParentElement.FQName)); 
                } 
            }
        } 

        internal override SchemaElement Clone(SchemaElement parentElement)
        {
            Debug.Assert(IsFunctionImport, "we only support clone for FunctionImports"); 
            Function function = new FunctionImportElement((EntityContainer)parentElement);
            function._isAggregate = _isAggregate; 
            function._isBuiltIn = _isBuiltIn; 
            function._isNiladicFunction = _isNiladicFunction;
            function._isComposable = _isComposable; 
            function._entitySet = _entitySet;
            function._commandText = _commandText;
            function._storeFunctionName = _storeFunctionName;
            function._type = _type; 
            function._returnType = _returnType;
            function._collectionKind = _collectionKind; 
            function._parameterTypeSemantics = _parameterTypeSemantics; 
            function._schema = _schema;
            function.Name = this.Name; 

            // Clone all the parameters
            foreach (Parameter parameter in this.Parameters)
            { 
                AddErrorKind error = function.Parameters.TryAdd((Parameter)parameter.Clone(function));
                Debug.Assert(error == AddErrorKind.Succeeded, "Since we are cloning a validated function, this should never fail"); 
            } 

            return function; 
        }
        #endregion

        #region Internal Properties 
        /// 
        /// 
        ///  
        /// 
        internal string UnresolvedReturnType 
        {
            get
            {
                return _unresolvedType; 
            }
            set 
            { 
                _unresolvedType = value;
            } 
        }
        #endregion //Internal Properties

        #region Private Methods 

        ///  
        /// The method that is called when a DbSchema attribute is encountered. 
        /// 
        /// An XmlReader positioned at the Type attribute. 
        private void HandleDbSchemaAttribute(XmlReader reader)
        {
            Debug.Assert(Schema.DataModel == SchemaDataModelOption.ProviderDataModel, "We shouldn't see this attribute unless we are parsing ssdl");
            Debug.Assert(reader != null); 

            _schema = reader.Value; 
        } 

        ///  
        /// Handler for the Version attribute
        /// 
        /// xml reader currently positioned at Version attribute
        private void HandleAggregateAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
            bool isAggregate = false; 
            HandleBoolAttribute(reader, ref isAggregate);
            IsAggregate = isAggregate; 
        }

        /// 
        /// Handler for the Namespace attribute 
        /// 
        /// xml reader currently positioned at Namespace attribute 
        private void HandleBuiltInAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
            bool isBuiltIn = false;
            HandleBoolAttribute(reader, ref isBuiltIn);
            IsBuiltIn = isBuiltIn;
        } 

        ///  
        /// Handler for the Alias attribute 
        /// 
        /// xml reader currently positioned at Alias attribute 
        private void HandleStoreFunctionNameAttribute(XmlReader reader)
        {
            Debug.Assert(reader != null);
            string value = reader.Value.ToString(); 
            if (!String.IsNullOrEmpty(value))
            { 
                value = value.Trim(); 
                StoreFunctionName = value;
            } 
        }

        /// 
        /// Handler for the NiladicFunction attribute 
        /// 
        /// xml reader currently positioned at Namespace attribute 
        private void HandleNiladicFunctionAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
            bool isNiladicFunction = false;
            HandleBoolAttribute(reader, ref isNiladicFunction);
            IsNiladicFunction = isNiladicFunction;
        } 

        ///  
        /// Handler for the IsComposable attribute 
        /// 
        /// xml reader currently positioned at Namespace attribute 
        private void HandleIsComposableFunctionAttribute(XmlReader reader)
        {
            Debug.Assert(reader != null);
            bool isComposable = true; 
            HandleBoolAttribute(reader, ref isComposable);
            IsComposable = isComposable; 
        } 

        private void HandleCommandTextFunctionElment(XmlReader reader) 
        {
            Debug.Assert(reader != null);

            FunctionCommandText commandText = new FunctionCommandText(this); 
            commandText.Parse(reader);
            _commandText = commandText; 
        } 

        private void HandleReturnTypeAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null);
            Debug.Assert(UnresolvedReturnType == null);
 
            string type;
            if (!Utils.GetString(Schema, reader, out type)) 
                return; 

            switch (RemoveTypeModifier(ref type)) 
            {
                case TypeModifier.Array:
                    CollectionKind = CollectionKind.Bag;
                    break; 
                case TypeModifier.None:
                    break; 
                default: 
                    Debug.Assert(false, "RemoveTypeModifier already checks for this");
                    break; 
            }

            if (!Utils.ValidateDottedName(Schema, reader, type))
                return; 

            UnresolvedReturnType = type; 
        } 

        ///  
        /// Handler for the Parameter Element
        /// 
        /// xml reader currently positioned at Parameter Element
        private void HandleParameterElement(XmlReader reader) 
        {
            Debug.Assert(reader != null); 
 
            Parameter parameter = new Parameter(this);
 
            parameter.Parse(reader);

            Parameters.Add(parameter, true, Strings.ParameterNameAlreadyDefinedDuplicate);
        } 

        ///  
        /// Handler for the ReturnType element 
        /// 
        /// xml reader currently positioned at ReturnType element 
        private void HandleReturnTypeElement(XmlReader reader)
        {
            Debug.Assert(reader != null);
 
            ReturnType returnType = new ReturnType(this);
 
            returnType.Parse(reader); 

            this._returnType = returnType; 

        }

 
        /// 
        /// Handles ParameterTypeSemantics attribute 
        ///  
        /// 
        private void HandleParameterTypeSemanticsAttribute(XmlReader reader) 
        {
            Debug.Assert(reader != null);

            string value = reader.Value; 

            if (String.IsNullOrEmpty(value)) 
            { 
                return;
            } 

            value = value.Trim();

            if (!String.IsNullOrEmpty(value)) 
            {
                switch (value) 
                { 
                    case "ExactMatchOnly":
                        ParameterTypeSemantics = ParameterTypeSemantics.ExactMatchOnly; 
                        break;
                    case "AllowImplicitPromotion":
                        ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitPromotion;
                        break; 
                    case "AllowImplicitConversion":
                        ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion; 
                        break; 
                    default:
                        // don't try to use the name of the function, because we are still parsing the 
                        // attributes, and we may not be to the name attribute yet.
                        AddError(ErrorCode.InvalidValueForParameterTypeSemantics, EdmSchemaErrorSeverity.Error, reader,
                            System.Data.Entity.Strings.InvalidValueForParameterTypeSemanticsAttribute(
                                          value)); 

                        break; 
                } 
            }
        } 

        /// 
        /// Handler for the EntitySet attribute
        ///  
        /// xml reader currently positioned at EntitySet attribute
        private void HandleEntitySetAttribute(XmlReader reader) 
        { 
            Debug.Assert(reader != null);
            string entitySetName; 
            if (Utils.GetString(Schema, reader, out entitySetName))
            {
                _unresolvedEntitySet = entitySetName;
            } 
        }
 
        #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