#region Copyright (c) Microsoft Corporation /// /// Copyright (c) Microsoft Corporation. All Rights Reserved. /// Information Contained Herein is Proprietary and Confidential. /// #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Xml; using System.Xml.Schema; using 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 { /// /// Map file loader /// /// /// MapFileLoader instance and MapFile instance should be 1:1 mapping. /// #if WEB_EXTENSIONS_CODE internal abstract class MapFileLoader #else [CLSCompliant(true)] public abstract class MapFileLoader #endif { /// /// Save the given map file. /// /// The map file to be saved public void SaveMapFile(MapFile mapFile) { Debug.Assert(mapFile != null, "mapFile is null!"); SaveExternalFiles(mapFile); using (var mapFileWriter = GetMapFileWriter()) { GetMapFileSerializer().Serialize(mapFileWriter, Unwrap(mapFile)); } } /// /// Load the map file. /// /// Concrete map file instance. public MapFile LoadMapFile() { MapFile mapFile = null; using (var mapFileReader = GetMapFileReader()) { var proxyGenerationErrors = new List(); ValidationEventHandler handler = (sender, e) => { bool isError = (e.Severity == XmlSeverityType.Error); proxyGenerationErrors.Add( new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, MapFileName, e.Exception, !isError)); if (isError) { throw e.Exception; } }; var readerSettings = new XmlReaderSettings() { Schemas = GetMapFileSchemaSet(), ValidationType = ValidationType.Schema, ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings, }; using (XmlReader reader = XmlReader.Create(mapFileReader, readerSettings, string.Empty)) { try { readerSettings.ValidationEventHandler += handler; mapFile = ReadMapFile(reader); SetMapFileLoadErrors(mapFile, proxyGenerationErrors); } finally { readerSettings.ValidationEventHandler -= handler; } } } if (mapFile != null) { LoadExternalFiles(mapFile); } return mapFile; } /// /// Load metadata file from file system /// public void LoadMetadataFile(MetadataFile metadataFile) { try { metadataFile.CleanUpContent(); metadataFile.LoadContent(ReadMetadataFile(metadataFile.FileName)); } catch (Exception ex) { metadataFile.ErrorInLoading = ex; } } /// /// Load extension file /// public void LoadExtensionFile(ExtensionFile extensionFile) { try { extensionFile.CleanUpContent(); extensionFile.ContentBuffer = ReadExtensionFile(extensionFile.FileName); } catch (Exception ex) { extensionFile.ErrorInLoading = ex; } } #region protected abstract methods /// /// The name of the file where the MapFile instance is loaded. /// protected abstract string MapFileName { get; } /// /// Wrap the map file impl. /// protected abstract MapFile Wrap(object mapFileImpl); /// /// Unwrap the map file. /// protected abstract object Unwrap(MapFile mapFile); /// /// Get the map file schema set /// /// Xml schema set of the map file protected abstract XmlSchemaSet GetMapFileSchemaSet(); /// /// Get the map file serializer /// /// Xml serializer of the map file protected abstract XmlSerializer GetMapFileSerializer(); /// /// Get access to a text reader that gets access to the map file byte stream /// /// Text reader of the map file protected virtual TextReader GetMapFileReader() { throw new NotImplementedException(); } /// /// Get access to a text writer that writes the map file byte stream. /// /// Text writer of the map file protected virtual TextWriter GetMapFileWriter() { throw new NotImplementedException(); } /// /// Get access to a byte array that contain the contents of the given metadata /// file /// /// /// Name of the metadata file. Could be a path relative to the svcmap file location /// or the name of an item in a metadata storage. /// /// Content of the metadata file protected virtual byte[] ReadMetadataFile(string name) { throw new NotImplementedException(); } /// /// Write the metadata file. /// /// The metadata file to be written protected virtual void WriteMetadataFile(MetadataFile file) { throw new NotImplementedException(); } /// /// Get access to a byte array that contain the contents of the given extension /// file /// /// /// Name of the extension file. Could be a path relative to the svcmap file location /// or the name of an item in a metadata storage. /// /// Content of the extension file protected virtual byte[] ReadExtensionFile(string name) { throw new NotImplementedException(); } /// /// Write the extension file. /// /// The extension file to be written protected virtual void WriteExtensionFile(ExtensionFile file) { throw new NotImplementedException(); } #endregion protected abstract methods #region private methods private MapFile ReadMapFile(XmlReader reader) { try { return Wrap(GetMapFileSerializer().Deserialize(reader)); } catch (InvalidOperationException ex) { XmlException xmlException = ex.InnerException as XmlException; if (xmlException != null) { // the innerException contains detail error message throw xmlException; } XmlSchemaException schemaException = ex.InnerException as XmlSchemaException; if (schemaException != null) { if (schemaException.LineNumber > 0) { // append line/position to the message throw new XmlSchemaException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_AppendLinePosition, schemaException.Message, schemaException.LineNumber, schemaException.LinePosition), schemaException, schemaException.LineNumber, schemaException.LinePosition); } else { throw schemaException; } } // It's something we can't handle, throw it. throw; } } private void SaveExternalFiles(MapFile mapFile) { // KEEP the order! The name of metadata files could be adjusted when we save them. foreach (MetadataFile metadataFile in mapFile.MetadataList) { if (metadataFile.ErrorInLoading == null) { WriteMetadataFile(metadataFile); } } foreach (ExtensionFile extensionFile in mapFile.Extensions) { if (extensionFile.ErrorInLoading == null) { WriteExtensionFile(extensionFile); } } } private void LoadExternalFiles(MapFile mapFile) { // Do basic check for metadata files and extension files. ValidateMapFile(mapFile); foreach (MetadataFile metadataFile in mapFile.MetadataList) { metadataFile.IsExistingFile = true; LoadMetadataFile(metadataFile); } foreach (ExtensionFile extensionFile in mapFile.Extensions) { extensionFile.IsExistingFile = true; LoadExtensionFile(extensionFile); } } private void ValidateMapFile(MapFile mapFile) { var metadataFileNames = mapFile.MetadataList.Select(p => p.FileName).Where(p => !string.IsNullOrEmpty(p)); var extensionFileNames = mapFile.Extensions.Select(p => p.FileName).Where(p => !string.IsNullOrEmpty(p)); var fileNameSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string fileName in metadataFileNames.Concat(extensionFileNames)) { if (!fileNameSet.Contains(fileName)) { fileNameSet.Add(fileName); } else { throw new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_TwoExternalFilesWithSameName, fileName)); } } } private void SetMapFileLoadErrors(MapFile mapFile, IEnumerable proxyGenerationErrors) { mapFile.LoadErrors = proxyGenerationErrors; } #endregion private methods } }