#region Copyright (c) Microsoft Corporation /// /// Copyright (c) Microsoft Corporation. All Rights Reserved. /// Information Contained Herein is Proprietary and Confidential. /// #endregion using System; using System.Diagnostics; using System.IO; using System.Xml; using System.Xml.Schema; using Discovery = System.Web.Services.Discovery; using Description = System.Web.Services.Description; using MetadataSection = System.ServiceModel.Description.MetadataSection; using XmlSerialization = System.Xml.Serialization; #if WEB_EXTENSIONS_CODE using System.Web.Resources; #else using Microsoft.VSDesigner.WCF.Resources; #endif #if WEB_EXTENSIONS_CODE namespace System.Web.Compilation.WCFModel #else namespace Microsoft.VSDesigner.WCFModel #endif { /// /// This class presents a single metadata file in the ReferenceGroup /// /// #if WEB_EXTENSIONS_CODE internal class MetadataFile : ExternalFile #else [CLSCompliant(true)] public class MetadataFile : ExternalFile #endif { // Default File Name public const string DEFAULT_FILE_NAME = "service"; private MetadataType m_MetadataType; private string m_SourceUrl; // A GUID string private string m_ID; private int m_SourceId; // properties to merge metadata private bool m_Ignore; private bool m_IsMergeResult; private int SOURCE_ID_NOT_SPECIFIED = 0; private MetadataContent m_CachedMetadata; // Content of the metadata file, one of them must be private byte[] m_BinaryContent; /// /// Constructor /// /// Must support a default construct for XmlSerializer public MetadataFile() { m_ID = Guid.NewGuid().ToString(); m_BinaryContent = new byte[] { }; } /// /// Constructor /// /// File Name /// SourceUrl /// File Content /// public MetadataFile(string name, string url, string content) : base(name) { m_ID = Guid.NewGuid().ToString(); m_SourceUrl = url; if (content == null) { throw new ArgumentNullException("content"); } LoadContent(content); } /// /// Constructor /// /// File Name /// SourceUrl /// File Content /// public MetadataFile(string name, string url, byte[] byteContent) : base(name) { m_ID = Guid.NewGuid().ToString(); m_SourceUrl = url; if (byteContent == null) { throw new ArgumentNullException("byteContent"); } LoadContent(byteContent); } /// /// Retrieves the file content in binary format /// /// /// public byte[] BinaryContent { get { return m_BinaryContent; } } /// /// Cached state /// /// /// private MetadataContent CachedMetadata { get { if (m_CachedMetadata == null) { m_CachedMetadata = LoadMetadataContent(m_MetadataType); } return m_CachedMetadata; } } /// /// Retrieves the file content /// /// /// public string Content { get { StreamReader memReader = new StreamReader(new MemoryStream(m_BinaryContent)); return memReader.ReadToEnd(); } } /// /// The Type of Metadata /// /// /// [XmlSerialization.XmlAttribute("MetadataType")] public MetadataType FileType { get { return m_MetadataType; } set { m_MetadataType = value; } } /// /// GUID string, it is used to track a metadata item (when it is updated) /// /// /// [XmlSerialization.XmlAttribute()] public string ID { get { return m_ID; } set { m_ID = value; } } /// /// If it is true, the metadata file will be ignored by the code generator /// /// /// [XmlSerialization.XmlAttribute()] public bool Ignore { get { return m_Ignore; } set { m_Ignore = value; } } /// /// A special attribute used by XmlSerializer to decide whether Ignore attribute exists /// /// /// [XmlSerialization.XmlIgnore()] public bool IgnoreSpecified { get { return m_Ignore; } set { if (!value) { m_Ignore = false; } } } /// /// whether the metadata file is a result of merging /// /// /// [XmlSerialization.XmlAttribute()] public bool IsMergeResult { get { return m_IsMergeResult; } set { m_IsMergeResult = value; } } /// /// A special attribute used by XmlSerializer to decide whether IsMergeResult attribute exists /// /// /// [XmlSerialization.XmlIgnore()] public bool IsMergeResultSpecified { get { return m_IsMergeResult; } set { if (!value) { m_IsMergeResult = false; } } } /// /// Retrieves the content of a discovery file /// /// /// public Discovery.DiscoveryDocument MetadataDiscoveryDocument { get { return CachedMetadata.MetadataDiscoveryDocument; } } /// /// format error /// /// /// [XmlSerialization.XmlIgnore()] public Exception MetadataFormatError { get { return CachedMetadata.MetadataFormatError; } } /// /// Retrieves the content of a WSDL file /// /// /// public Description.ServiceDescription MetadataServiceDescription { get { return CachedMetadata.MetadataServiceDescription; } } /// /// Retrieves the content of a schema file /// /// /// public XmlSchema MetadataXmlSchema { get { return CachedMetadata.MetadataXmlSchema; } } /// /// Retrieves the content of an Xml file /// /// /// public XmlDocument MetadataXmlDocument { get { return CachedMetadata.MetadataXmlDocument; } } /// /// the SourceId links the the SourceId in the MetadataSource table /// /// /// [XmlSerialization.XmlAttribute()] public int SourceId { get { return m_SourceId; } set { if (value < 0) { Debug.Fail("Source ID shouldn't be a nagtive number"); throw new ArgumentException(WCFModelStrings.ReferenceGroup_InvalidSourceId); } m_SourceId = value; } } /// /// A special attribute used by XmlSerializer to decide whether SourceId attribute exists /// /// /// [XmlSerialization.XmlIgnore()] public bool SourceIdSpecified { get { return m_SourceId != SOURCE_ID_NOT_SPECIFIED; } set { if (!value) { m_SourceId = SOURCE_ID_NOT_SPECIFIED; } } } /// /// The sourceUrl of the metadata file /// /// /// [XmlSerialization.XmlAttribute()] public string SourceUrl { get { return m_SourceUrl; } set { m_SourceUrl = value; } } /// /// Retrieves the TargetNamespace when it is a schema item or a WSDL item /// /// /// public string TargetNamespace { get { return CachedMetadata.TargetNamespace; } } /// /// Detemine the type of a metadata item /// /// /// File Type /// private MetadataType DetermineFileType(XmlReader reader) { try { if (reader.IsStartElement(XmlStrings.WSDL.Elements.Root, XmlStrings.WSDL.NamespaceUri)) { return MetadataType.Wsdl; } else if (reader.IsStartElement(XmlStrings.XmlSchema.Elements.Root, XmlStrings.XmlSchema.NamespaceUri)) { return MetadataType.Schema; } else if (reader.IsStartElement(XmlStrings.WSPolicy.Elements.Policy, XmlStrings.WSPolicy.NamespaceUri) || reader.IsStartElement(XmlStrings.WSPolicy.Elements.Policy, XmlStrings.WSPolicy.NamespaceUri15)) { return MetadataType.Policy; } else if (reader.IsStartElement(XmlStrings.DISCO.Elements.Root, XmlStrings.DISCO.NamespaceUri)) { return MetadataType.Disco; } else if (reader.IsStartElement(XmlStrings.DataServices.Elements.Root, XmlStrings.DataServices.NamespaceUri)) { return MetadataType.Edmx; } else { return MetadataType.Xml; } } catch (XmlException) { // This must mean that the document isn't an XML Document so we continue trying other things... return MetadataType.Unknown; } } /// /// return the default extension /// /// /// public string GetDefaultExtension() { switch (m_MetadataType) { case MetadataType.Disco: return "disco"; case MetadataType.Wsdl: return "wsdl"; case MetadataType.Schema: return "xsd"; case MetadataType.Xml: return "xml"; case MetadataType.Policy: return "xml"; case MetadataType.Edmx: return "edmx"; default: return "data"; } } /// /// return the default filename without extension /// /// /// public string GetDefaultFileName() { if (!String.IsNullOrEmpty(TargetNamespace)) { string ns = TargetNamespace; if (!ns.EndsWith("/", StringComparison.Ordinal)) { int i = ns.LastIndexOfAny(Path.GetInvalidFileNameChars()); if (i >= 0) { ns = ns.Substring(i + 1); } string defaultExtension = "." + GetDefaultExtension(); if (ns.Length > defaultExtension.Length && ns.EndsWith(defaultExtension, StringComparison.OrdinalIgnoreCase)) { ns = ns.Substring(0, ns.Length - defaultExtension.Length); } if (ns.Length > 0) { return ns; } } } return DEFAULT_FILE_NAME; } /// /// Load the content of a Metadata item into the object model /// /// /// internal void LoadContent(byte[] byteContent) { m_BinaryContent = byteContent; LoadContentFromTextReader(new StreamReader(new MemoryStream(byteContent))); } /// /// Load the content of a Metadata item into the object model /// /// /// internal void LoadContent(string content) { MemoryStream memStream = new MemoryStream(); StreamWriter contentWriter = new StreamWriter(memStream); contentWriter.Write(content); contentWriter.Flush(); m_BinaryContent = memStream.ToArray(); LoadContentFromTextReader(new StringReader(content)); } /// /// Load the content of a Metadata item into the object model /// /// /// private void LoadContentFromTextReader(TextReader contentReader) { if (contentReader == null) { throw new ArgumentNullException("contentReader"); } // reset... ErrorInLoading = null; m_CachedMetadata = null; using (XmlTextReader xmlReader = new XmlTextReader(contentReader)) { if (m_MetadataType == MetadataType.Unknown) { // If we don't know the metedata type, we try to sniff it... MetadataType fileType = DetermineFileType(xmlReader); // try m_CachedMetadata = LoadMetadataContent(fileType, xmlReader); if (m_CachedMetadata.MetadataFormatError == null) { m_MetadataType = fileType; } } } } /// /// the function is called when the metadata is removed, and we need clean up the content /// /// internal void CleanUpContent() { ErrorInLoading = null; m_BinaryContent = new byte[] { }; m_CachedMetadata = null; } /// /// Load schema/wsdl model from binary content. -- Parse the metadata content /// /// /// private MetadataContent LoadMetadataContent(MetadataType fileType) { if (ErrorInLoading != null) { return new MetadataContent(ErrorInLoading); } using (XmlTextReader xmlReader = new XmlTextReader(new StreamReader(new MemoryStream(m_BinaryContent)))) { return LoadMetadataContent(fileType, xmlReader); } } /// /// Load schema/wsdl model from text reader. -- it will parse the metadata content. /// /// /// private MetadataContent LoadMetadataContent(MetadataType fileType, XmlTextReader xmlReader) { MetadataContent cachedMetadata = new MetadataContent(); try { switch (fileType) { case MetadataType.Disco: cachedMetadata = new MetadataContent(Discovery.DiscoveryDocument.Read(xmlReader)); break; case MetadataType.Wsdl: cachedMetadata = new MetadataContent(Description.ServiceDescription.Read(xmlReader)); cachedMetadata.MetadataServiceDescription.RetrievalUrl = GetMetadataSourceUrl(); break; case MetadataType.Schema: cachedMetadata = new MetadataContent(XmlSchema.Read(xmlReader, null)); cachedMetadata.MetadataXmlSchema.SourceUri = GetMetadataSourceUrl(); break; case MetadataType.Unknown: // For unknown types, we don't do nothing... break; default: Debug.Assert(fileType == MetadataType.Xml || fileType == MetadataType.Policy || fileType == MetadataType.Edmx); XmlDocument tempDoc = new XmlDocument(); tempDoc.Load(xmlReader); cachedMetadata = new MetadataContent(tempDoc); break; } } catch (Exception ex) { cachedMetadata = new MetadataContent(ex); } return cachedMetadata; } /// /// convert metadata file to MetadataSection (to feed code/proxy generator) /// We don't reuse the buffered object model, because the generator could modify & corrupt them. /// /// internal MetadataSection CreateMetadataSection() { MetadataContent metadata = LoadMetadataContent(m_MetadataType); if (metadata.MetadataFormatError != null) { throw metadata.MetadataFormatError; } MetadataSection metadataSection = null; switch (FileType) { case MetadataType.Unknown: break; case MetadataType.Disco: if (metadata.MetadataServiceDescription != null) { metadataSection = MetadataSection.CreateFromServiceDescription(metadata.MetadataServiceDescription); } break; case MetadataType.Wsdl: // We need to make a copy of the WSDL object model since the act of importing it actuall // modifies it, and we don't want the cached instance to be polluted... System.Web.Services.Description.ServiceDescription description = metadata.MetadataServiceDescription; if (description != null) { metadataSection = MetadataSection.CreateFromServiceDescription(description); } break; case MetadataType.Schema: if (metadata.MetadataXmlSchema != null) { metadataSection = MetadataSection.CreateFromSchema(metadata.MetadataXmlSchema); } break; case MetadataFile.MetadataType.Policy: if (metadata.MetadataXmlDocument != null) { metadataSection = MetadataSection.CreateFromPolicy(metadata.MetadataXmlDocument.DocumentElement, null); } break; case MetadataFile.MetadataType.Xml: case MetadataFile.MetadataType.Edmx: if (metadata.MetadataXmlDocument != null) { metadataSection = new MetadataSection(null, null, metadata.MetadataXmlDocument.DocumentElement); } break; default: System.Diagnostics.Debug.Fail("Unknown Type?"); break; } return metadataSection; } /// /// Metadata source Url is used in error messages /// /// internal string GetMetadataSourceUrl() { if (String.IsNullOrEmpty(SourceUrl)) { return FileName; } else { return SourceUrl; } } /// /// Metadata File Type Enum /// /// public enum MetadataType { [XmlSerialization.XmlEnum(Name = "Unknown")] Unknown = 0, [XmlSerialization.XmlEnum(Name = "Disco")] Disco = 1, [XmlSerialization.XmlEnum(Name = "Wsdl")] Wsdl = 2, [XmlSerialization.XmlEnum(Name = "Schema")] Schema = 3, [XmlSerialization.XmlEnum(Name = "Policy")] Policy = 4, [XmlSerialization.XmlEnum(Name = "Xml")] Xml = 5, [XmlSerialization.XmlEnum(Name = "Edmx")] Edmx = 6, } /// /// Metadata contained inside the file. Only one of field is valid, which depends on the MetadataType /// /// private class MetadataContent { private Discovery.DiscoveryDocument m_MetadataDiscoveryDocument; private Description.ServiceDescription m_MetadataServiceDescription; private XmlSchema m_MetadataXmlSchema; private XmlDocument m_MetadataXmlDocument; private Exception m_MetadataFormatError; private string m_TargetNamespace; internal MetadataContent() { m_TargetNamespace = String.Empty; } internal MetadataContent(Discovery.DiscoveryDocument discoveryDocument) { m_MetadataDiscoveryDocument = discoveryDocument; m_TargetNamespace = String.Empty; } internal MetadataContent(Description.ServiceDescription serviceDescription) { m_MetadataServiceDescription = serviceDescription; m_TargetNamespace = serviceDescription.TargetNamespace; } internal MetadataContent(XmlSchema schema) { m_MetadataXmlSchema = schema; m_TargetNamespace = schema.TargetNamespace; } internal MetadataContent(XmlDocument document) { m_MetadataXmlDocument = document; m_TargetNamespace = String.Empty; } internal MetadataContent(Exception metadataFormatError) { m_MetadataFormatError = metadataFormatError; } /// /// Retrieves the content of a discovery file /// /// /// public Discovery.DiscoveryDocument MetadataDiscoveryDocument { get { return m_MetadataDiscoveryDocument; } } /// /// Error message if /// /// /// public Exception MetadataFormatError { get { return m_MetadataFormatError; } } /// /// Retrieves the content of a WSDL file /// /// /// public Description.ServiceDescription MetadataServiceDescription { get { return m_MetadataServiceDescription; } } /// /// Retrieves the content of a schema file /// /// /// public XmlSchema MetadataXmlSchema { get { return m_MetadataXmlSchema; } } /// /// Retrieves the content of an Xml file /// /// /// public XmlDocument MetadataXmlDocument { get { return m_MetadataXmlDocument; } } /// /// Retrieves the TargetNamespace when it is a schema item or a WSDL item /// /// /// public string TargetNamespace { get { return m_TargetNamespace; } } } } }