[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.Configuration / System / Configuration / MgmtConfigurationRecord.cs
blobf694686b8988e10f7079f698a90c2e51a2f86158
1 //------------------------------------------------------------------------------
2 // <copyright file="MgmtConfigurationRecord.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 namespace System.Configuration {
8 using System.CodeDom.Compiler;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Collections.Specialized;
12 using System.Configuration.Internal;
13 using System.Globalization;
14 using System.IO;
15 using System.Reflection;
16 using System.Security;
17 using System.Security.Permissions;
18 using System.Text;
19 using System.Xml;
20 using System.Runtime.Versioning;
22 internal sealed class MgmtConfigurationRecord : BaseConfigurationRecord {
23 private const int DEFAULT_INDENT = 4;
24 private const int MAX_INDENT = 10;
26 private Hashtable _sectionGroups; // ConfigurationSectionGroups that have been evaluated,
27 // which may or may not be defined in this web.config file.
28 // config key -> ConfigurationSectionGroup
30 private Hashtable _sectionFactories; // All inherited section declarations
31 // configKey -> FactoryId
33 private Hashtable _sectionGroupFactories; // All inherited section group declarations
34 // configKey -> FactoryId
36 private Hashtable _removedSections; // Sections that have been removed with ConfigurationSectionCollection.Remove()
37 // configKey -> configKey
39 private Hashtable _removedSectionGroups; // Section groups that have been removed with ConfigurationSectionCollection.Remove()
40 // configKey -> configKey
42 private Hashtable _locationTags; // List of all location tags encountered, even if empty
43 // locationSubPath -> locationSubPath
45 private HybridDictionary _streamInfoUpdates; // List of StreamInfo, including the main config file, the configSource this record uses, and
46 // new configSource stream added thru API
49 static internal MgmtConfigurationRecord Create(
50 IInternalConfigRoot configRoot,
51 IInternalConfigRecord parent,
52 string configPath,
53 string locationSubPath) {
55 MgmtConfigurationRecord configRecord = new MgmtConfigurationRecord();
56 configRecord.Init(configRoot, parent, configPath, locationSubPath);
57 return configRecord;
60 // don't allow instantiation except by Create
61 private MgmtConfigurationRecord() {
64 private void Init(
65 IInternalConfigRoot configRoot,
66 IInternalConfigRecord parent,
67 string configPath,
68 string locationSubPath) {
70 base.Init(configRoot, (BaseConfigurationRecord) parent, configPath, locationSubPath);
72 if ( IsLocationConfig &&
73 (MgmtParent._locationTags == null || !MgmtParent._locationTags.Contains(_locationSubPath))) {
75 // By instantiating a "new" LocationSubPath class, we have implicitly
76 // asked for one to be created
77 _flags[ForceLocationWritten] = true;
80 // Copy all stream information so that we can model changes to ConfigSource
81 InitStreamInfoUpdates();
84 private void InitStreamInfoUpdates() {
85 _streamInfoUpdates = new HybridDictionary(true);
86 if (ConfigStreamInfo.HasStreamInfos) {
87 foreach (StreamInfo streamInfo in ConfigStreamInfo.StreamInfos.Values) {
88 _streamInfoUpdates.Add(streamInfo.StreamName, streamInfo.Clone());
93 // The parent config record cast to this type
94 private MgmtConfigurationRecord MgmtParent {
95 get {
96 return(MgmtConfigurationRecord) _parent;
100 // The IInternalConfigHost cast to UpdateConfigHost.
101 private UpdateConfigHost UpdateConfigHost {
102 get {
103 return (UpdateConfigHost) Host;
107 // Class flags
108 static readonly SimpleBitVector32 MgmtClassFlags = new SimpleBitVector32(
109 ClassSupportsKeepInputs
110 | ClassIgnoreLocalErrors);
112 override protected SimpleBitVector32 ClassFlags {
113 get {
114 return MgmtClassFlags;
119 // Create the factory object that is used to create new instances of a ConfigurationSection.
120 // Our factory is a ConstructorInfo that creates the section.
122 override protected object CreateSectionFactory(FactoryRecord factoryRecord) {
124 // Get the type of the factory
125 Type type = TypeUtil.GetTypeWithReflectionPermission(Host, factoryRecord.FactoryTypeName, true);
128 // If the type is not a ConfigurationSection, use the DefaultSection if the type
129 // implements IConfigurationSectionHandler.
131 if (!typeof(ConfigurationSection).IsAssignableFrom(type)) {
132 TypeUtil.VerifyAssignableType(typeof(IConfigurationSectionHandler), type, true);
133 type = typeof(DefaultSection);
136 ConstructorInfo ctor = TypeUtil.GetConstructorWithReflectionPermission(type, typeof(ConfigurationSection), true);
138 return ctor;
142 // Create the ConfigurationSection.
144 override protected object CreateSection(bool inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, SectionInput sectionInput, object parentConfig, ConfigXmlReader reader) {
145 // Create an instance of the ConfigurationSection
146 ConstructorInfo ctor = (ConstructorInfo) factoryRecord.Factory;
147 ConfigurationSection configSection = (ConfigurationSection) TypeUtil.InvokeCtorWithReflectionPermission(ctor);
149 // Attach the ConfigurationSection to this record
150 configSection.SectionInformation.AttachToConfigurationRecord(this, factoryRecord, sectionRecord);
151 configSection.CallInit();
153 // Initialize the ConfigurationSection with XML or just its parent.
154 ConfigurationSection parentConfigSection = (ConfigurationSection) parentConfig;
155 configSection.Reset(parentConfigSection);
156 if (reader != null) {
157 configSection.DeserializeSection(reader);
160 if (sectionInput != null && sectionInput.ConfigBuilder != null) {
161 configSection = CallHostProcessConfigurationSection(configSection, sectionInput.ConfigBuilder);
164 // Clear the modified bit.
165 configSection.ResetModified();
167 return configSection;
171 // Create the type used to create new instances of a ConfigurationSectionGroup.
172 // Our factory is a ConstructorInfo that creates the section group.
174 private ConstructorInfo CreateSectionGroupFactory(FactoryRecord factoryRecord) {
175 Type type;
177 if (String.IsNullOrEmpty(factoryRecord.FactoryTypeName)) {
178 type = typeof(ConfigurationSectionGroup);
180 else {
181 type = TypeUtil.GetTypeWithReflectionPermission(Host, factoryRecord.FactoryTypeName, true);
184 ConstructorInfo ctor = TypeUtil.GetConstructorWithReflectionPermission(type, typeof(ConfigurationSectionGroup), true);
186 return ctor;
190 // Ensure the existence of a section group factory, and return it.
192 private ConstructorInfo EnsureSectionGroupFactory(FactoryRecord factoryRecord) {
193 ConstructorInfo factory = (ConstructorInfo) factoryRecord.Factory;
194 if (factory == null) {
195 factory = CreateSectionGroupFactory(factoryRecord);
196 factoryRecord.Factory = factory;
199 return factory;
204 // Create a new ConfigurationSection with the same values as the parent.
205 // We must use a different instance than the parent, as the parent is cached
206 // by the config system and the child ConfigurationSection may change due to
207 // user interaction.
209 override protected object UseParentResult(string configKey, object parentResult, SectionRecord sectionRecord) {
210 FactoryRecord factoryRecord = FindFactoryRecord(configKey, false);
211 if (factoryRecord == null) {
212 throw new ConfigurationErrorsException(SR.GetString(SR.Config_unrecognized_configuration_section, configKey));
215 object result = CallCreateSection(false, factoryRecord, sectionRecord, null, parentResult, null);
216 return result;
220 // There is no runtime object at designtime - always return the result.
222 override protected object GetRuntimeObject(object result) {
223 return result;
227 // Return the section result cast to a ConfigurationSection,
228 // or null if the section does not exist or has not been evaluated.
230 private ConfigurationSection GetConfigSection(string configKey) {
231 SectionRecord sectionRecord = GetSectionRecord(configKey, false);
232 if (sectionRecord != null && sectionRecord.HasResult) {
233 return (ConfigurationSection) sectionRecord.Result;
235 else {
236 return null;
241 // Return the collection of ConfigurationSectionGroups.
243 private Hashtable SectionGroups {
244 get {
245 if (_sectionGroups == null) {
246 _sectionGroups = new Hashtable();
249 return _sectionGroups;
254 // Return the collection of removed sections.
256 private Hashtable RemovedSections {
257 get {
258 if (_removedSections == null) {
259 _removedSections = new Hashtable();
262 return _removedSections;
267 // Return the collection of removed section groups.
269 private Hashtable RemovedSectionGroups {
270 get {
271 if (_removedSectionGroups == null) {
272 _removedSectionGroups = new Hashtable();
275 return _removedSectionGroups;
280 // Lookup a section group. Return null if it doesn't exist or hasn't been evaluated.
282 internal ConfigurationSectionGroup LookupSectionGroup(string configKey) {
283 ConfigurationSectionGroup configSectionGroup = null;
284 if (_sectionGroups != null) {
285 configSectionGroup = (ConfigurationSectionGroup) _sectionGroups[configKey];
288 return configSectionGroup;
291 // Returns the ConfigurationSectionGroup of the configKey.
292 // The ConfigurationSectionGroup is created if it doesn't exist.
293 // This method only returns null if a FactoryRecord does not exist for the
294 // desired configKey.
295 internal ConfigurationSectionGroup GetSectionGroup(string configKey) {
296 ConfigurationSectionGroup configSectionGroup = LookupSectionGroup(configKey);
297 if (configSectionGroup == null) {
298 BaseConfigurationRecord configRecord;
299 FactoryRecord factoryRecord = FindFactoryRecord(configKey, false, out configRecord);
300 if (factoryRecord == null) {
301 return null;
304 if (!factoryRecord.IsGroup) {
305 throw ExceptionUtil.ParameterInvalid("sectionGroupName");
308 if (factoryRecord.FactoryTypeName == null) {
310 // If no type is defined for the section group, return a base ConfigurationSectionGroup.
311 // For example:
312 // <configSections>
313 // <sectionGroup name="mySectionGroup" />
314 // </configSections>
316 configSectionGroup = new ConfigurationSectionGroup();
318 else {
320 // Create the section group of the desired type.
321 // For example:
322 // <configSections>
323 // <sectionGroup name="mySectionGroup" type="MySectionGroupType, acme" />
324 // </configSections>
326 ConstructorInfo ctor = EnsureSectionGroupFactory(factoryRecord);
328 try {
329 configSectionGroup = (ConfigurationSectionGroup) TypeUtil.InvokeCtorWithReflectionPermission(ctor);
331 catch (Exception e) {
332 throw new ConfigurationErrorsException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey),
333 e, factoryRecord);
337 configSectionGroup.AttachToConfigurationRecord(this, factoryRecord);
339 // Add it to the collection
340 SectionGroups[configKey] = configSectionGroup;
343 return configSectionGroup;
347 // Create a collection of all location tags encountered in the file.
349 internal ConfigurationLocationCollection GetLocationCollection(Configuration config) {
350 ArrayList locations = new ArrayList();
352 // Now add the other empty location sections we recorded
353 if (_locationTags != null) {
354 foreach (string subPath in _locationTags.Values) {
355 locations.Add(new ConfigurationLocation(config, subPath));
359 return new ConfigurationLocationCollection(locations);
363 // AddLocation
365 // Record all location tags in the config file, even if they are empty.
367 protected override void AddLocation(string locationSubPath) {
368 if (_locationTags == null) {
369 _locationTags = new Hashtable(StringComparer.OrdinalIgnoreCase);
372 _locationTags[locationSubPath] = locationSubPath;
376 // Collection of all section factories, both in this file and inherited.
378 internal Hashtable SectionFactories {
379 get {
380 if (_sectionFactories == null) {
381 _sectionFactories = GetAllFactories(false);
384 return _sectionFactories;
389 // Collection of all section groups, both in this file and inherited.
391 internal Hashtable SectionGroupFactories {
392 get {
393 if (_sectionGroupFactories == null) {
394 _sectionGroupFactories = GetAllFactories(true);
397 return _sectionGroupFactories;
402 // Get all the factories available, both in this file and inherited.
404 private Hashtable GetAllFactories(bool isGroup) {
405 Hashtable factories = new Hashtable();
407 MgmtConfigurationRecord configRecord = this;
408 do {
409 if (configRecord._factoryRecords != null) {
410 foreach (FactoryRecord factoryRecord in configRecord._factoryRecords.Values) {
411 if (factoryRecord.IsGroup == isGroup) {
412 string configKey = factoryRecord.ConfigKey;
413 factories[configKey] = new FactoryId(factoryRecord.ConfigKey, factoryRecord.Group, factoryRecord.Name);
418 configRecord = configRecord.MgmtParent;
419 } while (!configRecord.IsRootConfig);
421 return factories;
424 internal ConfigurationSection FindImmediateParentSection(ConfigurationSection section) {
425 ConfigurationSection result = null;
427 string configKey = section.SectionInformation.SectionName;
428 SectionRecord sectionRecord = GetSectionRecord(configKey, false);
429 if (sectionRecord.HasLocationInputs) {
430 SectionInput input = sectionRecord.LastLocationInput;
431 Debug.Assert(input.HasResult, "input.HasResult");
432 result = (ConfigurationSection) input.Result;
434 else if (sectionRecord.HasIndirectLocationInputs) {
435 Debug.Assert(IsLocationConfig, "Indirect location inputs exist only in location config record");
436 SectionInput input = sectionRecord.LastIndirectLocationInput;
437 Debug.Assert(input != null);
438 Debug.Assert(input.HasResult, "input.HasResult");
439 result = (ConfigurationSection) input.Result;
441 else if (IsRootDeclaration(configKey, true)) {
442 FactoryRecord factoryRecord = GetFactoryRecord(configKey, false);
444 object resultObject;
445 object resultRuntimeObject;
446 CreateSectionDefault(configKey, false, factoryRecord, null, out resultObject, out resultRuntimeObject);
447 result = (ConfigurationSection) resultObject;
449 else {
450 MgmtConfigurationRecord current = this.MgmtParent;
451 while (!current.IsRootConfig) {
452 sectionRecord = current.GetSectionRecord(configKey, false);
453 if (sectionRecord != null && sectionRecord.HasResult) {
454 result = (ConfigurationSection) sectionRecord.Result;
455 break;
458 current = current.MgmtParent;
461 Debug.Assert(!current.IsRootConfig, "An immediate parent result should have been found");
464 if (!result.IsReadOnly()) {
465 result.SetReadOnly();
468 return result;
472 // Get the immediate parent configuration section, and clone it.
474 internal ConfigurationSection FindAndCloneImmediateParentSection(ConfigurationSection configSection) {
475 string configKey = configSection.SectionInformation.ConfigKey;
476 ConfigurationSection parentSection = FindImmediateParentSection(configSection);
477 SectionRecord sectionRecord = GetSectionRecord(configKey, false);
478 ConfigurationSection clone = (ConfigurationSection) UseParentResult(configKey, parentSection, sectionRecord);
479 return clone;
483 // Revert the ConfigurationSection to the value of its parent.
485 internal void RevertToParent(ConfigurationSection configSection) {
487 // Remove any RawXml set by ConfigurationSection.SetRawXml
488 configSection.SectionInformation.RawXml = null;
490 try {
491 // Reset to parent value
492 ConfigurationSection parentConfigSection = FindImmediateParentSection(configSection);
493 configSection.Reset(parentConfigSection);
495 // Consider it to be unmodified
496 configSection.ResetModified();
498 catch (Exception e) {
499 throw new ConfigurationErrorsException(SR.GetString(SR.Config_exception_in_config_section_handler, configSection.SectionInformation.SectionName),
500 e, ConfigStreamInfo.StreamName, 0);
503 // Record that the section is to be removed.
504 configSection.SectionInformation.Removed = true;
508 // Return the outer XML of a section as a string.
509 // Return null if the section does not exist in the file.
511 internal string GetRawXml(string configKey) {
512 // Get the section record created during Init
513 SectionRecord sectionRecord = GetSectionRecord(configKey, false);
514 if (sectionRecord == null || !sectionRecord.HasFileInput) {
515 return null;
518 // The section exists, so find and return its RawXml.
519 string [] keys = configKey.Split(ConfigPathSeparatorParams);
520 ConfigXmlReader reader = GetSectionXmlReader(keys, sectionRecord.FileInput);
522 return reader.RawXml;
526 // Update the section with the XML provided.
528 // This method will throw out any changes made to the section up to this point.
530 // If xmlElement is null or empty, it is equivalent to calling RevertToParent
532 internal void SetRawXml(ConfigurationSection configSection, string xmlElement) {
534 // Null or empty is equivalent to RevertToParent().
535 if (string.IsNullOrEmpty(xmlElement)) {
536 RevertToParent(configSection);
537 return;
540 ValidateSectionXml(xmlElement, configSection.SectionInformation.Name);
542 // Reset the ConfigurationSection with the XML.
543 ConfigurationSection parentConfigSection = FindImmediateParentSection(configSection);
544 ConfigXmlReader reader = new ConfigXmlReader(xmlElement, null, 0);
546 // Store the raw XML.
547 configSection.SectionInformation.RawXml = xmlElement;
549 // Update the section with the xml
550 try {
551 try {
552 bool wasPresent = configSection.ElementPresent;
553 PropertySourceInfo saveInfo = configSection.ElementInformation.PropertyInfoInternal();
555 configSection.Reset(parentConfigSection);
556 configSection.DeserializeSection(reader);
557 configSection.ResetModified();
559 configSection.ElementPresent = wasPresent;
560 configSection.ElementInformation.ChangeSourceAndLineNumber(saveInfo);
562 catch {
563 configSection.SectionInformation.RawXml = null;
564 throw;
567 catch (Exception e) {
568 throw new ConfigurationErrorsException(SR.GetString(SR.Config_exception_in_config_section_handler, configSection.SectionInformation.SectionName),
569 e, null, 0);
572 // Ignore previous attempts to remove the section.
573 configSection.SectionInformation.Removed = false;
577 // Return true if a stream is being used by a configSource directive in other input.
579 private bool IsStreamUsed(string oldStreamName) {
580 MgmtConfigurationRecord current = this;
581 if (IsLocationConfig) {
583 // For a location configuration, the input we need to check
584 // are the section records and location sections in the file,
585 // which are available in the parent record.
587 current = MgmtParent;
590 // Check whether a file section is using the configsource directive.
592 if (current._sectionRecords != null) {
593 foreach (SectionRecord sectionRecord in current._sectionRecords.Values) {
594 if ( sectionRecord.HasFileInput &&
595 StringUtil.EqualsIgnoreCase(sectionRecord.FileInput.SectionXmlInfo.ConfigSourceStreamName, oldStreamName)) {
596 return true;
603 // Check whether a location is using the configsource directive.
605 if (current._locationSections != null) {
606 foreach (LocationSectionRecord locationSectionRecord in current._locationSections) {
607 if (StringUtil.EqualsIgnoreCase(locationSectionRecord.SectionXmlInfo.ConfigSourceStreamName, oldStreamName)) {
608 return true;
613 return false;
617 // Set the configSource attribute on a ConfigurationSection
619 internal void ChangeConfigSource(
620 SectionInformation sectionInformation,
621 string oldConfigSource,
622 string oldConfigSourceStreamName,
623 string newConfigSource) {
625 if (String.IsNullOrEmpty(oldConfigSource)) {
626 oldConfigSource = null;
629 if (String.IsNullOrEmpty(newConfigSource)) {
630 newConfigSource = null;
633 // Check if there is a change to config source
634 if (StringUtil.EqualsIgnoreCase(oldConfigSource, newConfigSource))
635 return;
637 if (String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
638 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_requires_file));
641 string newConfigSourceStreamName = null;
642 if (newConfigSource != null) {
643 newConfigSourceStreamName = Host.GetStreamNameForConfigSource(ConfigStreamInfo.StreamName, newConfigSource);
646 // Add the stream to the updates
647 if (newConfigSourceStreamName != null) {
649 // Ensure that no parent is using the same config source stream
651 ValidateUniqueChildConfigSource(sectionInformation.ConfigKey, newConfigSourceStreamName, newConfigSource, null);
653 StreamInfo streamInfo = (StreamInfo) _streamInfoUpdates[newConfigSourceStreamName];
654 if (streamInfo != null) {
656 // Detect if another section in this file is using the same configSource
657 // with has a different section name.
659 if (streamInfo.SectionName != sectionInformation.ConfigKey) {
660 throw new ConfigurationErrorsException(
661 SR.GetString(SR.Config_source_cannot_be_shared, newConfigSource));
664 else {
666 // Add stream to updates
668 streamInfo = new StreamInfo(sectionInformation.ConfigKey, newConfigSource, newConfigSourceStreamName);
669 _streamInfoUpdates.Add(newConfigSourceStreamName, streamInfo);
673 // remove old streamname if no longer referenced
674 if (oldConfigSourceStreamName != null && !IsStreamUsed(oldConfigSourceStreamName)) {
675 _streamInfoUpdates.Remove(oldConfigSourceStreamName);
678 // update the configSourceStreamName
679 sectionInformation.ConfigSourceStreamName = newConfigSourceStreamName;
683 // Verify that the string is valid xml, begins with the expected section name,
684 // and contains no more or less than a single element.
686 // Throws a ConfigurationErrorsException if there is an error.
688 private void ValidateSectionXml(string xmlElement, string configKey) {
689 if (string.IsNullOrEmpty(xmlElement))
690 return;
692 XmlTextReader reader = null;
693 try {
694 XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.Default, Encoding.Unicode);
695 reader = new XmlTextReader(xmlElement, XmlNodeType.Element, context);
697 // Verify that the it is an element
698 reader.Read();
699 if (reader.NodeType != XmlNodeType.Element) {
700 throw new ConfigurationErrorsException(SR.GetString(SR.Config_unexpected_node_type, reader.NodeType));
703 // Verify the name of the element is a section
704 string group, name;
705 SplitConfigKey(configKey, out group, out name);
706 if (reader.Name != name) {
707 throw new ConfigurationErrorsException(SR.GetString(SR.Config_unexpected_element_name, reader.Name));
710 for (;;) {
711 if (!reader.Read()) {
712 // ensure there is a matching end element
713 if (reader.Depth != 0) {
714 throw new ConfigurationErrorsException(SR.GetString(SR.Config_unexpected_element_end),reader);
717 break;
720 switch (reader.NodeType) {
721 // disallowed node types within a section
722 case XmlNodeType.XmlDeclaration:
723 case XmlNodeType.DocumentType:
724 throw new ConfigurationErrorsException(SR.GetString(SR.Config_invalid_node_type),reader);
726 default:
727 break;
731 // don't allow XML after the end element
732 if (reader.Depth <= 0 && reader.NodeType != XmlNodeType.EndElement) {
733 throw new ConfigurationErrorsException(SR.GetString(SR.Config_more_data_than_expected),reader);
737 finally {
738 if (reader != null) {
739 reader.Close();
745 // Add a new configuration section to this config file.
746 // This adds both the section declaration and definition to the config file.
748 // Called from ConfigurationSectionCollection.Add().
749 // Note this method DOES NOT update the associated ConfigurationSectionCollection.
751 internal void AddConfigurationSection(string group, string name, ConfigurationSection configSection) {
753 // <configSections> is not permitted within a <location> tag.
754 if (IsLocationConfig) {
755 throw new InvalidOperationException(SR.GetString(SR.Config_add_configurationsection_in_location_config));
758 VerifySectionName(name, null, false);
760 if (configSection == null) {
761 throw new ArgumentNullException("configSection");
764 // Ensure the section is not already part of the configuration hierarchy.
765 if (configSection.SectionInformation.Attached) {
766 throw new InvalidOperationException(SR.GetString(SR.Config_add_configurationsection_already_added));
769 string configKey = BaseConfigurationRecord.CombineConfigKey(group, name);
771 // Ensure the section is not already declared.
772 FactoryRecord factoryRecord = FindFactoryRecord(configKey, true);
773 if (factoryRecord != null) {
774 throw new ArgumentException(SR.GetString(SR.Config_add_configurationsection_already_exists));
777 // Add the configSource if needed.
778 if (!String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource)) {
779 ChangeConfigSource(configSection.SectionInformation, null, null, configSection.SectionInformation.ConfigSource);
782 // Add to list of all sections.
783 if (_sectionFactories != null) {
784 _sectionFactories.Add(configKey, new FactoryId(configKey, group, name));
787 // Get the type name.
788 string typeName = configSection.SectionInformation.Type;
789 if (typeName == null) {
790 typeName = Host.GetConfigTypeName(configSection.GetType());
793 // Add a factory record for the section.
794 factoryRecord = new FactoryRecord(configKey,
795 group,
796 name,
797 typeName,
798 configSection.SectionInformation.AllowLocation,
799 configSection.SectionInformation.AllowDefinition,
800 configSection.SectionInformation.AllowExeDefinition,
801 configSection.SectionInformation.OverrideModeDefaultSetting,
802 configSection.SectionInformation.RestartOnExternalChanges,
803 configSection.SectionInformation.RequirePermission,
804 _flags[IsTrusted],
805 false, // isUndeclared
806 ConfigStreamInfo.StreamName,
807 -1);
809 // Construct a factory for the section
810 factoryRecord.Factory = TypeUtil.GetConstructorWithReflectionPermission(
811 configSection.GetType(), typeof(ConfigurationSection), true);
813 factoryRecord.IsFactoryTrustedWithoutAptca = TypeUtil.IsTypeFromTrustedAssemblyWithoutAptca(configSection.GetType());
815 EnsureFactories()[configKey] = factoryRecord;
817 // Add a section record for the section.
818 // Since we are adding a new definition, it cannot be locked.
819 SectionRecord sectionRecord = EnsureSectionRecordUnsafe(configKey, false);
820 sectionRecord.Result = configSection;
821 sectionRecord.ResultRuntimeObject = configSection;
823 // Undo any previous removals of the section.
824 if (_removedSections != null) {
825 _removedSections.Remove(configKey);
828 // Attach the section to the configuration record.
829 configSection.SectionInformation.AttachToConfigurationRecord(this, factoryRecord, sectionRecord);
832 // If there is rawXml, set it now. Note this will override any other changes to the section
833 // definition made after the call to SetXml.
835 string rawXml = configSection.SectionInformation.RawXml;
836 if (!String.IsNullOrEmpty(rawXml)) {
837 configSection.SectionInformation.RawXml = null;
838 configSection.SectionInformation.SetRawXml(rawXml);
843 // Remove a configuration section from this config file.
844 // This removes both the section declaration and definition from the config file.
845 // Note, however, that if a parent config file declares the section,
846 // a new instance of the section can be retrieved having the value of the
847 // immediate parent.
849 // Called from ConfigurationSectionCollection.Remove().
850 // Note this method DOES NOT update the associated ConfigurationSectionCollection.
852 internal void RemoveConfigurationSection(string group, string name) {
853 bool sectionIsUsed = false; // Is section used in our record
855 VerifySectionName(name, null, true);
857 string configKey = BaseConfigurationRecord.CombineConfigKey(group, name);
859 // If it's already removed, don't try to remove it again.
860 if (RemovedSections.Contains(configKey)) {
861 return;
864 // If it's not a registered section, there's nothing to do.
865 if (FindFactoryRecord(configKey, true) == null) {
866 return;
869 // Detach from this record
870 ConfigurationSection configSection = GetConfigSection(configKey);
871 if (configSection != null) {
872 configSection.SectionInformation.DetachFromConfigurationRecord();
875 // Remove from list of all sections if this is the root declaration.
876 bool isRootDeclaration = IsRootDeclaration(configKey, false);
877 if (_sectionFactories != null && isRootDeclaration) {
878 _sectionFactories.Remove(configKey);
881 // Remove from collection of factory records.
882 if (!IsLocationConfig && _factoryRecords != null && _factoryRecords.Contains(configKey)) {
883 sectionIsUsed = true;
884 _factoryRecords.Remove(configKey);
887 // Remove from collection of section records.
888 if (_sectionRecords != null && _sectionRecords.Contains(configKey)) {
889 sectionIsUsed = true;
890 _sectionRecords.Remove(configKey);
893 // Remove all location section records for this section in this file.
894 if (_locationSections != null) {
895 int i = 0;
896 while (i < _locationSections.Count) {
897 LocationSectionRecord locationSectionRecord = (LocationSectionRecord) _locationSections[i];
898 if (locationSectionRecord.ConfigKey != configKey) {
899 i++;
901 else {
902 sectionIsUsed = true;
903 _locationSections.RemoveAt(i);
908 if (sectionIsUsed) {
909 // Add to RemovedSections since we need to remove
910 // it from the file later.
911 RemovedSections.Add(configKey, configKey);
915 // Remove all references from configSource
916 // Note that we can't remove an item while enumerating it.
918 List<string> streamsToRemove = new List<string>();
919 foreach (StreamInfo streamInfo in _streamInfoUpdates.Values) {
920 if (streamInfo.SectionName == configKey) {
921 streamsToRemove.Add(streamInfo.StreamName);
925 foreach (string stream in streamsToRemove) {
926 _streamInfoUpdates.Remove(stream);
931 // Add a new configuration section group to this config file.
933 // Called from ConfigurationSectionGroupCollection.Add().
934 // Note this method DOES NOT update the associated ConfigurationSectionGroupCollection.
936 internal void AddConfigurationSectionGroup(string group, string name, ConfigurationSectionGroup configSectionGroup) {
937 // <location> tags can't have a <configSections> declaration.
938 if (IsLocationConfig) {
939 throw new InvalidOperationException(SR.GetString(SR.Config_add_configurationsectiongroup_in_location_config));
942 // Validate name argument.
943 VerifySectionName(name, null, false);
945 // Validate configSectionGroup argument.
946 if (configSectionGroup == null) {
947 throw ExceptionUtil.ParameterInvalid("name");
950 // A section group can only belong to one section group collection.
951 if (configSectionGroup.Attached) {
952 throw new InvalidOperationException(SR.GetString(SR.Config_add_configurationsectiongroup_already_added));
955 string configKey = BaseConfigurationRecord.CombineConfigKey(group, name);
957 // Do not add if the section group already exists, even if it is of a different type.
958 FactoryRecord factoryRecord = FindFactoryRecord(configKey, true);
959 if (factoryRecord != null) {
960 throw new ArgumentException(SR.GetString(SR.Config_add_configurationsectiongroup_already_exists));
963 // Add to list of all section groups.
964 if (_sectionGroupFactories != null) {
965 _sectionGroupFactories.Add(configKey, new FactoryId(configKey, group, name));
968 // Get the type name - if it is not specified explicitly, get it from the type of the object.
969 string typeName = configSectionGroup.Type;
970 if (typeName == null) {
971 typeName = Host.GetConfigTypeName(configSectionGroup.GetType());
974 // Create a factory record and add it to the collection of factory records.
975 factoryRecord = new FactoryRecord(configKey, group, name, typeName, ConfigStreamInfo.StreamName, -1);
976 EnsureFactories()[configKey] = factoryRecord;
978 // Add it to list of evaluated configuration section groups.
979 SectionGroups[configKey] = configSectionGroup;
981 // Remove it from RemovedSectionGroups if it was previously removed.
982 if (_removedSectionGroups != null) {
983 _removedSectionGroups.Remove(configKey);
986 // Attach to the configuration record.
987 configSectionGroup.AttachToConfigurationRecord(this, factoryRecord);
991 // Return a list of all FactoryRecords of sections that are descendents of
992 // a section group.
994 private ArrayList GetDescendentSectionFactories(string configKey) {
995 ArrayList sectionGroups = new ArrayList();
997 string configKeyAncestor;
998 if (configKey.Length == 0) {
999 configKeyAncestor = string.Empty;
1001 else {
1002 configKeyAncestor = configKey + "/";
1005 foreach (FactoryId factoryId in SectionFactories.Values) {
1006 if (factoryId.Group == configKey || StringUtil.StartsWith(factoryId.Group, configKeyAncestor)) {
1007 sectionGroups.Add(factoryId);
1011 return sectionGroups;
1015 // Return a list of all FactoryRecords of section groups that are descendents of
1016 // a section group, including the section group itself.
1018 private ArrayList GetDescendentSectionGroupFactories(string configKey) {
1020 ArrayList sectionGroups = new ArrayList();
1022 string configKeyAncestor;
1023 if (configKey.Length == 0) {
1024 configKeyAncestor = string.Empty;
1026 else {
1027 configKeyAncestor = configKey + "/";
1030 foreach (FactoryId factoryId in SectionGroupFactories.Values) {
1031 if (factoryId.ConfigKey == configKey || StringUtil.StartsWith(factoryId.ConfigKey, configKeyAncestor)) {
1032 sectionGroups.Add(factoryId);
1036 return sectionGroups;
1040 // Remove a configuration section group from this config file.
1041 // This removes both the section group declaration and definition from the config file,
1042 // along with all descendent groups and sections.
1044 // Note, however, that if a parent config file declares the section group,
1045 // a new instance of the section can be retrieved having the value of the
1046 // immediate parent.
1048 // Called from ConfigurationSectionGroupCollection.Remove().
1049 // Note this method DOES NOT update the associated ConfigurationSectionCollection.
1051 internal void RemoveConfigurationSectionGroup(string group, string name) {
1052 // Validate arguments
1053 VerifySectionName(name, null, false);
1055 string configKey = BaseConfigurationRecord.CombineConfigKey(group, name);
1057 // If it's not a registered section, there's nothing to do.
1058 if (FindFactoryRecord(configKey, true) == null) {
1059 return;
1062 // Remove all descendent sections.
1063 ArrayList sections = GetDescendentSectionFactories(configKey);
1064 foreach (FactoryId descendent in sections) {
1065 RemoveConfigurationSection(descendent.Group, descendent.Name);
1068 // Remove all descendent sections groups, including the configKey group.
1069 ArrayList sectionGroups = GetDescendentSectionGroupFactories(configKey);
1070 foreach (FactoryId descendent in sectionGroups) {
1072 // If it's already removed, don't try to remove it again.
1073 // We don't do this test above the loop for configKey, because
1074 // the section groups contained within the section group may
1075 // be changed by the user once added.
1077 if (RemovedSectionGroups.Contains(descendent.ConfigKey)) {
1078 continue;
1081 // If the section group has been evaluated, detatch it.
1082 ConfigurationSectionGroup sectionGroup = LookupSectionGroup(descendent.ConfigKey);
1083 if (sectionGroup != null) {
1084 sectionGroup.DetachFromConfigurationRecord();
1087 // Remove from list of all section group factories if this is the root declaration.
1088 bool isRootDeclaration = IsRootDeclaration(descendent.ConfigKey, false);
1089 if (_sectionGroupFactories != null && isRootDeclaration) {
1090 _sectionGroupFactories.Remove(descendent.ConfigKey);
1093 // Remove from list of factory records.
1094 if (!IsLocationConfig && _factoryRecords != null) {
1095 _factoryRecords.Remove(descendent.ConfigKey);
1098 // Remove from evaluated section groups.
1099 if (_sectionGroups != null) {
1100 _sectionGroups.Remove(descendent.ConfigKey);
1104 // Add to list of section groups that are removed
1105 // Note that this will add section groups that might not be used
1106 // in this config file. That just results in some extra work during
1107 // save, it is not harmful.
1109 RemovedSectionGroups.Add(descendent.ConfigKey, descendent.ConfigKey);
1114 // Return the file path to this configuration file.
1116 internal string ConfigurationFilePath {
1117 get {
1118 string filepath = UpdateConfigHost.GetNewStreamname(ConfigStreamInfo.StreamName);
1119 if (filepath == null) {
1120 filepath = String.Empty;
1123 if (!String.IsNullOrEmpty(filepath)) {
1124 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, filepath).Demand();
1127 return filepath;
1133 // Update the config file with the changes in each ConfigurationSection
1135 internal void SaveAs(string filename, ConfigurationSaveMode saveMode, bool forceUpdateAll) {
1137 // Get the updates.
1138 SectionUpdates declarationUpdates = GetConfigDeclarationUpdates(saveMode, forceUpdateAll);
1140 ConfigDefinitionUpdates definitionUpdates;
1141 ArrayList configSourceUpdates;
1142 bool checkedConfigForUpdates = false;
1143 bool requireUpdates = (filename != null);
1144 GetConfigDefinitionUpdates(requireUpdates, saveMode, forceUpdateAll, out definitionUpdates, out configSourceUpdates);
1146 if (filename != null) {
1147 Debug.Assert(filename.Length > 0, "The caller should make sure that filename is not empty");
1150 // Verify that the filename is not being used.
1152 // Note that if we are using a remote host, all the streamName's in _streamInfoUpdates
1153 // are actually fullpaths on the remote machine. In this case there is no way to
1154 // detect if we have a conflict or not.
1155 if (!Host.IsRemote && _streamInfoUpdates.Contains(filename)) {
1156 throw new ArgumentException(SR.GetString(SR.Filename_in_SaveAs_is_used_already, filename));
1160 // If there was no config file for this config record,
1161 // record the new stream name and version.
1163 if (String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
1164 StreamInfo streamInfo = new StreamInfo(null, null, filename);
1165 _streamInfoUpdates.Add(filename, streamInfo);
1167 ConfigStreamInfo.StreamName = filename;
1168 ConfigStreamInfo.StreamVersion = MonitorStream(null, null, ConfigStreamInfo.StreamName);
1172 // Update the host to redirect filenames
1174 UpdateConfigHost.AddStreamname(ConfigStreamInfo.StreamName, filename, Host.IsRemote);
1176 // Redirect also all configSource filenames
1177 foreach (StreamInfo streamInfo in _streamInfoUpdates.Values) {
1178 if (!String.IsNullOrEmpty(streamInfo.SectionName)) {
1179 // Get the new configSource streamName based on the new filename path
1180 string newStreamName = InternalConfigHost.StaticGetStreamNameForConfigSource(
1181 filename, streamInfo.ConfigSource);
1183 // Ask UpdateConfigHost to intercept them.
1184 UpdateConfigHost.AddStreamname(streamInfo.StreamName, newStreamName, Host.IsRemote);
1190 if (!requireUpdates) {
1191 // Check if there are any updates needed for the
1192 // configuration record itself.
1193 requireUpdates = RecordItselfRequiresUpdates;
1196 if (declarationUpdates != null || definitionUpdates != null || requireUpdates) {
1197 // Copy the input stream before opening the output stream.
1198 byte[] readBuffer = null;
1199 Encoding encoding = null;
1200 if (ConfigStreamInfo.HasStream) {
1201 using (Stream streamRead = Host.OpenStreamForRead(ConfigStreamInfo.StreamName)) {
1202 if (streamRead == null) {
1203 throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), ConfigStreamInfo.StreamName, 0);
1206 readBuffer = new byte[streamRead.Length];
1207 int count = streamRead.Read(readBuffer, 0, (int) streamRead.Length);
1208 if (count != streamRead.Length) {
1209 throw new ConfigurationErrorsException(SR.GetString(SR.Config_data_read_count_mismatch));
1212 // Read the first byte so that we can determine the encoding.
1213 try {
1214 using (StreamReader reader = new StreamReader(ConfigStreamInfo.StreamName)) {
1215 if (reader.Peek() >= 0) {
1216 reader.Read();
1219 // Dev10 bug 687017 - Handle only UTF-16 explicitly, so that handling of other
1220 // encodings are not affected.
1221 if (reader.CurrentEncoding is UnicodeEncoding) {
1222 encoding = reader.CurrentEncoding;
1226 catch {
1227 // Ignore any errors, encoding will remain null.
1231 string changedStreamName = FindChangedConfigurationStream();
1232 if (changedStreamName != null) {
1233 throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), changedStreamName, 0);
1236 checkedConfigForUpdates = true;
1238 // Write the changes to the output stream.
1239 object writeContext = null;
1240 bool streamOpened = false;
1241 try {
1242 try {
1243 using (Stream streamWrite = Host.OpenStreamForWrite(ConfigStreamInfo.StreamName, null, ref writeContext)) {
1244 streamOpened = true;
1245 // Use the default StreamWriter constructor if encoding is null,
1246 // otherwise specify the encoding.
1247 using (StreamWriter streamWriter = encoding == null ? new StreamWriter(streamWrite) : new StreamWriter(streamWrite, encoding)) {
1248 XmlUtilWriter utilWriter = new XmlUtilWriter(streamWriter, true);
1249 if (ConfigStreamInfo.HasStream) {
1250 CopyConfig(declarationUpdates, definitionUpdates, readBuffer,
1251 ConfigStreamInfo.StreamName, NamespaceChangeNeeded, utilWriter);
1253 else {
1254 CreateNewConfig(declarationUpdates, definitionUpdates, NamespaceChangeNeeded, utilWriter);
1259 catch {
1260 if (streamOpened) {
1261 Host.WriteCompleted(ConfigStreamInfo.StreamName, false, writeContext);
1264 throw;
1268 // Guarantee that exceptions contain at least the name of the stream by wrapping them
1269 // in a ConfigurationException.
1271 catch (Exception e) {
1272 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, 0);
1275 Host.WriteCompleted(ConfigStreamInfo.StreamName, true, writeContext);
1277 // Update stream information for the config file
1278 ConfigStreamInfo.HasStream = true;
1279 ConfigStreamInfo.ClearStreamInfos();
1280 ConfigStreamInfo.StreamVersion = MonitorStream(null, null, ConfigStreamInfo.StreamName);
1283 if (configSourceUpdates != null) {
1284 // If we haven't checked before, check now
1285 if (!checkedConfigForUpdates) {
1286 string changedStreamName = FindChangedConfigurationStream();
1287 if (changedStreamName != null) {
1288 throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), changedStreamName, 0);
1292 // write updates
1293 foreach (DefinitionUpdate update in configSourceUpdates) {
1294 SaveConfigSource(update);
1298 // Update state to reflect the changes to the config file
1299 UpdateRecords();
1302 private bool AreDeclarationAttributesModified(FactoryRecord factoryRecord, ConfigurationSection configSection) {
1303 return factoryRecord.FactoryTypeName != configSection.SectionInformation.Type
1304 || factoryRecord.AllowLocation != configSection.SectionInformation.AllowLocation
1305 || factoryRecord.RestartOnExternalChanges != configSection.SectionInformation.RestartOnExternalChanges
1306 || factoryRecord.RequirePermission != configSection.SectionInformation.RequirePermission
1307 || factoryRecord.AllowDefinition != configSection.SectionInformation.AllowDefinition
1308 || factoryRecord.AllowExeDefinition != configSection.SectionInformation.AllowExeDefinition
1309 || factoryRecord.OverrideModeDefault.OverrideMode != configSection.SectionInformation.OverrideModeDefaultSetting.OverrideMode // Compare the value only
1310 || configSection.SectionInformation.IsModifiedFlags();
1313 private void AppendAttribute(StringBuilder sb, string key, string value) {
1314 sb.Append(key);
1315 sb.Append("=\"");
1316 sb.Append(value);
1317 sb.Append("\" ");
1320 private string GetUpdatedSectionDeclarationXml(FactoryRecord factoryRecord, ConfigurationSection configSection, ConfigurationSaveMode saveMode) {
1321 StringBuilder sb = new StringBuilder();
1322 sb.Append('<');
1323 sb.Append(KEYWORD_SECTION);
1324 sb.Append(' ');
1325 string type = (configSection.SectionInformation.Type != null) ? configSection.SectionInformation.Type : factoryRecord.FactoryTypeName;
1326 if (TypeStringTransformerIsSet)
1327 type = TypeStringTransformer(type);
1329 AppendAttribute(sb, KEYWORD_SECTION_NAME, configSection.SectionInformation.Name);
1330 AppendAttribute(sb, KEYWORD_SECTION_TYPE, type);
1332 if ( !configSection.SectionInformation.AllowLocation ||
1333 (saveMode == ConfigurationSaveMode.Full) ||
1334 ((saveMode == ConfigurationSaveMode.Modified) &&
1335 configSection.SectionInformation.AllowLocationModified)) {
1336 AppendAttribute(sb,
1337 KEYWORD_SECTION_ALLOWLOCATION,
1338 configSection.SectionInformation.AllowLocation ?
1339 KEYWORD_TRUE :
1340 KEYWORD_FALSE);
1343 if ((configSection.SectionInformation.AllowDefinition != ConfigurationAllowDefinition.Everywhere) ||
1344 (saveMode == ConfigurationSaveMode.Full) ||
1345 (saveMode == ConfigurationSaveMode.Modified && configSection.SectionInformation.AllowDefinitionModified)) {
1347 string v = null;
1348 switch (configSection.SectionInformation.AllowDefinition) {
1349 case ConfigurationAllowDefinition.Everywhere:
1350 v = KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE;
1351 break;
1352 case ConfigurationAllowDefinition.MachineOnly:
1353 v = KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY;
1354 break;
1356 case ConfigurationAllowDefinition.MachineToWebRoot:
1357 v = KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT;
1358 break;
1360 case ConfigurationAllowDefinition.MachineToApplication:
1361 v = KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION;
1362 break;
1365 AppendAttribute(sb, KEYWORD_SECTION_ALLOWDEFINITION, v);
1368 if ((configSection.SectionInformation.AllowExeDefinition !=
1369 ConfigurationAllowExeDefinition.MachineToApplication ) ||
1370 (saveMode == ConfigurationSaveMode.Full) ||
1371 (saveMode == ConfigurationSaveMode.Modified && configSection.SectionInformation.AllowExeDefinitionModified)) {
1373 AppendAttribute( sb,
1374 KEYWORD_SECTION_ALLOWEXEDEFINITION,
1375 ExeDefinitionToString(
1376 configSection.SectionInformation.AllowExeDefinition )
1380 if ( (configSection.SectionInformation.OverrideModeDefaultSetting.IsDefaultForSection == false) ||
1381 (saveMode == ConfigurationSaveMode.Full) ||
1382 (saveMode == ConfigurationSaveMode.Modified && configSection.SectionInformation.OverrideModeDefaultModified)) {
1384 AppendAttribute( sb,
1385 KEYWORD_SECTION_OVERRIDEMODEDEFAULT,
1386 configSection.SectionInformation.OverrideModeDefaultSetting.OverrideModeXmlValue);
1389 if (!configSection.SectionInformation.RestartOnExternalChanges) {
1390 AppendAttribute(sb, KEYWORD_SECTION_RESTARTONEXTERNALCHANGES, KEYWORD_FALSE);
1392 else if ((saveMode == ConfigurationSaveMode.Full) ||
1393 (saveMode == ConfigurationSaveMode.Modified && configSection.SectionInformation.RestartOnExternalChangesModified)) {
1394 AppendAttribute(sb, KEYWORD_SECTION_RESTARTONEXTERNALCHANGES, KEYWORD_TRUE);
1397 if (!configSection.SectionInformation.RequirePermission) {
1398 AppendAttribute(sb, KEYWORD_SECTION_REQUIREPERMISSION, KEYWORD_FALSE);
1400 else if ((saveMode == ConfigurationSaveMode.Full) ||
1401 (saveMode == ConfigurationSaveMode.Modified && configSection.SectionInformation.RequirePermissionModified)) {
1402 AppendAttribute(sb, KEYWORD_SECTION_REQUIREPERMISSION, KEYWORD_TRUE);
1405 sb.Append("/>");
1407 return sb.ToString();
1410 // ExeDefinitionToString
1412 // Take an ExeDefinition and translate it to a string
1414 private string ExeDefinitionToString(
1415 ConfigurationAllowExeDefinition allowDefinition ) {
1416 switch (allowDefinition) {
1417 case ConfigurationAllowExeDefinition.MachineOnly:
1418 return KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY;
1420 case ConfigurationAllowExeDefinition.MachineToApplication:
1421 return KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION;
1423 case ConfigurationAllowExeDefinition.MachineToRoamingUser:
1424 return KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING;
1426 case ConfigurationAllowExeDefinition.MachineToLocalUser:
1427 return KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL;
1430 throw ExceptionUtil.PropertyInvalid("AllowExeDefinition");
1433 private string GetUpdatedSectionGroupDeclarationXml(FactoryRecord factoryRecord, ConfigurationSectionGroup configSectionGroup) {
1434 if (TargetFramework != null && !configSectionGroup.ShouldSerializeSectionGroupInTargetVersion(TargetFramework))
1435 return null;
1437 StringBuilder sb = new StringBuilder();
1438 sb.Append('<');
1439 sb.Append(KEYWORD_SECTIONGROUP);
1440 sb.Append(' ');
1441 AppendAttribute(sb, KEYWORD_SECTIONGROUP_NAME, configSectionGroup.Name);
1442 string type = (configSectionGroup.Type != null) ? configSectionGroup.Type : factoryRecord.FactoryTypeName;
1443 if (TypeStringTransformerIsSet)
1444 type = TypeStringTransformer(type);
1446 AppendAttribute(sb, KEYWORD_SECTIONGROUP_TYPE, type);
1448 sb.Append('>');
1450 return sb.ToString();
1453 private bool HasRemovedSectionsOrGroups {
1454 get {
1455 return(_removedSections != null && _removedSections.Count > 0)
1456 || (_removedSectionGroups != null && _removedSectionGroups.Count > 0);
1460 // HasRemovedSections
1462 // Does this MgmtConfigrationRecord have any sections to be removed?
1464 private bool HasRemovedSections {
1465 get {
1466 return( ( _removedSections != null ) &&
1467 ( _removedSections.Count > 0 ) );
1471 // Gather all the updates to the configuration section declarations.
1472 private SectionUpdates GetConfigDeclarationUpdates(ConfigurationSaveMode saveMode, bool forceUpdateAll) {
1473 if (IsLocationConfig)
1474 return null;
1476 // hasChanged will be set to true if there is any change that will impact the current config file.
1477 bool hasChanged = HasRemovedSectionsOrGroups;
1478 SectionUpdates sectionUpdates = new SectionUpdates(string.Empty);
1480 if (_factoryRecords != null) {
1481 foreach (FactoryRecord factoryRecord in _factoryRecords.Values) {
1482 if (!factoryRecord.IsGroup) {
1483 string updatedXml = null;
1485 // Never write out an undeclared section.
1486 if (factoryRecord.IsUndeclared) {
1487 continue;
1490 // Note that GetConfigSection will return only those sections that have a sectionRecord
1491 // and has a result. In another word, only sections that have been accessed.
1492 ConfigurationSection configSection = GetConfigSection(factoryRecord.ConfigKey);
1494 if (configSection != null) {
1496 // We should skip this section declaration only if all below hold true:
1497 // 1. The section should not be declared at this level. Reasons:
1498 // i. The section is originally not declared at this level, or
1499 // ii. The user calls SectionInformation.ForceDeclaration(false)
1500 // 2. It's not machine.config. Otherwise we must declare it even if the user called ForceDeclaration(false)
1501 // 3. It's already declared higher up.
1502 // 4. It's not valid in the current Target Framework version
1503 if (!configSection.SectionInformation.IsDeclared
1504 && !MgmtParent.IsRootConfig
1505 && MgmtParent.FindFactoryRecord(factoryRecord.ConfigKey, false) != null) {
1507 if (factoryRecord.HasFile) {
1508 hasChanged = true;
1511 continue;
1513 if (TargetFramework != null &&
1514 !configSection.ShouldSerializeSectionInTargetVersion(TargetFramework))
1516 continue;
1519 if (AreDeclarationAttributesModified(factoryRecord, configSection) || !factoryRecord.HasFile) {
1520 updatedXml = GetUpdatedSectionDeclarationXml(factoryRecord, configSection, saveMode);
1521 if (!string.IsNullOrEmpty(updatedXml))
1522 hasChanged = true;
1526 DeclarationUpdate update = new DeclarationUpdate(factoryRecord.ConfigKey, !factoryRecord.HasFile, updatedXml);
1527 sectionUpdates.AddSection(update);
1529 else {
1530 bool addGroupUpdate = false;
1532 // LookupSectionGroup will return an object only if the group has been accessed
1533 ConfigurationSectionGroup configSectionGroup = LookupSectionGroup(factoryRecord.ConfigKey);
1535 if (!factoryRecord.HasFile) {
1536 // Not in the file, so it means the group is added programmatically.
1537 addGroupUpdate = true;
1539 else if (configSectionGroup != null && configSectionGroup.IsDeclarationRequired) {
1540 // The section group is declared in this config file
1541 addGroupUpdate = true;
1543 else if (factoryRecord.FactoryTypeName != null || configSectionGroup != null) {
1544 FactoryRecord parentFactoryRecord = null;
1545 if (!MgmtParent.IsRootConfig) {
1546 parentFactoryRecord = MgmtParent.FindFactoryRecord(factoryRecord.ConfigKey, false);
1549 // Add it if declaration is required. Please note this check is identical to the check
1550 // for _declarationRequired in ConfigurationSectionGroup.AttachToConfigurationRecord.
1551 addGroupUpdate = (parentFactoryRecord == null || parentFactoryRecord.FactoryTypeName == null);
1554 if (addGroupUpdate) {
1555 string updatedXml = null;
1557 if (!factoryRecord.HasFile
1558 || (configSectionGroup != null && configSectionGroup.Type != factoryRecord.FactoryTypeName)) {
1560 updatedXml = GetUpdatedSectionGroupDeclarationXml(factoryRecord, configSectionGroup);
1561 if (!string.IsNullOrEmpty(updatedXml)) {
1562 hasChanged = true;
1566 Debug.Assert(!factoryRecord.IsUndeclared, "!factoryRecord.IsUndeclared");
1567 Debug.Assert(!IsImplicitSection(factoryRecord.ConfigKey), "We should never write out an implicit section");
1569 DeclarationUpdate update = new DeclarationUpdate(factoryRecord.ConfigKey, !factoryRecord.HasFile, updatedXml);
1570 sectionUpdates.AddSectionGroup(update);
1576 if (_sectionRecords != null) {
1577 foreach (SectionRecord sectionRecord in _sectionRecords.Values) {
1578 if (GetFactoryRecord(sectionRecord.ConfigKey, false) != null || !sectionRecord.HasResult) {
1579 // Skip because this factory is defined locally ( in
1580 // which case we handled above), or it was not used
1581 continue;
1584 ConfigurationSection configSection = (ConfigurationSection) sectionRecord.Result;
1585 FactoryRecord factoryRecord = MgmtParent.FindFactoryRecord(sectionRecord.ConfigKey, false);
1587 // Add this section declaration if:
1588 // 1. The section is not declared locally (otherwise it's handled above)
1589 // 2. SectionInformation.IsDeclared is true (i.e. user called SectionInformation.ForceDeclaration(true))
1590 if (configSection.SectionInformation.IsDeclared) {
1591 Debug.Assert(!IsImplicitSection(sectionRecord.ConfigKey), "We should never write out an implicit section");
1592 Debug.Assert(!factoryRecord.IsUndeclared, "!factoryRecord.IsUndeclared");
1593 string updatedXml = GetUpdatedSectionDeclarationXml(factoryRecord, configSection, saveMode);
1594 if (!string.IsNullOrEmpty(updatedXml)) {
1595 hasChanged = true;
1596 DeclarationUpdate update = new DeclarationUpdate(factoryRecord.ConfigKey, true, updatedXml);
1597 sectionUpdates.AddSection(update);
1603 if (_sectionGroups != null) {
1604 foreach (ConfigurationSectionGroup configSectionGroup in _sectionGroups.Values) {
1605 if (GetFactoryRecord(configSectionGroup.SectionGroupName, false) != null) {
1606 continue;
1609 FactoryRecord factoryRecord = MgmtParent.FindFactoryRecord(configSectionGroup.SectionGroupName, false);
1610 if ( configSectionGroup.IsDeclared ||
1611 (factoryRecord != null && configSectionGroup.Type != factoryRecord.FactoryTypeName)) {
1613 string updatedXml = GetUpdatedSectionGroupDeclarationXml(factoryRecord, configSectionGroup);
1614 if (!string.IsNullOrEmpty(updatedXml)) {
1615 hasChanged = true;
1616 DeclarationUpdate update = new DeclarationUpdate(factoryRecord.ConfigKey, true, updatedXml);
1617 sectionUpdates.AddSectionGroup(update);
1623 if (hasChanged) {
1624 return sectionUpdates;
1626 else {
1627 return null;
1631 private bool AreLocationAttributesModified(SectionRecord sectionRecord, ConfigurationSection configSection) {
1633 OverrideModeSetting overrideMode = OverrideModeSetting.LocationDefault;
1634 bool inheritInChildApplications = true;
1636 if (sectionRecord.HasFileInput) {
1637 SectionXmlInfo sectionXmlInfo = sectionRecord.FileInput.SectionXmlInfo;
1638 overrideMode = sectionXmlInfo.OverrideModeSetting;
1639 inheritInChildApplications = !sectionXmlInfo.SkipInChildApps;
1642 // We will use IsSameForLocation tag so that we flag modes that cant go into the same location tag
1643 // as different. If we don't do that it will appear like the mode was not changed which will
1644 // case conflict later when determining if the section is moved ( when writing the new config updates )
1646 return
1647 (!OverrideModeSetting.CanUseSameLocationTag(overrideMode, configSection.SectionInformation.OverrideModeSetting))
1648 || (inheritInChildApplications != configSection.SectionInformation.InheritInChildApplications);
1651 private bool AreSectionAttributesModified(SectionRecord sectionRecord, ConfigurationSection configSection) {
1652 string configSource;
1653 string protectionProviderName;
1654 string configBuilderName;
1656 if (sectionRecord.HasFileInput) {
1657 SectionXmlInfo sectionXmlInfo = sectionRecord.FileInput.SectionXmlInfo;
1658 configSource = sectionXmlInfo.ConfigSource;
1659 protectionProviderName = sectionXmlInfo.ProtectionProviderName;
1660 configBuilderName = sectionXmlInfo.ConfigBuilderName;
1662 else {
1663 configSource = null;
1664 protectionProviderName = null;
1665 configBuilderName = null;
1668 return
1669 !StringUtil.EqualsNE(configSource, configSection.SectionInformation.ConfigSource)
1670 || !StringUtil.EqualsNE(protectionProviderName, configSection.SectionInformation.ProtectionProviderName)
1671 || !StringUtil.EqualsNE(configBuilderName, configSection.SectionInformation.ConfigBuilderName)
1672 || AreLocationAttributesModified(sectionRecord, configSection);
1675 private bool IsConfigSectionMoved(SectionRecord sectionRecord, ConfigurationSection configSection) {
1676 if (!sectionRecord.HasFileInput)
1677 return true;
1679 return AreLocationAttributesModified(sectionRecord, configSection);
1682 // Gather all the updates to the configuration section definitions.
1683 private void GetConfigDefinitionUpdates(
1684 bool requireUpdates, ConfigurationSaveMode saveMode, bool forceSaveAll,
1685 out ConfigDefinitionUpdates definitionUpdates, out ArrayList configSourceUpdates) {
1687 definitionUpdates = new ConfigDefinitionUpdates();
1688 configSourceUpdates = null;
1689 bool hasChanged = HasRemovedSections;
1691 // Loop through all the section records.
1692 if (_sectionRecords != null) {
1693 InitProtectedConfigurationSection(); // Make sure we have the initialized the protected config section, otherwise the foreach loop may ---- up
1694 foreach (DictionaryEntry de in _sectionRecords) {
1695 string configKey = (string)de.Key;
1696 SectionRecord sectionRecord = (SectionRecord) de.Value;
1697 sectionRecord.AddUpdate = false;
1698 bool addUpdate = sectionRecord.HasFileInput; // If true, add this section to definitionUpdates, and optinally to configSourceUpdates
1699 OverrideModeSetting overrideMode = OverrideModeSetting.LocationDefault;
1700 bool inheritInChildApplications = true;
1701 bool moved = false;
1702 string updatedXml = null;
1703 bool addToConfigSourceUpdates = false; // If true, we have to update the external config file for this section
1705 if (!sectionRecord.HasResult) {
1706 if (sectionRecord.HasFileInput) {
1707 SectionXmlInfo sectionXmlInfo = sectionRecord.FileInput.SectionXmlInfo;
1708 overrideMode = sectionXmlInfo.OverrideModeSetting;
1709 inheritInChildApplications = !sectionXmlInfo.SkipInChildApps;
1710 addToConfigSourceUpdates = requireUpdates && !String.IsNullOrEmpty(sectionXmlInfo.ConfigSource);
1713 else {
1714 ConfigurationSection configSection = (ConfigurationSection) sectionRecord.Result;
1716 if (TargetFramework != null && !configSection.ShouldSerializeSectionInTargetVersion(TargetFramework))
1717 continue;
1719 overrideMode = configSection.SectionInformation.OverrideModeSetting;
1720 inheritInChildApplications = configSection.SectionInformation.InheritInChildApplications;
1722 // it is an error to require a location section when the type doesn't allow locations.
1723 if (!configSection.SectionInformation.AllowLocation && (!overrideMode.IsDefaultForLocationTag || !inheritInChildApplications)) {
1724 throw new ConfigurationErrorsException(SR.GetString(SR.Config_inconsistent_location_attributes, configKey));
1727 addToConfigSourceUpdates = requireUpdates && !String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource);
1728 try {
1729 bool isModified = configSection.SectionInformation.ForceSave ||
1730 configSection.IsModified() ||
1731 (forceSaveAll && !configSection.SectionInformation.IsLocked);
1733 bool sectionAttributesModified = AreSectionAttributesModified(sectionRecord, configSection);
1734 bool sectionContentModified = (isModified || configSection.SectionInformation.RawXml != null);
1736 // Get the updated XML if the section has been modified.
1737 if (sectionContentModified || sectionAttributesModified) {
1738 configSection.SectionInformation.VerifyIsEditable();
1739 configSection.SectionInformation.Removed = false;
1740 addUpdate = true;
1741 moved = IsConfigSectionMoved(sectionRecord, configSection);
1743 if (!addToConfigSourceUpdates) {
1744 addToConfigSourceUpdates =
1745 !String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource)
1746 && (sectionContentModified || configSection.SectionInformation.ConfigSourceModified);
1749 if ( isModified ||
1750 configSection.SectionInformation.RawXml == null ||
1751 saveMode == ConfigurationSaveMode.Full) {
1752 // Note: we won't use RawXml if saveMode == Full because Full means we want to
1753 // write all properties, and RawXml may not have all properties.
1755 // 'System.Windows.Forms.ApplicationConfiguration' is declared as an implicit section. Saving this section definition without declaration in machine .config file is causing IIS
1756 // and "wcfservice' project creation failed. See bug #297811 for more details. Following fix exclude this section empty definition in the machine.config while saving it.
1757 if (String.Equals(configSection.SectionInformation.Name, ConfigurationStringConstants.WinformsApplicationConfigurationSectionName, StringComparison.Ordinal) &&
1758 String.Equals(configSection._configRecord.ConfigPath, ClientConfigurationHost.MachineConfigName, StringComparison.Ordinal)) {
1759 updatedXml = null;
1761 else {
1762 ConfigurationSection parentConfigSection = FindImmediateParentSection(configSection);
1763 updatedXml = configSection.SerializeSection(parentConfigSection, configSection.SectionInformation.Name, saveMode);
1765 ValidateSectionXml(updatedXml, configKey);
1767 else {
1768 updatedXml = configSection.SectionInformation.RawXml;
1771 if (string.IsNullOrEmpty(updatedXml)) {
1773 // We always need to emit a section, even if empty, when:
1774 // * The section has configSoure
1775 // * The section is in a location section that has non-default attributes
1776 // * The section is encrypted.
1778 if ( !String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource) ||
1779 !configSection.SectionInformation.LocationAttributesAreDefault ||
1780 (configSection.SectionInformation.ProtectionProvider != null)) {
1782 updatedXml = WriteEmptyElement(configSection.SectionInformation.Name);
1786 if (string.IsNullOrEmpty(updatedXml)) {
1787 configSection.SectionInformation.Removed = true;
1788 // configSection.ElementPresent = false;
1789 updatedXml = null;
1790 addUpdate = false;
1791 if (sectionRecord.HasFileInput) {
1792 hasChanged = true;
1793 // VSWhidbey 580658: When a section is to be removed, its corresponding file
1794 // input should be cleared as well so this section will be indicated as "moved"
1795 // next time something is added back to the section. Without marking it as "moved",
1796 // adding new content to a removed section fails as the bug describes.
1797 sectionRecord.RemoveFileInput();
1800 else {
1801 // configSection.ElementPresent = true;
1802 if (sectionAttributesModified || moved || String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource)) {
1803 hasChanged = true;
1806 // Encrypt if required.
1807 if (configSection.SectionInformation.ProtectionProvider != null) {
1808 ProtectedConfigurationSection protectedConfig = GetSection(BaseConfigurationRecord.RESERVED_SECTION_PROTECTED_CONFIGURATION) as ProtectedConfigurationSection;
1809 try {
1810 string encryptedSection = Host.EncryptSection(updatedXml, configSection.SectionInformation.ProtectionProvider, protectedConfig);
1812 // VsWhidbey 495120: The config host is responsible for encrypting a section, but it is the job of
1813 // System.Configuration to format an encrypted section during write (and to detect an encrypted section during read.)
1814 updatedXml = ProtectedConfigurationSection.FormatEncryptedSection(encryptedSection, configSection.SectionInformation.Name, configSection.SectionInformation.ProtectionProvider.Name);
1816 catch (Exception e) {
1817 throw new ConfigurationErrorsException(
1818 SR.GetString(SR.Encryption_failed, configSection.SectionInformation.SectionName, configSection.SectionInformation.ProtectionProvider.Name, e.Message),
1824 else if (configSection.SectionInformation.Removed) {
1825 addUpdate = false;
1826 if (sectionRecord.HasFileInput) {
1827 hasChanged = true;
1831 catch (Exception e) {
1832 throw new ConfigurationErrorsException(SR.GetString(SR.Config_exception_in_config_section_handler, configSection.SectionInformation.SectionName), e);
1836 if (addUpdate) {
1837 // Make sure we are not addingh a definition of a locked section
1838 if (GetSectionLockedMode(sectionRecord.ConfigKey) == OverrideMode.Deny) {
1839 throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_locked), (IConfigErrorInfo)(null));
1842 sectionRecord.AddUpdate = true;
1843 DefinitionUpdate definitionUpdate = definitionUpdates.AddUpdate(overrideMode, inheritInChildApplications, moved, updatedXml, sectionRecord);
1844 if (addToConfigSourceUpdates) {
1845 if (configSourceUpdates == null) {
1846 configSourceUpdates = new ArrayList();
1849 configSourceUpdates.Add(definitionUpdate);
1855 if (_flags[ ForceLocationWritten ]) {
1856 // We must write the location tag
1857 hasChanged = true;
1858 definitionUpdates.RequireLocation = true;
1861 if (_flags[ SuggestLocationRemoval ]) {
1862 // We should try to remove location
1863 hasChanged = true;
1866 if (hasChanged) {
1867 definitionUpdates.CompleteUpdates();
1869 else {
1870 definitionUpdates = null;
1875 // WriteEmptyElement
1877 // Take a element name, and create an xml string that contains
1878 // that element in an empty state
1880 private string WriteEmptyElement( string ElementName ) {
1881 StringBuilder sb;
1883 sb = new StringBuilder();
1885 // Create element
1886 sb.Append('<');
1887 sb.Append(ElementName);
1888 sb.Append(" />");
1890 return sb.ToString();
1893 // After the config file has been written out, update the section records
1894 // to reflect changes that were made in the config file.
1895 private void UpdateRecords() {
1896 if (_factoryRecords != null) {
1897 foreach (FactoryRecord factoryRecord in _factoryRecords.Values) {
1898 // Update stream information
1899 if (String.IsNullOrEmpty(factoryRecord.Filename)) {
1900 factoryRecord.Filename = ConfigStreamInfo.StreamName;
1903 factoryRecord.LineNumber = 0;
1905 ConfigurationSection configSection = GetConfigSection(factoryRecord.ConfigKey);
1906 if (configSection != null) {
1907 if (configSection.SectionInformation.Type != null) {
1908 factoryRecord.FactoryTypeName = configSection.SectionInformation.Type;
1911 factoryRecord.AllowLocation = configSection.SectionInformation.AllowLocation;
1912 factoryRecord.RestartOnExternalChanges = configSection.SectionInformation.RestartOnExternalChanges;
1913 factoryRecord.RequirePermission = configSection.SectionInformation.RequirePermission;
1914 factoryRecord.AllowDefinition = configSection.SectionInformation.AllowDefinition;
1915 factoryRecord.AllowExeDefinition = configSection.SectionInformation.AllowExeDefinition;
1920 if (_sectionRecords != null) {
1921 string definitionConfigPath = (IsLocationConfig) ? _parent.ConfigPath : ConfigPath;
1922 foreach (SectionRecord sectionRecord in _sectionRecords.Values) {
1923 string configSource;
1924 string configSourceStreamName;
1925 object configSourceStreamVersion;
1926 ConfigurationSection configSection;
1928 if (sectionRecord.HasResult) {
1929 configSection = (ConfigurationSection) sectionRecord.Result;
1930 configSource = configSection.SectionInformation.ConfigSource;
1931 if (String.IsNullOrEmpty(configSource)) {
1932 configSource = null;
1935 configSourceStreamName = configSection.SectionInformation.ConfigSourceStreamName;
1936 if (String.IsNullOrEmpty(configSourceStreamName)) {
1937 configSourceStreamName = null;
1940 else {
1941 configSection = null;
1942 configSource = null;
1943 configSourceStreamName = null;
1945 // If there is no result, then the only way there could be a
1946 // section record is:
1947 // 1. For there to be input in the file.
1948 // 2. A location tag applies to this record
1949 Debug.Assert(sectionRecord.HasFileInput || sectionRecord.HasLocationInputs, "sectionRecord.HasFileInput || sectionRecord.HasLocationInputs");
1951 // Note that if it's a location input, we don't need to monitor the configSource because
1952 // that stream is monitored by one of our parent's config record
1953 if (sectionRecord.HasFileInput) {
1954 SectionXmlInfo sectionXmlInfo = sectionRecord.FileInput.SectionXmlInfo;
1955 configSource = sectionXmlInfo.ConfigSource;
1956 configSourceStreamName = sectionXmlInfo.ConfigSourceStreamName;
1960 if (!String.IsNullOrEmpty(configSource)) {
1961 configSourceStreamVersion = MonitorStream(sectionRecord.ConfigKey, configSource, configSourceStreamName);
1963 else {
1964 configSourceStreamVersion = null;
1967 if (!sectionRecord.HasResult) {
1968 Debug.Assert(sectionRecord.HasFileInput || sectionRecord.HasLocationInputs, "sectionRecord.HasFileInput || sectionRecord.HasLocationInputs");
1970 // Note that if it's a location input, we don't need to monitor the configSource because
1971 // that stream is monitored by one of our parent's config record
1972 if (sectionRecord.HasFileInput) {
1973 SectionXmlInfo sectionXmlInfo = sectionRecord.FileInput.SectionXmlInfo;
1974 sectionXmlInfo.StreamVersion = ConfigStreamInfo.StreamVersion;
1975 sectionXmlInfo.ConfigSourceStreamVersion = configSourceStreamVersion;
1978 else {
1979 configSection.SectionInformation.RawXml = null;
1980 bool addUpdate = sectionRecord.AddUpdate;
1981 sectionRecord.AddUpdate = false;
1983 if (addUpdate) {
1984 SectionInput fileInput = sectionRecord.FileInput;
1985 if (fileInput == null) {
1986 SectionXmlInfo sectionXmlInfo = new SectionXmlInfo(
1987 sectionRecord.ConfigKey, definitionConfigPath, _configPath, _locationSubPath,
1988 ConfigStreamInfo.StreamName, 0, ConfigStreamInfo.StreamVersion, null,
1989 configSource, configSourceStreamName, configSourceStreamVersion,
1990 configSection.SectionInformation.ConfigBuilderName,
1991 configSection.SectionInformation.ProtectionProviderName,
1992 configSection.SectionInformation.OverrideModeSetting,
1993 !configSection.SectionInformation.InheritInChildApplications);
1995 fileInput = new SectionInput(sectionXmlInfo, null);
1996 fileInput.Result = configSection;
1997 fileInput.ResultRuntimeObject = configSection;
1998 sectionRecord.AddFileInput(fileInput);
2000 else {
2001 SectionXmlInfo sectionXmlInfo = fileInput.SectionXmlInfo;
2002 sectionXmlInfo.LineNumber = 0;
2003 sectionXmlInfo.StreamVersion = ConfigStreamInfo.StreamVersion;
2005 sectionXmlInfo.RawXml = null;
2006 sectionXmlInfo.ConfigSource = configSource;
2007 sectionXmlInfo.ConfigSourceStreamName = configSourceStreamName;
2008 sectionXmlInfo.ConfigSourceStreamVersion = configSourceStreamVersion;
2009 sectionXmlInfo.ConfigBuilderName = configSection.SectionInformation.ConfigBuilderName;
2010 sectionXmlInfo.ProtectionProviderName = configSection.SectionInformation.ProtectionProviderName;
2011 sectionXmlInfo.OverrideModeSetting = configSection.SectionInformation.OverrideModeSetting;
2012 sectionXmlInfo.SkipInChildApps = !configSection.SectionInformation.InheritInChildApplications;
2015 fileInput.ProtectionProvider = configSection.SectionInformation.ProtectionProvider;
2018 try {
2019 configSection.ResetModified();
2021 catch (Exception e) {
2022 throw new ConfigurationErrorsException(SR.GetString(SR.Config_exception_in_config_section_handler, sectionRecord.ConfigKey), e, ConfigStreamInfo.StreamName, 0);
2028 // Copy remaining stream updates, which correspond to streams used by location sections
2029 foreach (StreamInfo streamInfo in _streamInfoUpdates.Values) {
2030 if (!ConfigStreamInfo.StreamInfos.Contains(streamInfo.StreamName)) {
2031 MonitorStream(streamInfo.SectionName, streamInfo.ConfigSource, streamInfo.StreamName);
2035 // reinitialize _streamInfoUpdates
2036 InitStreamInfoUpdates();
2038 // Update namespace value
2039 _flags[ NamespacePresentInFile ] = _flags[ NamespacePresentCurrent ];
2041 // You only have one chance to force the location config, now you
2042 // will have to recreate the object
2043 _flags[ ForceLocationWritten ] = false;
2044 _flags[ SuggestLocationRemoval ] = false;
2046 // Handle removed location sections
2047 if (!IsLocationConfig && _locationSections != null && _removedSections != null && _removedSections.Count > 0) {
2048 int i = 0;
2049 while (i < _locationSections.Count) {
2050 LocationSectionRecord locationSectionRecord = (LocationSectionRecord) _locationSections[i];
2051 if (_removedSections.Contains(locationSectionRecord.ConfigKey)) {
2052 _locationSections.RemoveAt(i);
2054 else {
2055 i++;
2060 _removedSections = null;
2061 _removedSectionGroups = null;
2064 // Create a new config file.
2065 private void CreateNewConfig(
2066 SectionUpdates declarationUpdates,
2067 ConfigDefinitionUpdates definitionUpdates,
2068 NamespaceChange namespaceChange,
2069 XmlUtilWriter utilWriter) {
2070 int linePosition = DEFAULT_INDENT + 1;
2071 int indent = DEFAULT_INDENT;
2073 // Write Header
2074 utilWriter.Write(string.Format(CultureInfo.InvariantCulture,
2075 FORMAT_NEWCONFIGFILE,
2076 ConfigStreamInfo.StreamEncoding.WebName));
2078 // Write <configuration> tag
2079 if (namespaceChange == NamespaceChange.Add) {
2080 utilWriter.Write(string.Format(CultureInfo.InvariantCulture,
2081 FORMAT_CONFIGURATION_NAMESPACE,
2082 KEYWORD_CONFIGURATION_NAMESPACE));
2084 else {
2085 utilWriter.Write(FORMAT_CONFIGURATION);
2089 if (declarationUpdates != null) {
2090 WriteNewConfigDeclarations(declarationUpdates, utilWriter, linePosition, indent, false);
2093 WriteNewConfigDefinitions(definitionUpdates, utilWriter, linePosition, indent);
2095 utilWriter.Write(FORMAT_CONFIGURATION_ENDELEMENT);
2098 private void WriteNewConfigDeclarations(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) {
2099 if (!skipFirstIndent) {
2100 utilWriter.AppendSpacesToLinePosition(linePosition);
2103 utilWriter.Write("<configSections>\r\n");
2104 WriteUnwrittenConfigDeclarations(declarationUpdates, utilWriter, linePosition + indent, indent, false);
2105 utilWriter.AppendSpacesToLinePosition(linePosition);
2106 utilWriter.Write("</configSections>\r\n");
2108 if (skipFirstIndent) {
2109 utilWriter.AppendSpacesToLinePosition(linePosition);
2113 private void WriteUnwrittenConfigDeclarations(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) {
2114 WriteUnwrittenConfigDeclarationsRecursive(declarationUpdates, utilWriter, linePosition, indent, skipFirstIndent);
2117 private void WriteUnwrittenConfigDeclarationsRecursive(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) {
2118 string[] unretrievedSectionNames = declarationUpdates.GetUnretrievedSectionNames();
2119 if (unretrievedSectionNames != null) {
2120 foreach (string configKey in unretrievedSectionNames) {
2121 Debug.Assert(!IsImplicitSection(configKey), "We should never write out an implicit section");
2122 if (!skipFirstIndent) {
2123 utilWriter.AppendSpacesToLinePosition(linePosition);
2125 skipFirstIndent = false;
2127 DeclarationUpdate update = declarationUpdates.GetDeclarationUpdate(configKey);
2128 if (update != null && !string.IsNullOrEmpty(update.UpdatedXml)) {
2129 utilWriter.Write(update.UpdatedXml);
2130 utilWriter.AppendNewLine();
2135 string[] unretrievedGroupNames = declarationUpdates.GetUnretrievedGroupNames();
2136 if (unretrievedGroupNames != null) {
2137 foreach (string group in unretrievedGroupNames) {
2138 if (TargetFramework != null) {
2139 ConfigurationSectionGroup g = GetSectionGroup(group);
2140 if (g != null && !g.ShouldSerializeSectionGroupInTargetVersion(TargetFramework)){
2141 declarationUpdates.MarkGroupAsRetrieved(group);
2142 continue;
2145 if (!skipFirstIndent) {
2146 utilWriter.AppendSpacesToLinePosition(linePosition);
2148 skipFirstIndent = false;
2150 SectionUpdates declarationUpdatesChild = declarationUpdates.GetSectionUpdatesForGroup(group);
2151 DeclarationUpdate groupUpdate = declarationUpdatesChild.GetSectionGroupUpdate();
2152 if (groupUpdate == null) {
2153 utilWriter.Write("<sectionGroup name=\"" + group + "\">");
2155 else {
2156 utilWriter.Write(groupUpdate.UpdatedXml);
2158 utilWriter.AppendNewLine();
2160 WriteUnwrittenConfigDeclarationsRecursive(declarationUpdatesChild, utilWriter, linePosition + indent, indent, false);
2161 utilWriter.AppendSpacesToLinePosition(linePosition);
2162 utilWriter.Write("</sectionGroup>\r\n");
2167 private void WriteNewConfigDefinitions(ConfigDefinitionUpdates configDefinitionUpdates, XmlUtilWriter utilWriter, int linePosition, int indent) {
2168 if (configDefinitionUpdates == null)
2169 return;
2171 foreach (LocationUpdates locationUpdates in configDefinitionUpdates.LocationUpdatesList) {
2172 SectionUpdates sectionUpdates = locationUpdates.SectionUpdates;
2173 if (sectionUpdates.IsEmpty || !sectionUpdates.IsNew)
2174 continue;
2176 configDefinitionUpdates.FlagLocationWritten();
2177 bool writeLocationTag = _locationSubPath != null || !locationUpdates.IsDefault;
2178 int recurseLinePosition = linePosition;
2180 utilWriter.AppendSpacesToLinePosition(linePosition);
2182 if (writeLocationTag) {
2183 // write the <location> start tag
2184 if (_locationSubPath == null) {
2185 utilWriter.Write(String.Format(CultureInfo.InvariantCulture, FORMAT_LOCATION_NOPATH, locationUpdates.OverrideMode.LocationTagXmlString, BoolToString(locationUpdates.InheritInChildApps)));
2187 else {
2188 utilWriter.Write(String.Format(CultureInfo.InvariantCulture, FORMAT_LOCATION_PATH, locationUpdates.OverrideMode.LocationTagXmlString, BoolToString(locationUpdates.InheritInChildApps), _locationSubPath));
2191 recurseLinePosition += indent;
2192 utilWriter.AppendSpacesToLinePosition(recurseLinePosition);
2195 // Invoke the recursive write.
2196 WriteNewConfigDefinitionsRecursive(utilWriter, locationUpdates.SectionUpdates, recurseLinePosition, indent, true);
2198 if (writeLocationTag) {
2199 // Write the location end tag
2200 utilWriter.AppendSpacesToLinePosition(linePosition);
2201 utilWriter.Write(FORMAT_LOCATION_ENDELEMENT);
2202 utilWriter.AppendNewLine();
2206 if (configDefinitionUpdates.RequireLocation) {
2207 Debug.Assert(IsLocationConfig, "IsLocationConfig");
2209 // If we still require this to be written, then we must write it out now
2210 configDefinitionUpdates.FlagLocationWritten();
2212 utilWriter.AppendSpacesToLinePosition(linePosition);
2214 utilWriter.Write(String.Format(CultureInfo.InvariantCulture, FORMAT_LOCATION_PATH, OverrideModeSetting.LocationDefault.LocationTagXmlString, KEYWORD_TRUE, _locationSubPath));
2215 utilWriter.AppendSpacesToLinePosition(linePosition);
2216 utilWriter.Write(FORMAT_LOCATION_ENDELEMENT);
2217 utilWriter.AppendNewLine();
2221 // Recursively write new sections for each section group.
2222 private bool WriteNewConfigDefinitionsRecursive(XmlUtilWriter utilWriter, SectionUpdates sectionUpdates, int linePosition, int indent, bool skipFirstIndent) {
2223 bool wroteASection = false;
2225 string[] movedSectionNames = sectionUpdates.GetMovedSectionNames();
2226 if (movedSectionNames != null) {
2227 wroteASection = true;
2228 foreach (string configKey in movedSectionNames) {
2229 DefinitionUpdate update = sectionUpdates.GetDefinitionUpdate(configKey);
2230 WriteSectionUpdate(utilWriter, update, linePosition, indent, skipFirstIndent);
2231 utilWriter.AppendNewLine();
2232 skipFirstIndent = false;
2236 string[] newGroupNames = sectionUpdates.GetNewGroupNames();
2237 if (newGroupNames != null) {
2238 foreach (string group in newGroupNames) {
2240 if (TargetFramework != null) {
2241 ConfigurationSectionGroup g = GetSectionGroup(group);
2242 if (g != null && !g.ShouldSerializeSectionGroupInTargetVersion(TargetFramework)){
2243 sectionUpdates.MarkGroupAsRetrieved(group);
2244 continue;
2248 if (!skipFirstIndent) {
2249 utilWriter.AppendSpacesToLinePosition(linePosition);
2251 skipFirstIndent = false;
2253 utilWriter.Write("<" + group + ">\r\n");
2254 bool recurseWroteASection = WriteNewConfigDefinitionsRecursive(
2255 utilWriter, sectionUpdates.GetSectionUpdatesForGroup(group), linePosition + indent, indent, false);
2257 if (recurseWroteASection) {
2258 wroteASection = true;
2261 utilWriter.AppendSpacesToLinePosition(linePosition);
2262 utilWriter.Write("</" + group + ">\r\n");
2266 sectionUpdates.IsNew = false;
2268 return wroteASection;
2271 private void CheckPreamble(byte[] preamble, XmlUtilWriter utilWriter, byte[] buffer) {
2272 bool hasByteOrderMark = false;
2273 using (Stream preambleStream = new MemoryStream(buffer)) {
2274 byte[] streamStart = new byte[preamble.Length];
2275 if (preambleStream.Read(streamStart, 0, streamStart.Length) == streamStart.Length) {
2276 hasByteOrderMark = true;
2277 for (int i = 0; i < streamStart.Length; i++) {
2278 if (streamStart[i] != preamble[i]) {
2279 hasByteOrderMark = false;
2280 break;
2286 if (!hasByteOrderMark) {
2287 // Force the writer to emit byte order mark, then reset the stream
2288 // so that it is written over.
2289 object checkpoint = utilWriter.CreateStreamCheckpoint();
2290 utilWriter.Write('x');
2291 utilWriter.RestoreStreamCheckpoint(checkpoint);
2296 // Calculate a new indent based on the position of the parent element and the current node.
2298 private int UpdateIndent(int oldIndent, XmlUtil xmlUtil, XmlUtilWriter utilWriter, int parentLinePosition) {
2299 int indent = oldIndent;
2300 if (xmlUtil.Reader.NodeType == XmlNodeType.Element && utilWriter.IsLastLineBlank) {
2301 int childLinePosition = xmlUtil.TrueLinePosition;
2302 if (parentLinePosition < childLinePosition && childLinePosition <= parentLinePosition + MAX_INDENT) {
2303 indent = childLinePosition - parentLinePosition;
2307 return indent;
2310 // Copy a config file, replacing sections with updates.
2311 private void CopyConfig(SectionUpdates declarationUpdates, ConfigDefinitionUpdates definitionUpdates,
2312 byte[] buffer, string filename, NamespaceChange namespaceChange, XmlUtilWriter utilWriter) {
2314 CheckPreamble(ConfigStreamInfo.StreamEncoding.GetPreamble(), utilWriter, buffer);
2316 using (Stream stream = new MemoryStream(buffer)) {
2317 using (XmlUtil xmlUtil = new XmlUtil(stream, filename, false)) {
2318 // copy up to the <configuration> node
2319 XmlTextReader reader = xmlUtil.Reader;
2320 reader.WhitespaceHandling = WhitespaceHandling.All;
2321 reader.Read();
2322 xmlUtil.CopyReaderToNextElement(utilWriter, false);
2324 Debug.Assert(reader.NodeType == XmlNodeType.Element && reader.Name == KEYWORD_CONFIGURATION,
2325 "reader.NodeType == XmlNodeType.Element && reader.Name == KEYWORD_CONFIGURATION");
2327 int indent = DEFAULT_INDENT;
2328 int configurationElementLinePosition = xmlUtil.TrueLinePosition;
2329 bool isEmptyConfigurationElement = reader.IsEmptyElement;
2331 // copy <configuration> node
2332 // if the node is an empty element, we may need to open it.
2333 string configurationStartElement;
2334 if (namespaceChange == NamespaceChange.Add) {
2335 configurationStartElement = string.Format(
2336 CultureInfo.InvariantCulture, FORMAT_CONFIGURATION_NAMESPACE, KEYWORD_CONFIGURATION_NAMESPACE);
2338 else if (namespaceChange == NamespaceChange.Remove) {
2339 configurationStartElement = FORMAT_CONFIGURATION;
2341 else {
2342 configurationStartElement = null;
2345 bool needsChildren = (declarationUpdates != null || definitionUpdates != null);
2346 string configurationEndElement = xmlUtil.UpdateStartElement(utilWriter, configurationStartElement, needsChildren, configurationElementLinePosition, indent);
2348 bool foundConfigSectionsElement = false;
2349 if (!isEmptyConfigurationElement) {
2350 // copy up to the first element under <configuration>
2351 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2353 // updateIndent
2354 indent = UpdateIndent(indent, xmlUtil, utilWriter, configurationElementLinePosition);
2356 if (reader.NodeType == XmlNodeType.Element && reader.Name == KEYWORD_CONFIGSECTIONS) {
2357 foundConfigSectionsElement = true;
2359 int configSectionsElementLinePosition = xmlUtil.TrueLinePosition;
2360 bool isEmptyConfigSectionsElement = reader.IsEmptyElement;
2362 // if no updates, copy the entire <configSections> element
2363 if (declarationUpdates == null) {
2364 xmlUtil.CopyOuterXmlToNextElement(utilWriter, true);
2366 else {
2367 // copy <configSections>, and open it if it is an empty element
2368 string configSectionsEndElement = xmlUtil.UpdateStartElement(
2369 utilWriter, null, true, configSectionsElementLinePosition, indent);
2371 if (!isEmptyConfigSectionsElement) {
2372 // copy to next element under <configSections>, or up to closing </configSections>
2373 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2375 // copy config declarations
2376 CopyConfigDeclarationsRecursive(declarationUpdates, xmlUtil, utilWriter, string.Empty,
2377 configSectionsElementLinePosition, indent);
2379 Debug.Assert(reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGSECTIONS,
2380 "reader.NodeType == XmlNodeType.EndElement && reader.Name == \"KEYWORD_CONFIGSECTIONS\"");
2383 // write declarations not written by above copy
2384 if (declarationUpdates.HasUnretrievedSections()) {
2386 // determine the line position of the end element
2387 int endElementLinePosition = 0;
2388 if (configSectionsEndElement == null) {
2389 endElementLinePosition = xmlUtil.TrueLinePosition;
2392 // indent a new line
2393 if (!utilWriter.IsLastLineBlank) {
2394 utilWriter.AppendNewLine();
2397 WriteUnwrittenConfigDeclarations(declarationUpdates, utilWriter, configSectionsElementLinePosition + indent, indent, false);
2399 // restore spaces to end element
2400 if (configSectionsEndElement == null) {
2401 utilWriter.AppendSpacesToLinePosition(endElementLinePosition);
2405 // Copy the </configSections> element
2406 if (configSectionsEndElement == null) {
2407 xmlUtil.CopyXmlNode(utilWriter);
2409 else {
2410 // note that configSectionsEndElement already contains the proper indenting
2411 utilWriter.Write(configSectionsEndElement);
2414 // copy up to the next element under <configuration>, or up to closing </configSections>
2415 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2420 // Write new declarations
2421 if (!foundConfigSectionsElement && declarationUpdates != null) {
2422 bool skipFirstIndent = reader.Depth > 0 && reader.NodeType == XmlNodeType.Element;
2423 int newConfigSectionsLinePosition;
2424 if (skipFirstIndent) {
2425 newConfigSectionsLinePosition = xmlUtil.TrueLinePosition;
2427 else {
2428 newConfigSectionsLinePosition = configurationElementLinePosition + indent;
2431 WriteNewConfigDeclarations(declarationUpdates, utilWriter, newConfigSectionsLinePosition, indent, skipFirstIndent);
2434 if (definitionUpdates != null) {
2436 // Copy sections recursively. In the file we copy we start out at
2437 // location path="." allowOverride="true" inheritInChildApps="true"
2439 bool locationPathApplies = false;
2440 LocationUpdates locationUpdates = null;
2441 SectionUpdates sectionUpdates = null;
2442 if (!IsLocationConfig) {
2443 locationPathApplies = true;
2444 locationUpdates = definitionUpdates.FindLocationUpdates(OverrideModeSetting.LocationDefault, true);
2445 if (locationUpdates != null) {
2446 sectionUpdates = locationUpdates.SectionUpdates;
2450 CopyConfigDefinitionsRecursive(definitionUpdates, xmlUtil, utilWriter, locationPathApplies,
2451 locationUpdates, sectionUpdates, true, string.Empty, configurationElementLinePosition, indent);
2453 // Write new config sections from new groups.
2454 WriteNewConfigDefinitions(definitionUpdates, utilWriter, configurationElementLinePosition + indent, indent);
2456 #if DBG
2457 Debug.Assert(configurationEndElement != null || (reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGURATION),
2458 "configurationEndElement != null || (reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGURATION)");
2459 #endif
2463 #if DBG
2465 foreach (LocationUpdates l in definitionUpdates.LocationUpdatesList) {
2466 Debug.Assert(!l.SectionUpdates.HasUnretrievedSections(), "!l.SectionUpdates.HasUnretrievedSections()");
2469 #endif
2473 if (configurationEndElement != null) {
2474 // If we have to add closing config tag, then do it now
2475 // before copying extra whitespace/comments
2476 if (!utilWriter.IsLastLineBlank) {
2477 utilWriter.AppendNewLine();
2480 utilWriter.Write(configurationEndElement);
2484 // Copy the remainder of the file, the closing </configuration> node plus any whitespace
2485 // and comments
2487 while (xmlUtil.CopyXmlNode(utilWriter)) {
2493 private bool CopyConfigDeclarationsRecursive(
2494 SectionUpdates declarationUpdates, XmlUtil xmlUtil, XmlUtilWriter utilWriter, string group,
2495 int parentLinePosition, int parentIndent) {
2497 bool wroteASection = false;
2498 XmlTextReader reader = xmlUtil.Reader;
2499 int linePosition;
2500 int indent;
2501 int startingLinePosition;
2503 indent = UpdateIndent(parentIndent, xmlUtil, utilWriter, parentLinePosition);
2505 if (reader.NodeType == XmlNodeType.Element) {
2506 linePosition = xmlUtil.TrueLinePosition;
2507 startingLinePosition = linePosition;
2509 else if (reader.NodeType == XmlNodeType.EndElement) {
2510 linePosition = parentLinePosition + indent;
2511 if (utilWriter.IsLastLineBlank) {
2512 startingLinePosition = xmlUtil.TrueLinePosition;
2514 else {
2515 startingLinePosition = parentLinePosition;
2518 else {
2519 linePosition = parentLinePosition + indent;
2520 startingLinePosition = 0;
2524 // Write any new section declarations that apply to this group
2526 if (declarationUpdates != null) {
2527 string[] movedSectionNames = declarationUpdates.GetMovedSectionNames();
2528 if (movedSectionNames != null) {
2529 if (!utilWriter.IsLastLineBlank) {
2530 utilWriter.AppendNewLine();
2533 foreach (string configKey in movedSectionNames) {
2534 DeclarationUpdate sectionUpdate = declarationUpdates.GetDeclarationUpdate(configKey);
2535 Debug.Assert(!IsImplicitSection(configKey), "We should never write out an implicit section");
2537 // Write the one line section declaration.
2538 utilWriter.AppendSpacesToLinePosition(linePosition);
2539 utilWriter.Write(sectionUpdate.UpdatedXml);
2540 utilWriter.AppendNewLine();
2542 wroteASection = true;
2545 // Restore the whitespace we used for the first element, which is either a start or an end element.
2546 utilWriter.AppendSpacesToLinePosition(startingLinePosition);
2550 if (reader.NodeType == XmlNodeType.Element) {
2552 // For each element at this depth, either:
2553 // - Write the element verbatim and recurse due to a group hierarchy element.
2554 // - Write the element verbatim because it is unchanged
2555 // - Write the updated XML for the section.
2556 // - Skip it because the section has been removed.
2558 int depth = reader.Depth;
2559 while (reader.Depth == depth) {
2560 bool recurse = false;
2561 DeclarationUpdate sectionUpdate = null;
2562 DeclarationUpdate groupUpdate = null;
2563 SectionUpdates declarationUpdatesChild = null;
2564 SectionUpdates recurseDeclarationUpdates = declarationUpdates;
2565 string recurseGroup = group;
2567 // update the lineposition and indent for each element
2568 indent = UpdateIndent(indent, xmlUtil, utilWriter, parentLinePosition);
2569 linePosition = xmlUtil.TrueLinePosition;
2571 string directive = reader.Name;
2572 string name = reader.GetAttribute(KEYWORD_SECTIONGROUP_NAME);
2573 string configKey = CombineConfigKey(group, name);
2574 if (directive == KEYWORD_SECTIONGROUP) {
2575 // it's a group - get the updates for children
2576 declarationUpdatesChild = declarationUpdates.GetSectionUpdatesForGroup(name);
2577 if (declarationUpdatesChild != null) {
2578 // get the group update
2579 groupUpdate = declarationUpdatesChild.GetSectionGroupUpdate();
2581 // recurse if there are more sections to copy
2582 if (declarationUpdatesChild.HasUnretrievedSections()) {
2583 recurse = true;
2584 recurseGroup = configKey;
2585 recurseDeclarationUpdates = declarationUpdatesChild;
2589 else {
2590 // it is a section - get the update
2591 Debug.Assert(!IsImplicitSection(configKey), "We should never write out an implicit section");
2592 sectionUpdate = declarationUpdates.GetDeclarationUpdate(configKey);
2595 bool writeGroupUpdate = (groupUpdate != null && groupUpdate.UpdatedXml != null);
2596 if (recurse) {
2597 #if DBG
2598 string startElementName = reader.Name;
2599 #endif
2601 // create a checkpoint that we can revert to if no children are written
2602 object checkpoint = utilWriter.CreateStreamCheckpoint();
2603 string closingElement = null;
2605 // Copy this element node and up to the first subelement
2606 if (writeGroupUpdate) {
2607 // replace the element with the updated xml
2608 utilWriter.Write(groupUpdate.UpdatedXml);
2610 // skip over the start element
2611 reader.Read();
2613 else {
2614 closingElement= xmlUtil.UpdateStartElement(utilWriter, null, true, linePosition, indent);
2617 if (closingElement == null) {
2618 // Only if there is a closing element should
2619 // we move to it
2620 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2623 // Recurse
2624 bool recurseWroteASection = CopyConfigDeclarationsRecursive(
2625 recurseDeclarationUpdates, xmlUtil, utilWriter, recurseGroup, linePosition, indent);
2627 if (closingElement != null) {
2628 utilWriter.AppendSpacesToLinePosition(linePosition);
2629 utilWriter.Write(closingElement);
2631 // Since we already got to </configSections> in reader, lets
2632 // indent so we can copy the element in the right place
2633 utilWriter.AppendSpacesToLinePosition(parentLinePosition);
2634 } else {
2635 // Copy the end element
2636 xmlUtil.CopyXmlNode(utilWriter);
2639 if (recurseWroteASection || writeGroupUpdate) {
2640 wroteASection = true;
2642 else {
2643 // back out the change
2644 utilWriter.RestoreStreamCheckpoint(checkpoint);
2647 // Copy up to the next element, or exit this level.
2648 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2650 else {
2651 bool skip;
2652 bool skipChildElements = false;
2653 if (sectionUpdate == null) {
2654 skip = true;
2655 if (writeGroupUpdate) {
2656 // Insert an empty <sectionGroup type="typename" > node, to introduce the type
2657 wroteASection = true;
2658 utilWriter.Write(groupUpdate.UpdatedXml);
2659 utilWriter.AppendNewLine();
2660 utilWriter.AppendSpacesToLinePosition(linePosition);
2661 utilWriter.Write(FORMAT_SECTIONGROUP_ENDELEMENT);
2662 utilWriter.AppendNewLine();
2663 utilWriter.AppendSpacesToLinePosition(linePosition);
2665 else if (groupUpdate != null) {
2666 // VSWhidbey 522450
2667 // If groupUpdate exists, that means we've decided in GetConfigDeclarationUpdates
2668 // that the section group should stay in the file.
2669 Debug.Assert(groupUpdate.UpdatedXml == null, "groupUpdate.UpdatedXml == null");
2670 Debug.Assert(!declarationUpdatesChild.HasUnretrievedSections(),
2671 "If the group has any unretrieved section, we should have chosen the recursive code path above.");
2673 wroteASection = true;
2674 skip = false;
2676 // We should skip all the child sections. If we indeed need to keep any child
2677 // section, we should have chosen the recursive code path above.
2678 skipChildElements = true;
2681 else {
2682 wroteASection = true;
2683 if (sectionUpdate.UpdatedXml == null) {
2684 skip = false;
2686 else {
2687 skip = true;
2689 // Write the updated XML on a single line
2690 utilWriter.Write(sectionUpdate.UpdatedXml);
2694 if (skip) {
2696 // Skip over the existing element, then
2697 // copy up to the next element, or exit this level.
2699 xmlUtil.SkipAndCopyReaderToNextElement(utilWriter, true);
2701 else {
2702 if (skipChildElements) {
2703 xmlUtil.SkipChildElementsAndCopyOuterXmlToNextElement(utilWriter);
2705 else {
2706 // Copy this entire contents of this element and then to the next element, or exit this level.
2707 xmlUtil.CopyOuterXmlToNextElement(utilWriter, true);
2714 return wroteASection;
2717 // Copy configuration sections from the original configuration file.
2718 private bool CopyConfigDefinitionsRecursive(
2719 ConfigDefinitionUpdates configDefinitionUpdates, XmlUtil xmlUtil, XmlUtilWriter utilWriter,
2720 bool locationPathApplies, LocationUpdates locationUpdates, SectionUpdates sectionUpdates,
2721 bool addNewSections, string group, int parentLinePosition, int parentIndent) {
2723 bool wroteASection = false;
2724 XmlTextReader reader = xmlUtil.Reader;
2725 int linePosition;
2726 int indent;
2727 int startingLinePosition;
2729 indent = UpdateIndent(parentIndent, xmlUtil, utilWriter, parentLinePosition);
2731 if (reader.NodeType == XmlNodeType.Element) {
2732 linePosition = xmlUtil.TrueLinePosition;
2733 startingLinePosition = linePosition;
2735 else if (reader.NodeType == XmlNodeType.EndElement) {
2736 linePosition = parentLinePosition + indent;
2737 if (utilWriter.IsLastLineBlank) {
2738 startingLinePosition = xmlUtil.TrueLinePosition;
2740 else {
2741 startingLinePosition = parentLinePosition;
2744 else {
2745 linePosition = parentLinePosition + indent;
2746 startingLinePosition = 0;
2750 // Write any new sections that apply to this group
2752 if (sectionUpdates != null && addNewSections) {
2753 // Remove newness, so we won't write again
2754 sectionUpdates.IsNew = false;
2756 Debug.Assert(locationPathApplies, "locationPathApplies");
2757 string[] movedSectionNames = sectionUpdates.GetMovedSectionNames();
2758 if (movedSectionNames != null) {
2759 if (!utilWriter.IsLastLineBlank) {
2760 utilWriter.AppendNewLine();
2763 utilWriter.AppendSpacesToLinePosition(linePosition);
2764 bool skipFirstIndent = true;
2766 foreach (string configKey in movedSectionNames) {
2767 DefinitionUpdate update = sectionUpdates.GetDefinitionUpdate(configKey);
2769 WriteSectionUpdate(utilWriter, update, linePosition, indent, skipFirstIndent);
2770 skipFirstIndent = false;
2771 utilWriter.AppendNewLine();
2772 wroteASection = true;
2775 // Restore the whitespace we used for the first element, which is either a start or an end element.
2776 utilWriter.AppendSpacesToLinePosition(startingLinePosition);
2780 if (reader.NodeType == XmlNodeType.Element) {
2782 // For each element at this depth, either:
2783 // - Write the element verbatim and recurse due to a location section or group hierarchy element.
2784 // - Write the element verbatim because it is unchanged, or because the current location does
2785 // not apply.
2786 // - Write the updated XML for the section.
2787 // - Skip it because the section has been removed.
2789 int depth = reader.Depth;
2790 while (reader.Depth == depth) {
2791 bool recurse = false;
2792 DefinitionUpdate update = null;
2793 bool elementLocationPathApplies = locationPathApplies;
2794 LocationUpdates recurseLocationUpdates = locationUpdates;
2795 SectionUpdates recurseSectionUpdates = sectionUpdates;
2796 bool recurseAddNewSections = addNewSections;
2797 string recurseGroup = group;
2798 bool removedSectionOrGroup = false;
2800 // update the lineposition and indent for each element
2801 indent = UpdateIndent(indent, xmlUtil, utilWriter, parentLinePosition);
2802 linePosition = xmlUtil.TrueLinePosition;
2804 string elementName = reader.Name;
2805 if (elementName == KEYWORD_LOCATION) {
2806 string locationSubPathAttribute = reader.GetAttribute(KEYWORD_LOCATION_PATH);
2807 locationSubPathAttribute = NormalizeLocationSubPath(locationSubPathAttribute, xmlUtil);
2808 elementLocationPathApplies = false;
2809 OverrideModeSetting overrideMode = OverrideModeSetting.LocationDefault;
2810 bool inheritInChildApps = true;
2812 if (IsLocationConfig) {
2813 // For location config we will compare config paths instead of location strings
2814 // so that we dont end up comparing "1" with "Default Web Site" and ending up with the wrong result
2815 if (locationSubPathAttribute == null) {
2816 elementLocationPathApplies = false;
2818 else {
2819 elementLocationPathApplies = StringUtil.EqualsIgnoreCase(ConfigPath, Host.GetConfigPathFromLocationSubPath(Parent.ConfigPath, locationSubPathAttribute));
2822 else {
2823 Debug.Assert(LocationSubPath == null);
2825 // This is the same as doing StringUtil.EqualsIgnoreCase(_locationSubPath, locationSubPathAttribute)
2826 // but remember the first one is null. Also remember locationSubPathAttribute is already normalized
2827 elementLocationPathApplies = (locationSubPathAttribute == null);
2830 if (elementLocationPathApplies) {
2831 // Retrieve overrideMode and InheritInChildApps
2833 string allowOverrideAttribute = reader.GetAttribute(KEYWORD_LOCATION_ALLOWOVERRIDE);
2834 if (allowOverrideAttribute != null) {
2835 overrideMode = OverrideModeSetting.CreateFromXmlReadValue(Boolean.Parse(allowOverrideAttribute));
2838 string overrideModeAttribute = reader.GetAttribute(KEYWORD_LOCATION_OVERRIDEMODE);
2839 if (overrideModeAttribute != null) {
2840 overrideMode = OverrideModeSetting.CreateFromXmlReadValue(OverrideModeSetting.ParseOverrideModeXmlValue(overrideModeAttribute, null));
2842 Debug.Assert(allowOverrideAttribute == null, "allowOverride and overrideMode both detected in a <location> tag");
2845 string inheritInChildAppsAttribute = reader.GetAttribute(KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS);
2846 if (inheritInChildAppsAttribute != null) {
2847 inheritInChildApps = Boolean.Parse(inheritInChildAppsAttribute);
2850 // Flag that we already have one of these locations
2851 configDefinitionUpdates.FlagLocationWritten();
2854 if (reader.IsEmptyElement) {
2855 if (elementLocationPathApplies &&
2856 (configDefinitionUpdates.FindLocationUpdates(overrideMode,
2857 inheritInChildApps) != null)) {
2858 // If we are going to make updates here, then
2859 // delete the one that is here (so we can update later)
2860 elementLocationPathApplies = true;
2862 else {
2863 // If not lets leave it
2864 elementLocationPathApplies = false;
2867 else {
2868 // recurse if this location applies to us
2869 if (elementLocationPathApplies) {
2870 if (configDefinitionUpdates != null) {
2871 recurseLocationUpdates = configDefinitionUpdates.FindLocationUpdates(overrideMode, inheritInChildApps);
2872 if (recurseLocationUpdates != null) {
2873 recurse = true;
2874 recurseSectionUpdates = recurseLocationUpdates.SectionUpdates;
2876 // If this is <location path=".">, we don't want to add moved sections
2877 // to it.
2878 if (_locationSubPath == null && recurseLocationUpdates.IsDefault) {
2879 recurseAddNewSections = false;
2884 else {
2885 // recurse if necessary to remove items in _removedSections and _removedGroups
2886 if (HasRemovedSectionsOrGroups && !IsLocationConfig && Host.SupportsLocation) {
2887 recurse = true;
2888 recurseLocationUpdates = null;
2889 recurseSectionUpdates = null;
2890 recurseAddNewSections = false;
2895 else {
2896 string configKey = CombineConfigKey(group, elementName);
2897 FactoryRecord factoryRecord = FindFactoryRecord(configKey, false);
2898 if (factoryRecord == null) {
2899 // The factory was deleted, so regardless of whether this is a
2900 // section or sectionGroup, it can be skipped.
2901 if (!elementLocationPathApplies && !IsLocationConfig) {
2902 removedSectionOrGroup = true;
2905 else if (factoryRecord.IsGroup) {
2906 if (reader.IsEmptyElement) {
2907 if (!elementLocationPathApplies && !IsLocationConfig) {
2908 removedSectionOrGroup = true;
2911 else {
2912 // if the location path applies, recurse if there are updates
2913 if (sectionUpdates != null) {
2914 SectionUpdates sectionUpdatesChild = sectionUpdates.GetSectionUpdatesForGroup(elementName);
2915 if (sectionUpdatesChild != null) {
2916 recurse = true;
2917 recurseGroup = configKey;
2918 recurseSectionUpdates = sectionUpdatesChild;
2921 else if (!elementLocationPathApplies && !IsLocationConfig) {
2922 if (_removedSectionGroups != null && _removedSectionGroups.Contains(configKey)) {
2923 removedSectionOrGroup = true;
2925 else {
2926 recurse = true;
2927 recurseGroup = configKey;
2928 recurseLocationUpdates = null;
2929 recurseSectionUpdates = null;
2930 recurseAddNewSections = false;
2935 else {
2936 // it is a section - get the update
2937 if (sectionUpdates != null) {
2938 update = sectionUpdates.GetDefinitionUpdate(configKey);
2940 else if (!elementLocationPathApplies && !IsLocationConfig) {
2941 if (_removedSections != null && _removedSections.Contains(configKey)) {
2942 removedSectionOrGroup = true;
2948 if (recurse) {
2949 #if DBG
2950 string startElementName = reader.Name;
2951 #endif
2953 // flush, and get length of underlying stream
2954 object checkpoint = utilWriter.CreateStreamCheckpoint();
2956 // Copy this element node and up to the first subelement
2957 xmlUtil.CopyXmlNode(utilWriter);
2958 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2960 // Recurse
2961 bool recurseWroteASection = CopyConfigDefinitionsRecursive(
2962 configDefinitionUpdates, xmlUtil, utilWriter, elementLocationPathApplies, recurseLocationUpdates, recurseSectionUpdates,
2963 recurseAddNewSections, recurseGroup, linePosition, indent);
2965 // Copy the end element
2966 xmlUtil.CopyXmlNode(utilWriter);
2968 if (recurseWroteASection) {
2969 wroteASection = true;
2971 else {
2972 // back out the change
2973 utilWriter.RestoreStreamCheckpoint(checkpoint);
2976 // Copy up to the next element, or exit this level.
2977 xmlUtil.CopyReaderToNextElement(utilWriter, true);
2979 else {
2980 bool skip;
2981 if (update == null) {
2982 // remove the section from the file if we're in the correct location,
2983 // or if the section or group should be removed from all locations
2984 skip = elementLocationPathApplies || removedSectionOrGroup;
2986 else {
2987 // replace the section if the xml for it has been updated
2988 // if it is a configSource, don't write it unless the configSource parameters have changed
2989 skip = false;
2990 if (update.UpdatedXml != null) {
2991 ConfigurationSection configSection = (ConfigurationSection) update.SectionRecord.Result;
2992 if ( String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource) ||
2993 configSection.SectionInformation.ConfigSourceModified) {
2994 skip = true;
2995 WriteSectionUpdate(utilWriter, update, linePosition, indent, true);
2996 wroteASection = true;
3001 if (skip) {
3003 // Skip over the existing element, then
3004 // copy up to the next element, or exit this level.
3006 xmlUtil.SkipAndCopyReaderToNextElement(utilWriter, true);
3008 else {
3009 // Copy this entire contents of this element and then to the next element, or exit this level.
3010 xmlUtil.CopyOuterXmlToNextElement(utilWriter, true);
3011 wroteASection = true;
3018 // Write new section groups
3020 if (sectionUpdates != null && addNewSections && sectionUpdates.HasNewSectionGroups()) {
3021 // Add whitespace to align us with the other elements in this group
3022 linePosition = parentLinePosition + indent;
3023 if (reader.NodeType == XmlNodeType.EndElement) {
3024 if (utilWriter.IsLastLineBlank) {
3025 startingLinePosition = xmlUtil.TrueLinePosition;
3027 else {
3028 startingLinePosition = parentLinePosition;
3031 else {
3032 startingLinePosition = 0;
3035 utilWriter.AppendSpacesToLinePosition(linePosition);
3037 bool wroteNewSection = WriteNewConfigDefinitionsRecursive(utilWriter, sectionUpdates, linePosition, indent, true);
3038 if (wroteNewSection) {
3039 wroteASection = true;
3042 // Restore the whitespace of the end element
3043 utilWriter.AppendSpacesToLinePosition(startingLinePosition);
3046 return wroteASection;
3049 private void WriteSectionUpdate(XmlUtilWriter utilWriter, DefinitionUpdate update, int linePosition, int indent, bool skipFirstIndent) {
3050 ConfigurationSection configSection = (ConfigurationSection) update.SectionRecord.Result;
3051 string updatedXml;
3053 if (!String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource)) {
3054 updatedXml = string.Format(CultureInfo.InvariantCulture, FORMAT_SECTION_CONFIGSOURCE, configSection.SectionInformation.Name, configSection.SectionInformation.ConfigSource);
3056 else {
3057 updatedXml = update.UpdatedXml;
3060 string formattedXml = XmlUtil.FormatXmlElement(updatedXml, linePosition, indent, skipFirstIndent);
3061 utilWriter.Write(formattedXml);
3065 // SaveConfigSource
3067 private void SaveConfigSource(DefinitionUpdate update) {
3069 string configSourceStreamName;
3071 if (update.SectionRecord.HasResult) {
3072 ConfigurationSection configSection = (ConfigurationSection) update.SectionRecord.Result;
3073 configSourceStreamName = configSection.SectionInformation.ConfigSourceStreamName;
3075 else {
3076 Debug.Assert(update.SectionRecord.HasFileInput, "update.SectionRecord.HasFileInput");
3077 SectionInput fileInput = update.SectionRecord.FileInput;
3078 configSourceStreamName = fileInput.SectionXmlInfo.ConfigSourceStreamName;
3081 // Copy the input stream before opening the output stream.
3082 byte[] readBuffer = null;
3083 using (Stream streamRead = Host.OpenStreamForRead(configSourceStreamName)) {
3084 if (streamRead != null) {
3085 readBuffer = new byte[streamRead.Length];
3086 int count = streamRead.Read(readBuffer, 0, (int) streamRead.Length);
3087 if (count != streamRead.Length) {
3088 throw new ConfigurationErrorsException();
3093 // Write the changes to the output stream.
3094 bool hasFile = (readBuffer != null);
3095 object writeContext = null;
3096 bool streamOpened = false;
3098 try {
3099 try {
3100 string templateStreamName;
3102 if (Host.IsRemote) {
3103 // templateStreamName is used by OpenStreamForWrite for copying file attributes during saving.
3104 // (for details, see WriteFileContext.Complete.)
3106 // If we're using a remote host, then ConfigStreamInfo.StreamName is actually pointing to a
3107 // full filepath on a remote machine. In this case, it's impossible to copy the attributes
3108 // over, and thus we won't do it.
3109 templateStreamName = null;
3111 else {
3112 templateStreamName = ConfigStreamInfo.StreamName;
3115 using (Stream streamWrite = Host.OpenStreamForWrite(configSourceStreamName, templateStreamName, ref writeContext)) {
3116 streamOpened = true;
3117 if (update.UpdatedXml == null) {
3118 Debug.Assert(hasFile, "hasFile");
3119 if (hasFile) {
3120 streamWrite.Write(readBuffer, 0, readBuffer.Length);
3123 else {
3124 using (StreamWriter streamWriter = new StreamWriter(streamWrite)) {
3125 XmlUtilWriter utilWriter = new XmlUtilWriter(streamWriter, true);
3126 if (hasFile) {
3127 CopyConfigSource(utilWriter, update.UpdatedXml, configSourceStreamName, readBuffer);
3129 else {
3130 CreateNewConfigSource(utilWriter, update.UpdatedXml, DEFAULT_INDENT);
3136 catch {
3137 if (streamOpened) {
3138 Host.WriteCompleted(configSourceStreamName, false, writeContext);
3141 throw;
3145 // Guarantee that exceptions contain at least the name of the stream by wrapping them
3146 // in a ConfigurationException.
3148 catch (Exception e) {
3149 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, configSourceStreamName, 0);
3152 Host.WriteCompleted(configSourceStreamName, true, writeContext);
3155 private void CopyConfigSource(XmlUtilWriter utilWriter, string updatedXml, string configSourceStreamName, byte[] buffer) {
3156 // only copy the byte order mark if it exists in the current web.config
3157 byte[] preamble;
3158 using (Stream stream = new MemoryStream(buffer)) {
3159 using (XmlUtil xmlUtil = new XmlUtil(stream, configSourceStreamName, true)) {
3160 preamble = ConfigStreamInfo.StreamEncoding.GetPreamble();
3164 CheckPreamble(preamble, utilWriter, buffer);
3166 using (Stream stream = new MemoryStream(buffer)) {
3167 using (XmlUtil xmlUtil = new XmlUtil(stream, configSourceStreamName, false)) {
3168 XmlTextReader reader = xmlUtil.Reader;
3170 // copy up to the first element
3171 reader.WhitespaceHandling = WhitespaceHandling.All;
3172 reader.Read();
3174 // determine the indent to use for the element
3175 int indent = DEFAULT_INDENT;
3176 int linePosition = 1;
3177 bool hasElement = xmlUtil.CopyReaderToNextElement(utilWriter, false);
3178 if (hasElement) {
3179 // find the indent of the first attribute, if any
3180 int lineNumber = reader.LineNumber;
3181 linePosition = reader.LinePosition - 1;
3182 int attributeIndent = 0;
3183 while (reader.MoveToNextAttribute()) {
3184 if (reader.LineNumber > lineNumber) {
3185 attributeIndent = reader.LinePosition - linePosition;
3186 break;
3190 // find the indent of the first sub element, if any
3191 int elementIndent = 0;
3192 reader.Read();
3193 while (reader.Depth >= 1) {
3194 if (reader.NodeType == XmlNodeType.Element) {
3195 elementIndent = (reader.LinePosition - 1) - linePosition;
3196 break;
3199 reader.Read();
3202 if (elementIndent > 0) {
3203 indent = elementIndent;
3205 else if (attributeIndent > 0) {
3206 indent = attributeIndent;
3210 // Write the config source
3211 string formattedXml = XmlUtil.FormatXmlElement(updatedXml, linePosition, indent, true);
3212 utilWriter.Write(formattedXml);
3214 // Copy remaining contents
3215 if (hasElement) {
3216 // Skip over the existing element
3217 while (reader.Depth > 0) {
3218 reader.Read();
3221 if (reader.IsEmptyElement || reader.NodeType == XmlNodeType.EndElement) {
3222 reader.Read();
3225 // Copy remainder of file
3226 while (xmlUtil.CopyXmlNode(utilWriter)) {
3234 private void CreateNewConfigSource(XmlUtilWriter utilWriter, string updatedXml, int indent) {
3235 string formattedXml = XmlUtil.FormatXmlElement(updatedXml, 0, indent, true);
3236 utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FORMAT_CONFIGSOURCE_FILE, ConfigStreamInfo.StreamEncoding.WebName));
3237 utilWriter.Write(formattedXml + NL);
3240 private static string BoolToString(bool v) {
3241 return v ? KEYWORD_TRUE : KEYWORD_FALSE;
3244 // RemoveLocationWriteRequirement
3246 // It is possible that we have set the flag to force this location
3247 // to be written out. Allow a way to remove that
3249 internal void RemoveLocationWriteRequirement() {
3250 if (IsLocationConfig) {
3251 _flags[ ForceLocationWritten ] = false;
3252 _flags[ SuggestLocationRemoval ] = true;
3256 // NamespacePresent
3258 // Is the namespace present in the file or not? ...and do you
3259 // want it to be?
3261 internal bool NamespacePresent {
3262 get {
3263 return _flags[ NamespacePresentCurrent ];
3265 set {
3266 _flags[ NamespacePresentCurrent ] = value;
3270 // NamespaceChangeNeeded
3272 // On Update, do we need to add the namespace, remove it, or do nothing?
3274 private NamespaceChange NamespaceChangeNeeded {
3275 get {
3276 if (_flags[ NamespacePresentCurrent ] ==
3277 _flags[ NamespacePresentInFile ]) {
3278 return NamespaceChange.None;
3281 if (_flags[ NamespacePresentCurrent ]) {
3282 return NamespaceChange.Add;
3285 return NamespaceChange.Remove;
3289 // RecordItselfRequiresUpdates
3291 // Outside the scope of the sections and there definitions, does
3292 // the record itself require an update.
3294 private bool RecordItselfRequiresUpdates {
3295 get {
3296 return (NamespaceChangeNeeded != NamespaceChange.None);