File: System\Data\EntityModel\EntityClassGenerator.cs
Project: ndp\fx\src\DataWeb\Design\System.Data.Services.Design.csproj (System.Data.Services.Design)
//---------------------------------------------------------------------
// <copyright file="EntityClassGenerator.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.Collections.Generic;
using System.Data.EntityModel;
using System.Data.Metadata.Edm;
using System.Data.Services.Design.Common;
using System.Data.Services.Design.Xml;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.XPath;
 
namespace System.Data.Common.Utils
{
    internal static class EntityUtil
    {
        static internal void CheckArgumentNull<T>(T value, string parameterName) where T : class
        {
            System.Data.Services.Design.EntityUtil.CheckArgumentNull<T>(value, parameterName);
        }
    }
}
 
namespace System.Data.Services.Design
{
    internal static class EntityUtil
    {
        static internal void CheckArgumentNull<T>(T value, string parameterName) where T : class
        {
            EDesignUtil.CheckArgumentNull<T>(value, parameterName);
        }
 
        static internal void CheckStringArgument(string value, string parameterName)
        {
            EDesignUtil.CheckStringArgument(value, parameterName);
        }
    }
 
    /// <summary>
    /// Summary description for CodeGenerator.
    /// </summary>
    public sealed class EntityClassGenerator
    {
        #region Instance Fields
        private LanguageOption _languageOption = LanguageOption.GenerateCSharpCode;
        private DataServiceCodeVersion _version = DataServiceCodeVersion.V1;
        private EdmToObjectNamespaceMap _edmToObjectNamespaceMap = new EdmToObjectNamespaceMap();
        private bool _useDataServiceCollection;
#if QFE_ENV
        private bool _useDataServiceCollectionExplicitlySet;
        private bool _versionExplicitlySet;
        const string UseDSC_EnvironmentVariable = "dscodegen_usedsc";
        const string Version_EnvironmentVariable = "dscodegen_version";
        const string Version2Dot0 = "2.0";
        const string UseDSCTrue = "1";
#endif
 
        #endregion
 
        #region Events
 
        /// <summary>
        /// The event that is raised when a type is generated
        /// </summary>
        public event EventHandler<TypeGeneratedEventArgs> OnTypeGenerated;
 
        /// <summary>
        /// The event that is raised when a property is generated
        /// </summary>
        public event EventHandler<PropertyGeneratedEventArgs> OnPropertyGenerated;
 
        #endregion
 
        #region Public Methods
        /// <summary>
        /// 
        /// </summary>
        public EntityClassGenerator()
        {
        }
 
        /// <summary>
        /// 
        /// </summary>
        public EntityClassGenerator(LanguageOption languageOption)
        {
            _languageOption = EDesignUtil.CheckLanguageOptionArgument(languageOption, "languageOption");
        }
 
        /// <summary>
        /// Get or set the flag that specifies if code generation emits the code necessary for data binding
        /// </summary>
        public bool UseDataServiceCollection
        {
            get
            {
                return _useDataServiceCollection;
            }
 
            set
            {
                _useDataServiceCollection = value;
#if QFE_ENV 
                _useDataServiceCollectionExplicitlySet = true;
#endif
            }
        }
 
        /// <summary>
        /// Gets and Sets the WCF Data Service version which the generated code will be compatible with.
        /// </summary>
        public DataServiceCodeVersion Version
        {
            get 
            { 
                return _version; 
            }
 
            set 
            { 
                _version = EDesignUtil.CheckDataServiceCodeVersionArgument(value, "value"); 
#if QFE_ENV
                _versionExplicitlySet = true; 
#endif
            }
        }
 
        /// <summary>
        /// Gets and Sets the Language to use for code generation.
        /// </summary>
        public LanguageOption LanguageOption
        {
            get { return _languageOption; }
            set { _languageOption = EDesignUtil.CheckLanguageOptionArgument(value, "value"); }
        }
 
        /// <summary>
        /// Gets the map entries use to customize the namespace of .net types that are generated
        /// and referenced by the generated code
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
        public EdmToObjectNamespaceMap EdmToObjectNamespaceMap
        {
            get { return _edmToObjectNamespaceMap; }
        }
 
        public IList<EdmSchemaError> GenerateCode(XmlReader sourceReader, string targetFilePath)
        {
            EntityUtil.CheckArgumentNull(sourceReader, "sourceReader");
            EntityUtil.CheckStringArgument(targetFilePath, "targetPath");
            using (LazyTextWriterCreator target = new LazyTextWriterCreator(targetFilePath))
            {
                // we do want to close the file
                return GenerateCode(sourceReader, target, null);
            }
        }
 
        /// <summary>
        /// Generate code by reading an EDMX schema from an XmlReader and outputting the code into a TextWriter
        /// </summary>
        /// <param name="sourceReader">Reader with a EDMX schema in it</param>
        /// <param name="targetWriter">Target writer for the generated code</param>
        /// <param name="namespacePrefix">Prefix to use for generated namespaces</param>
        /// <remarks>
        /// Note that the NamespacePrefix is used as the only namespace for types in the same namespace
        /// as the default container, and as a prefix for the server-provided namespace for everything else. If
        /// this argument is null, the server-provided namespaces are used for all types.
        /// </remarks>
        /// <returns></returns>
        public IList<EdmSchemaError> GenerateCode(XmlReader sourceReader, TextWriter targetWriter, string namespacePrefix)
        {
            EntityUtil.CheckArgumentNull(sourceReader, "sourceReader");
            EntityUtil.CheckArgumentNull(targetWriter, "targetWriter");
            using (LazyTextWriterCreator target = new LazyTextWriterCreator(targetWriter))
            {
                return GenerateCode(sourceReader, target, namespacePrefix);
            }   // does not actually close the targetWriter - that is the caller's responsibility
        }
 
        /// <summary>
        /// Given the specified element in a given namespace, remaps it into a 
        /// <paramref name="targetNamespace"/> and trims elements and attributes that
        /// don't conform to the schema.
        /// </summary>
        /// <param name="element">Element to fit.</param>
        /// <param name="schemaNamespace">Namespace of element.</param>
        /// <param name="targetNamespace">Target namespace.</param>
        /// <returns>A new <see cref="XElement"/> that fits the specified <paramref name="targetNamespace"/>.</returns>
        private static XElement FitElementToSchema(XElement element, string schemaNamespace, string targetNamespace)
        {
            Debug.Assert(element != null, "element != null");
            Debug.Assert(schemaNamespace != null, "schemaNamespace != null");
            Debug.Assert(targetNamespace != null, "targetNamespace != null");
            Debug.Assert(
                targetNamespace == XmlConstants.EdmV1dot1Namespace,
                "targetNamespace == XmlConstants.EdmV1dot1Namespace -- otherwise update CreateTargetSchemaSet to pull other schemas");
 
            XmlSchemaSet schemas = CreateTargetSchemaSet();
            XElement result = UpdateNamespaces(element, schemaNamespace, targetNamespace);
            XNodeSchemaApplier.Apply(schemas, result);
            return result;
        }
 
        /// <summary>
        /// Creates an <see cref="XmlReader"/> for the resource in the Entity Framework assembly
        /// specified by <paramref name="resourceName"/>.
        /// </summary>
        /// <param name="resourceName">Name of the resource to read.</param>
        /// <returns>A new <see cref="XmlReader"/> instance.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller should Dispose the XmlReader")]
        private static XmlReader CreateEdmResourceXmlReader(string resourceName)
        {
            bool success = false;
            Stream stream = null;
            XmlReader result = null;
            try
            {
                stream = typeof(EdmItemCollection).Assembly.GetManifestResourceStream(resourceName);
                result = XmlReader.Create(stream);
                success = true;
                return result;
            }
            finally
            {
                if (!success)
                {
                    if (result != null)
                    {
                        result.Close();
                    }
 
                    if (stream != null)
                    {
                        stream.Dispose();
                    }
                }
            }
        }
 
        /// <summary>Creates the <see cref="XmlSchemaSet"/> that adjusted schemas should target.</summary>
        /// <returns>A new <see cref="XmlSchemaSet"/> instance.</returns>
        private static XmlSchemaSet CreateTargetSchemaSet()
        {
            XmlSchemaSet result = new XmlSchemaSet();
            using (XmlReader reader = CreateEdmResourceXmlReader("System.Data.Resources.CodeGenerationSchema.xsd"))
            {
                result.Add(null, reader);
            }
 
            using (XmlReader reader = CreateEdmResourceXmlReader("System.Data.Resources.CSDLSchema_1_1.xsd"))
            {
                XmlSchema schema = result.Add(null, reader);
                RemoveReferentialConstraint(schema);
                AddCustomAttributesToEntityContainer(schema);
            }
 
            XmlSchemaSet result2 = new XmlSchemaSet();
            foreach (XmlSchema s in result.Schemas())
            {
                result2.Add(s);
            }
 
            return result2;
        }
 
        /// <summary>
        /// Removes the ReferentialConstraint element from the list of valid children for an association type.
        /// </summary>
        /// <param name="csdlSchema">Loaded <see cref="XmlSchema"/> for the 1.1 Entity Framework CSDL XSD.</param>
        private static void RemoveReferentialConstraint(XmlSchema csdlSchema)
        {
            Debug.Assert(csdlSchema != null, "csdlSchema != null");
 
            XmlSchemaComplexType associationType = csdlSchema.SchemaTypes[new XmlQualifiedName("TAssociation", csdlSchema.TargetNamespace)] as XmlSchemaComplexType;
            Debug.Assert(associationType != null, "associationType != null -- otherwise can't find TAssociation - CSDL resource has changed?");
 
            XmlSchemaSequence sequence = associationType.Particle as XmlSchemaSequence;
            XmlSchemaObject referentialConstraint = null;
            foreach (XmlSchemaObject item in sequence.Items)
            {
                XmlSchemaElement e = item as XmlSchemaElement;
                if (e.QualifiedName == new XmlQualifiedName("ReferentialConstraint", csdlSchema.TargetNamespace))
                {
                    referentialConstraint = e;
                    break;
                }
            }
 
            Debug.Assert(referentialConstraint != null, "referentialConstraint != null");
            sequence.Items.Remove(referentialConstraint);
        }
 
        /// <summary>
        /// Add any attribute to the entity container element in the schema. We need to do
        /// this, since this was missing from the xsd that got shipped in System.Data.Entity.dll
        /// in 3.5 SP1. Hence we need to make compensating changes now here. The reason why validating
        /// against this csd works in edmitemcollection is that they ignore all errors due to elements/attributes
        /// not in the edm namespace.
        /// </summary>
        /// <param name="csdlSchema">Loaded <see cref="XmlSchema"/> for the 1.1 Entity Framework CSDL XSD.</param>
        private static void AddCustomAttributesToEntityContainer(XmlSchema csdlSchema)
        {
            Debug.Assert(csdlSchema != null, "csdlSchema != null");
 
            XmlSchemaElement entityContainerElement = csdlSchema.Elements[new XmlQualifiedName("EntityContainer", csdlSchema.TargetNamespace)] as XmlSchemaElement;
            Debug.Assert(entityContainerElement != null, "entityContainerElement != null -- otherwise can't find EntityContainer element- CSDL resource has changed?");
 
            XmlSchemaComplexType complexType = entityContainerElement.SchemaType as XmlSchemaComplexType;
            complexType.AnyAttribute = new XmlSchemaAnyAttribute();
            complexType.AnyAttribute.Namespace = "##other";
            complexType.AnyAttribute.ProcessContents = XmlSchemaContentProcessing.Lax;
        }
 
        /// <summary>
        /// Creates a list of readers for adjusted schemas in the specifed <paramref name="sourceReader"/>/
        /// </summary>
        /// <param name="sourceReader">Input source for metadata.</param>
        /// <returns>A list of readers for Schema elements.</returns>
        /// <remarks>
        /// These are some processing differences between V1 and V2 metadata.
        /// 
        /// - V1 processes all Schema elements at any depth.
        /// - V2 processes only Schema elements that are at the root or under *:Edmx/*:DataServices[1] nodes
        ///
        /// - V1 processes Schema elements from known namespaces
        /// - V2 processes Schema elements from the first Schema's namespace
        /// 
        /// - V1 ignores the DataServiceVersion attribute on the DataServices element
        /// - V2 rejects DataServiceVersion attributes with a version that is not 1.0 or 2.0
        /// 
        /// The detection hinges on whether all Schema elements belong to 1.1 and 1.0 namespaces;
        /// if so, we use V1 rules.
        /// </remarks>
        private static List<XmlReader> CreateReaders(XmlReader sourceReader)
        {
            Debug.Assert(sourceReader != null, "sourceReader != null");
 
            NameTable nameTable = new NameTable();
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
            namespaceManager.AddNamespace("m", XmlConstants.DataWebMetadataNamespace);
            namespaceManager.AddNamespace("edmx", XmlConstants.EdmxNamespace);
            namespaceManager.AddNamespace("edmv1", XmlConstants.EdmV1Namespace);
            namespaceManager.AddNamespace("edmv1_1", XmlConstants.EdmV1dot1Namespace);
 
            XDocument sourceDocument = XDocument.Load(sourceReader);
            List<XElement> schemaElements = new List<XElement>();
            List<XmlReader> result = new List<XmlReader>();
 
            // Check for V2 schemas.
            if (TryCreateReadersV2(sourceDocument, schemaElements))
            {
                Debug.Assert(schemaElements.Count > 0, "schemaElements.Count > 0 -- otherwise TryCreateReadersV2 should have returned false");
                string schemaNamespace = null;
                for (int i = 0; i < schemaElements.Count; i++)
                {
                    if (schemaNamespace == null)
                    {
                        schemaNamespace = schemaElements[i].Name.NamespaceName;
                    }
                    else
                    {
                        if (schemaNamespace != schemaElements[i].Name.NamespaceName)
                        {
                            throw new NotSupportedException(Strings.InvalidMetadataMultipleNamespaces(schemaNamespace, schemaElements[i].Name.NamespaceName));
                        }
                    }
 
                    XElement fitted = FitElementToSchema(schemaElements[i], schemaNamespace, XmlConstants.EdmV1dot1Namespace);
                    result.Add(fitted.CreateReader());
                }
            }
            else
            {
                Debug.Assert(result.Count == 0, "result.Count == 0 -- otherwise TryCreateReadesV2 should have returned true");
                CreateReadersV1(sourceDocument, namespaceManager, result);
            }
 
            return result;
        }
 
        /// <summary>Creates readers as per V1 rules.</summary>
        /// <param name="sourceDocument">Parsed source document.</param>
        /// <param name="namespaceManager">Namespace manager with 'edmv1' and 'edmv1_1' defined.</param>
        /// <param name="readers">List of XmlReaders to populate.</param>
        /// <remarks>See comments on CreateReaders for details.</remarks>
        private static void CreateReadersV1(XDocument sourceDocument, XmlNamespaceManager namespaceManager, List<XmlReader> readers)
        {
            Debug.Assert(sourceDocument != null, "sourceDocument != null");
            Debug.Assert(namespaceManager != null, "namespaceManager != null");
            Debug.Assert(readers != null, "readers != null");
            Debug.Assert(namespaceManager.HasNamespace("edmv1"), "namespaceManager.HasNamespace('edmv1')");
            Debug.Assert(namespaceManager.HasNamespace("edmv1_1"), "namespaceManager.HasNamespace('edmv1_1')");
 
            // Look for 1.0 schema elements
            foreach (var element in sourceDocument.XPathSelectElements("//edmv1:Schema", namespaceManager))
            {
                readers.Add(element.CreateReader());
            }
 
            // Look for 1.1 schema elements
            foreach (var element in sourceDocument.XPathSelectElements("//edmv1_1:Schema", namespaceManager))
            {
                readers.Add(element.CreateReader());
            }
        }
 
        /// <summary>
        /// Determines whether the specified <paramref name="namespaceName"/> is a known Schema element.
        /// </summary>
        /// <param name="namespaceName">Name of namespace to check.</param>
        /// <returns>true if <paramref name="namespaceName"/> is a known Schema element; false otherwise.</returns>
        private static bool IsKnownSchemaNamespace(string namespaceName)
        {
            return namespaceName == XmlConstants.EdmV1Namespace || namespaceName == XmlConstants.EdmV1dot1Namespace;
        }
 
        /// <summary>Determines whether the specified <paramref name="element"/> is a V2 Schema element.</summary>
        /// <param name="element">Element to check.</param>
        /// <returns>true if <paramref name="element"/> is a V2 Schema element.</returns>
        /// <remarks>
        /// A V2 Schema is an element with a local name of 'Schema', within a namespace that doesn'
        /// t belong in the known V1 namespaces.
        /// </remarks>
        private static bool IsSchemaV2(XElement element)
        {
            Debug.Assert(element != null, "element != null");
            var name = element.Name;
            return name.LocalName == "Schema" && !IsKnownSchemaNamespace(name.NamespaceName);
        }
 
        /// <summary>Tries to create readers according to V2 rules.</summary>
        /// <param name="sourceDocument">Source document for metadata.</param>
        /// <param name="schemaElements">List of elements to populate with Schema nodes.</param>
        /// <returns>true if schema elements were found according to V2 rules; false otherwise.</returns>
        /// <remarks>See comments on CreateReaders for details.</remarks>
        private static bool TryCreateReadersV2(XDocument sourceDocument, List<XElement> schemaElements)
        {
            Debug.Assert(sourceDocument != null, "sourceDocument != null");
            Debug.Assert(schemaElements != null, "schemaElements != null");
 
            bool result = false;
 
            // Check for a root Schema element in a new namespace.
            var root = sourceDocument.Root;
            if (IsSchemaV2(root))
            {
                schemaElements.Add(root);
                result = true;
            }
            else
            {
                if (root.Name.LocalName == XmlConstants.EdmxElement)
                {
                    XElement dataServiceElement = root.Elements().Where(e => e.Name.LocalName == XmlConstants.EdmxDataServicesElement).FirstOrDefault();
                    if (dataServiceElement != null)
                    {
                        XNamespace metadataNs = XmlConstants.DataWebMetadataNamespace;
                        XAttribute version = dataServiceElement.Attributes(metadataNs + XmlConstants.HttpDataServiceVersion).FirstOrDefault();
                        if (version != null && version.Value != XmlConstants.DataServiceVersion1Dot0 && version.Value != XmlConstants.DataServiceVersion2Dot0)
                        {
                            throw new InvalidOperationException(Strings.InvalidMetadataDataServiceVersion(version.Value));
                        }
 
                        schemaElements.AddRange(dataServiceElement.Elements().Where(IsSchemaV2));
                        result = schemaElements.Count > 0;
                    }
                }
            }
 
            return result;
        }
 
        /// <summary>
        /// Updates the namespaces under the specified <paramref name="element"/>, changing <paramref name="oldNamespaceName"/> 
        /// into <paramref name="newNamespaceName"/>.
        /// </summary>
        /// <param name="element">Element to update (recursively).</param>
        /// <param name="oldNamespaceName">Old namespace.</param>
        /// <param name="newNamespaceName">New namespace.</param>
        /// <returns>The updated element.</returns>
        /// <remarks>
        /// Currently, the updates are in-place, so the returned element is always <paramref name="element"/>.
        /// 
        /// For valid cases, there will be no qualified CSDL attributes, so those code paths are
        /// not likely to hit - however the code preserves the namespaces so it can fail
        /// correctly later on.
        /// </remarks>
        private static XElement UpdateNamespaces(XElement element, string oldNamespaceName, string newNamespaceName)
        {
            Debug.Assert(element != null, "element != null");
            Debug.Assert(oldNamespaceName != null, "oldNamespaceName != null");
            Debug.Assert(newNamespaceName != null, "newNamespaceName != null");
 
            XNamespace oldNamespace = XNamespace.Get(oldNamespaceName);
            XNamespace newNamespace = XNamespace.Get(newNamespaceName);
 
            Stack<XElement> pending = new Stack<XElement>();
            pending.Push(element);
            do
            {
                XElement e = pending.Pop();
                if (e.Name.Namespace == oldNamespace)
                {
                    e.Name = newNamespace.GetName(e.Name.LocalName);
                }
 
                List<XAttribute> attributesToReplace = null;
                foreach (XAttribute attribute in e.Attributes())
                {
                    if (attribute.IsNamespaceDeclaration)
                    {
                        if (attribute.Value == oldNamespaceName)
                        {
                            attribute.Value = newNamespaceName;
                        }
                    }
                    else if (attribute.Name.Namespace == oldNamespace || IsOpenTypeAttribute(attribute))
                    {
                        XNodeSchemaApplier.AppendWithCreation(ref attributesToReplace, attribute);
                    }
                }
 
                if (attributesToReplace != null)
                {
                    attributesToReplace.Remove();
                    foreach (XAttribute attribute in attributesToReplace)
                    {
                        if (IsOpenTypeAttribute(attribute))
                        {
                            XAttribute existingAttribute = e.Attributes().SingleOrDefault(a => a.Name.NamespaceName == XmlConstants.EdmV1dot2Namespace && a.Name.LocalName == attribute.Name.LocalName);
 
                            if (existingAttribute == null)
                            {
                                e.Add(new XAttribute(XNamespace.Get(XmlConstants.EdmV1dot2Namespace) + attribute.Name.LocalName, attribute.Value));
                            }
                            else
                            {
                                existingAttribute.Value = attribute.Value;
                            }
                        }
                        else
                        {
                            e.Add(new XAttribute(newNamespace.GetName(attribute.Name.LocalName), attribute.Value));
                        }
                    }
                }
 
                foreach (var child in e.Elements())
                {
                    pending.Push(child);
                }
            }
            while (pending.Count > 0);
 
            return element;
        }
 
        private IList<EdmSchemaError> GenerateCode(XmlReader sourceReader, LazyTextWriterCreator target, string namespacePrefix)
        {
            List<XmlReader> readers = CreateReaders(sourceReader);
            List<EdmSchemaError> errors = new List<EdmSchemaError>();
            EdmItemCollection itemCollection = new EdmItemCollection(readers);
 
#if QFE_ENV
            _version = _versionExplicitlySet ? _version : GetDataServiceCodeVersionFromEnvironment();
            _useDataServiceCollection = _useDataServiceCollectionExplicitlySet ? _useDataServiceCollection : GetUseDataServiceCollectionFromEnvironment();
#endif
            if (_useDataServiceCollection && _version == DataServiceCodeVersion.V1)
            {
                throw new InvalidOperationException(Strings.VersionV1RequiresUseDataServiceCollectionFalse);
            }
 
            // generate code
            using (ClientApiGenerator generator = new ClientApiGenerator(null, itemCollection, this, errors, namespacePrefix))
            {
                generator.GenerateCode(target);
            }
 
            return errors;
        }
 
#if QFE_ENV
        //[System.Security.Permissions.EnvironmentPermission(System.Security.Permissions.SecurityAction.Assert, Read = EntityClassGenerator.Version_EnvironmentVariable)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2106")]
        private static DataServiceCodeVersion GetDataServiceCodeVersionFromEnvironment()
        {
            return Environment.GetEnvironmentVariable(EntityClassGenerator.Version_EnvironmentVariable) == EntityClassGenerator.Version2Dot0 ?
                                DataServiceCodeVersion.V2 : 
                                DataServiceCodeVersion.V1;
        }
 
        //[System.Security.Permissions.EnvironmentPermission(System.Security.Permissions.SecurityAction.Assert, Read = EntityClassGenerator.UseDSC_EnvironmentVariable)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2106")]
        private static bool GetUseDataServiceCollectionFromEnvironment()
        {
            return Environment.GetEnvironmentVariable(EntityClassGenerator.UseDSC_EnvironmentVariable) == EntityClassGenerator.UseDSCTrue;
        }
#endif
 
        /// <summary>Checks if the given attribute refers to OpenType attribute.</summary>
        /// <param name="attribute">Input attribute.</param>
        /// <returns>true if the attribute is OpenType attribute, false otherwise.</returns>
        private static bool IsOpenTypeAttribute(XAttribute attribute)
        {
            return attribute.Name.LocalName == XmlConstants.DataWebOpenTypeAttributeName && attribute.Name.Namespace == XNamespace.None;
        }
 
        #endregion
 
        #region Event Helpers
 
        /// <summary>
        /// Helper method that raises the TypeGenerated event
        /// </summary>
        /// <param name="eventArgs">The event arguments passed to the subscriber</param>
        internal void RaiseTypeGeneratedEvent(TypeGeneratedEventArgs eventArgs)
        {
            if (this.OnTypeGenerated != null)
            {
                this.OnTypeGenerated(this, eventArgs);
            }
        }
 
        /// <summary>
        /// Helper method that raises the PropertyGenerated event
        /// </summary>
        /// <param name="eventArgs">The event arguments passed to the subscriber</param>
        internal void RaisePropertyGeneratedEvent(PropertyGeneratedEventArgs eventArgs)
        {
            if (this.OnPropertyGenerated != null)
            {
                this.OnPropertyGenerated(this, eventArgs);
            }
        }
 
        #endregion
    }
}