public PnPSerializationScope(Type schemaTemplateType)
        {
            // Save the scope information
            this._baseSchemaNamespace    = schemaTemplateType.Namespace;
            this._baseSchemaAssemblyName = schemaTemplateType.Assembly.FullName;

            // Save the previous scope, if any
            this._previous = Current;

            // Set the new scope to this
            Current = this;
        }
        /// <summary>
        /// Serializes an in-memory ProvisioningTemplate into a Stream (the XML)
        /// </summary>
        /// <param name="template">The ProvisioningTemplate to serialize</param>
        /// <returns>The resulting Stream (the XML)</returns>
        public Stream ToFormattedTemplate(ProvisioningTemplate template)
        {
            if (template == null)
            {
                throw new ArgumentNullException(nameof(template));
            }

            using (var scope = new PnPSerializationScope(typeof(TSchemaTemplate)))
            {
                var    result = new TSchemaTemplate();
                Stream output = null;

                // Process the template to generate the output stream
                output = ProcessOutputStream(template, result);

                return(output);
            }
        }
        /// <summary>
        /// Deserializes a Stream of bytes (the XML) into a Provisioning Template, based on an optional identifier
        /// </summary>
        /// <param name="template">The source Stream of bytes (the XML)</param>
        /// <param name="identifier">An optional identifier for the template to deserialize</param>
        /// <returns>The deserialized Provisioning Template</returns>
        public ProvisioningTemplate ToProvisioningTemplate(Stream template, string identifier)
        {
            using (var scope = new PnPSerializationScope(typeof(TSchemaTemplate)))
            {
                // Prepare a variable to hold the resulting ProvisioningTemplate instance
                var result = new ProvisioningTemplate();

                // Prepare a variable to hold the single source formatted template
                // We provide the result instance of ProvisioningTemplate in order
                // to configure the tenant/hierarchy level items
                // We get back the XML-based object to use with the other serializers
                var source = ProcessInputStream(template, identifier, result);

                // We process the chain of deserialization
                // with the Provisioning-level serializers
                DeserializeTemplate(source, result);

                return(result);
            }
        }
        /// <summary>
        /// Deserializes a source Stream (the XML) into a ProvisioningHierarchy
        /// </summary>
        /// <param name="hierarchy">The source Stream (the XML)</param>
        /// <returns>The resulting ProvisioningHierarchy object</returns>
        public ProvisioningHierarchy ToProvisioningHierarchy(Stream hierarchy)
        {
            // Create a copy of the source stream
            MemoryStream sourceStream = new MemoryStream();

            hierarchy.Position = 0;
            hierarchy.CopyTo(sourceStream);
            sourceStream.Position = 0;

            // Check the provided template against the XML schema
            var validationResult = this.GetValidationResults(sourceStream);

            if (!validationResult.IsValid)
            {
                // TODO: Use resource file
                throw new ApplicationException("Template is not valid", new AggregateException(validationResult.Exceptions));
            }

            // Prepare the output variable
            ProvisioningHierarchy resultHierarchy = new ProvisioningHierarchy();

            // Determine if the file is a provisioning hierarchy
            sourceStream.Position = 0;
            XDocument xml = XDocument.Load(sourceStream);

            if (xml.Root.Name.LocalName != "Provisioning")
            {
                throw new ApplicationException("The provided provisioning file is not a Hierarchy!");
            }

            // Determine the specific formatter needed for the current provisioning file
            var innerFormatter = XMLPnPSchemaFormatter.GetSpecificFormatter(
                xml.Root.Name.NamespaceName);

            // Process all the provisioning templates included in the hierarchy, if any
            XmlNamespaceManager nsManager = new XmlNamespaceManager(new System.Xml.NameTable());

            nsManager.AddNamespace("pnp", xml.Root.Name.NamespaceName);

            // Start with templates embedded in the provisioning file
            var templates = xml.XPathSelectElements("/pnp:Provisioning/pnp:Templates/pnp:ProvisioningTemplate", nsManager).ToList();

            foreach (var template in templates)
            {
                // Save the single template into a MemoryStream
                MemoryStream templateStream = new MemoryStream();
                template.Save(templateStream);
                templateStream.Position = 0;

                // Process the single template with the classic technique
                var provisioningTemplate = innerFormatter.ToProvisioningTemplate(templateStream);

                // Add the generated template to the resulting hierarchy
                resultHierarchy.Templates.Add(provisioningTemplate);
            }

            // Then process any external file reference
            var templateFiles = xml.XPathSelectElements("/pnp:Provisioning/pnp:Templates/pnp:ProvisioningTemplateFile", nsManager).ToList();

            foreach (var template in templateFiles)
            {
                var templateID   = template.Attribute("ID")?.Value;
                var templateFile = template.Attribute("File")?.Value;
                if (!String.IsNullOrEmpty(templateFile) && !String.IsNullOrEmpty(templateID))
                {
                    // Process the single template file with the classic technique
                    var provisioningTemplate = this._provider.GetTemplate(templateFile);
                    provisioningTemplate.Id = templateID;

                    // Add the generated template to the resulting hierarchy
                    resultHierarchy.Templates.Add(provisioningTemplate);
                }
            }

            // And now process the top level children elements
            // using schema specific serializers

            using (var scope = new PnPSerializationScope(typeof(TSchemaTemplate)))
            {
                // We prepare a dummy template to leverage the existing serialization infrastructure
                var dummyTemplate = new ProvisioningTemplate
                {
                    Id = $"DUMMY-{Guid.NewGuid()}"
                };
                resultHierarchy.Templates.Add(dummyTemplate);

                // Deserialize the whole wrapper
                Object        wrapper       = null;
                var           wrapperType   = Type.GetType($"{PnPSerializationScope.Current?.BaseSchemaNamespace}.Provisioning, {PnPSerializationScope.Current?.BaseSchemaAssemblyName}", true);
                XmlSerializer xmlSerializer = new XmlSerializer(wrapperType);
                using (var reader = xml.Root.CreateReader())
                {
                    wrapper = xmlSerializer.Deserialize(reader);
                }

                #region Process Provisioning level serializers

                // Get all serializers to run in automated mode, ordered by DeserializationSequence
                var serializers = GetSerializersForCurrentContext(SerializerScope.Provisioning, a => a?.DeserializationSequence);

                // Invoke all the serializers
                InvokeSerializers(dummyTemplate, wrapper, serializers, SerializationAction.Deserialize);

                #endregion

                #region Process Tenant level serializers

                // Get all serializers to run in automated mode, ordered by DeserializationSequence
                serializers = GetSerializersForCurrentContext(SerializerScope.Tenant, a => a?.DeserializationSequence);

                // Invoke all the serializers
                InvokeSerializers(dummyTemplate, wrapper, serializers, SerializationAction.Deserialize);

                #endregion

                #region Process ProvisioningHierarchy level serializers

                // Get all serializers to run in automated mode, ordered by DeserializationSequence
                serializers = GetSerializersForCurrentContext(SerializerScope.ProvisioningHierarchy, a => a?.DeserializationSequence);

                // Invoke all the serializers
                InvokeSerializers(dummyTemplate, wrapper, serializers, SerializationAction.Deserialize);

                #endregion

                // Remove the dummy template from the hierarchy
                resultHierarchy.Templates.Remove(dummyTemplate);
            }

            return(resultHierarchy);
        }
        /// <summary>
        /// Serializes a ProvisioningHierarchy into a Stream (the XML)
        /// </summary>
        /// <param name="hierarchy">The ProvisioningHierarchy to serialize</param>
        /// <returns>The resulting Stream (the XML)</returns>
        public Stream ToFormattedHierarchy(ProvisioningHierarchy hierarchy)
        {
            if (hierarchy == null)
            {
                throw new ArgumentNullException(nameof(hierarchy));
            }

            using (var scope = new PnPSerializationScope(typeof(TSchemaTemplate)))
            {
                // We prepare a dummy template to leverage the existing deserialization infrastructure
                var dummyTemplate = new ProvisioningTemplate
                {
                    Id = $"DUMMY-{Guid.NewGuid()}"
                };
                hierarchy.Templates.Add(dummyTemplate);

                // Prepare the output wrapper
                Type   wrapperType;
                object wrapper, templatesItem;
                Array  templates;

                ProcessOutputHierarchy(dummyTemplate, out wrapperType, out wrapper, out templates, out templatesItem);

                // Handle the Sequences, if any
                // Get all ProvisioningHierarchy-level serializers to run in automated mode, ordered by SerializationSequence
                var serializers = GetSerializersForCurrentContext(SerializerScope.ProvisioningHierarchy, a => a?.SerializationSequence);

                // Invoke all the ProvisioningHierarchy-level serializers
                InvokeSerializers(dummyTemplate, wrapper, serializers, SerializationAction.Serialize);

                // Remove the dummy template
                hierarchy.Templates.Remove(dummyTemplate);

                // Add every single template to the output
                var provisioningTemplates = Array.CreateInstance(typeof(TSchemaTemplate), hierarchy.Templates.Count);
                for (int c = 0; c < hierarchy.Templates.Count; c++)
                {
                    // Prepare variable to hold the output template
                    var outputTemplate = new TSchemaTemplate();

                    // Serialize the real templates
                    SerializeTemplate(hierarchy.Templates[c], outputTemplate);

                    // Add the serialized template to the output
                    provisioningTemplates.SetValue(outputTemplate, c);
                }

                templatesItem.SetPublicInstancePropertyValue("ProvisioningTemplate", provisioningTemplates);

                templates.SetValue(templatesItem, 0);

                if (provisioningTemplates.Length > 0)
                {
                    wrapper.SetPublicInstancePropertyValue("Templates", templates);
                }

                // Serialize the XML-based object into a Stream
                XmlSerializerNamespaces ns =
                    new XmlSerializerNamespaces();
                ns.Add(((IXMLSchemaFormatter)this).NamespacePrefix,
                       ((IXMLSchemaFormatter)this).NamespaceUri);

                MemoryStream  output        = new MemoryStream();
                XmlSerializer xmlSerializer = new XmlSerializer(wrapperType);
                if (ns != null)
                {
                    xmlSerializer.Serialize(output, wrapper, ns);
                }
                else
                {
                    xmlSerializer.Serialize(output, wrapper);
                }

                // Re-base the Stream and return it
                output.Position = 0;
                return(output);
            }
        }