1 //------------------------------------------------------------------------------
2 // <copyright file="MgmtConfigurationRecord.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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
;
15 using System
.Reflection
;
16 using System
.Security
;
17 using System
.Security
.Permissions
;
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
,
53 string locationSubPath
) {
55 MgmtConfigurationRecord configRecord
= new MgmtConfigurationRecord();
56 configRecord
.Init(configRoot
, parent
, configPath
, locationSubPath
);
60 // don't allow instantiation except by Create
61 private MgmtConfigurationRecord() {
65 IInternalConfigRoot configRoot
,
66 IInternalConfigRecord parent
,
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
{
96 return(MgmtConfigurationRecord
) _parent
;
100 // The IInternalConfigHost cast to UpdateConfigHost.
101 private UpdateConfigHost UpdateConfigHost
{
103 return (UpdateConfigHost
) Host
;
108 static readonly SimpleBitVector32 MgmtClassFlags
= new SimpleBitVector32(
109 ClassSupportsKeepInputs
110 | ClassIgnoreLocalErrors
);
112 override protected SimpleBitVector32 ClassFlags
{
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);
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
) {
177 if (String
.IsNullOrEmpty(factoryRecord
.FactoryTypeName
)) {
178 type
= typeof(ConfigurationSectionGroup
);
181 type
= TypeUtil
.GetTypeWithReflectionPermission(Host
, factoryRecord
.FactoryTypeName
, true);
184 ConstructorInfo ctor
= TypeUtil
.GetConstructorWithReflectionPermission(type
, typeof(ConfigurationSectionGroup
), true);
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
;
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
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);
220 // There is no runtime object at designtime - always return the result.
222 override protected object GetRuntimeObject(object 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
;
241 // Return the collection of ConfigurationSectionGroups.
243 private Hashtable SectionGroups
{
245 if (_sectionGroups
== null) {
246 _sectionGroups
= new Hashtable();
249 return _sectionGroups
;
254 // Return the collection of removed sections.
256 private Hashtable RemovedSections
{
258 if (_removedSections
== null) {
259 _removedSections
= new Hashtable();
262 return _removedSections
;
267 // Return the collection of removed section groups.
269 private Hashtable RemovedSectionGroups
{
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) {
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.
313 // <sectionGroup name="mySectionGroup" />
316 configSectionGroup
= new ConfigurationSectionGroup();
320 // Create the section group of the desired type.
323 // <sectionGroup name="mySectionGroup" type="MySectionGroupType, acme" />
326 ConstructorInfo ctor
= EnsureSectionGroupFactory(factoryRecord
);
329 configSectionGroup
= (ConfigurationSectionGroup
) TypeUtil
.InvokeCtorWithReflectionPermission(ctor
);
331 catch (Exception e
) {
332 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_exception_creating_section_handler
, factoryRecord
.ConfigKey
),
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
);
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
{
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
{
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;
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
);
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);
445 object resultRuntimeObject
;
446 CreateSectionDefault(configKey
, false, factoryRecord
, null, out resultObject
, out resultRuntimeObject
);
447 result
= (ConfigurationSection
) resultObject
;
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
;
458 current
= current
.MgmtParent
;
461 Debug
.Assert(!current
.IsRootConfig
, "An immediate parent result should have been found");
464 if (!result
.IsReadOnly()) {
465 result
.SetReadOnly();
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
);
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;
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
) {
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
);
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
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
);
563 configSection
.SectionInformation
.RawXml
= null;
567 catch (Exception e
) {
568 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_exception_in_config_section_handler
, configSection
.SectionInformation
.SectionName
),
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
)) {
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
)) {
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
))
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
));
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
))
692 XmlTextReader reader
= null;
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
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
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
));
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
);
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
);
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
);
738 if (reader
!= null) {
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
,
798 configSection
.SectionInformation
.AllowLocation
,
799 configSection
.SectionInformation
.AllowDefinition
,
800 configSection
.SectionInformation
.AllowExeDefinition
,
801 configSection
.SectionInformation
.OverrideModeDefaultSetting
,
802 configSection
.SectionInformation
.RestartOnExternalChanges
,
803 configSection
.SectionInformation
.RequirePermission
,
805 false, // isUndeclared
806 ConfigStreamInfo
.StreamName
,
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
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
)) {
864 // If it's not a registered section, there's nothing to do.
865 if (FindFactoryRecord(configKey
, true) == null) {
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) {
896 while (i
< _locationSections
.Count
) {
897 LocationSectionRecord locationSectionRecord
= (LocationSectionRecord
) _locationSections
[i
];
898 if (locationSectionRecord
.ConfigKey
!= configKey
) {
902 sectionIsUsed
= true;
903 _locationSections
.RemoveAt(i
);
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
994 private ArrayList
GetDescendentSectionFactories(string configKey
) {
995 ArrayList sectionGroups
= new ArrayList();
997 string configKeyAncestor
;
998 if (configKey
.Length
== 0) {
999 configKeyAncestor
= string.Empty
;
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
;
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) {
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
)) {
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
{
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();
1133 // Update the config file with the changes in each ConfigurationSection
1135 internal void SaveAs(string filename
, ConfigurationSaveMode saveMode
, bool forceUpdateAll
) {
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.
1214 using (StreamReader reader
= new StreamReader(ConfigStreamInfo
.StreamName
)) {
1215 if (reader
.Peek() >= 0) {
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
;
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;
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
);
1254 CreateNewConfig(declarationUpdates
, definitionUpdates
, NamespaceChangeNeeded
, utilWriter
);
1261 Host
.WriteCompleted(ConfigStreamInfo
.StreamName
, false, writeContext
);
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);
1293 foreach (DefinitionUpdate update
in configSourceUpdates
) {
1294 SaveConfigSource(update
);
1298 // Update state to reflect the changes to the config file
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) {
1320 private string GetUpdatedSectionDeclarationXml(FactoryRecord factoryRecord
, ConfigurationSection configSection
, ConfigurationSaveMode saveMode
) {
1321 StringBuilder sb
= new StringBuilder();
1323 sb
.Append(KEYWORD_SECTION
);
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
)) {
1337 KEYWORD_SECTION_ALLOWLOCATION
,
1338 configSection
.SectionInformation
.AllowLocation
?
1343 if ((configSection
.SectionInformation
.AllowDefinition
!= ConfigurationAllowDefinition
.Everywhere
) ||
1344 (saveMode
== ConfigurationSaveMode
.Full
) ||
1345 (saveMode
== ConfigurationSaveMode
.Modified
&& configSection
.SectionInformation
.AllowDefinitionModified
)) {
1348 switch (configSection
.SectionInformation
.AllowDefinition
) {
1349 case ConfigurationAllowDefinition
.Everywhere
:
1350 v
= KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE
;
1352 case ConfigurationAllowDefinition
.MachineOnly
:
1353 v
= KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY
;
1356 case ConfigurationAllowDefinition
.MachineToWebRoot
:
1357 v
= KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT
;
1360 case ConfigurationAllowDefinition
.MachineToApplication
:
1361 v
= KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION
;
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
);
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
))
1437 StringBuilder sb
= new StringBuilder();
1439 sb
.Append(KEYWORD_SECTIONGROUP
);
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
);
1450 return sb
.ToString();
1453 private bool HasRemovedSectionsOrGroups
{
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
{
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
)
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
) {
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
) {
1513 if (TargetFramework
!= null &&
1514 !configSection
.ShouldSerializeSectionInTargetVersion(TargetFramework
))
1519 if (AreDeclarationAttributesModified(factoryRecord
, configSection
) || !factoryRecord
.HasFile
) {
1520 updatedXml
= GetUpdatedSectionDeclarationXml(factoryRecord
, configSection
, saveMode
);
1521 if (!string.IsNullOrEmpty(updatedXml
))
1526 DeclarationUpdate update
= new DeclarationUpdate(factoryRecord
.ConfigKey
, !factoryRecord
.HasFile
, updatedXml
);
1527 sectionUpdates
.AddSection(update
);
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
)) {
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
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
)) {
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) {
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
)) {
1616 DeclarationUpdate update
= new DeclarationUpdate(factoryRecord
.ConfigKey
, true, updatedXml
);
1617 sectionUpdates
.AddSectionGroup(update
);
1624 return sectionUpdates
;
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 )
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
;
1663 configSource
= null;
1664 protectionProviderName
= null;
1665 configBuilderName
= null;
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
)
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;
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
);
1714 ConfigurationSection configSection
= (ConfigurationSection
) sectionRecord
.Result
;
1716 if (TargetFramework
!= null && !configSection
.ShouldSerializeSectionInTargetVersion(TargetFramework
))
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
);
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;
1741 moved
= IsConfigSectionMoved(sectionRecord
, configSection
);
1743 if (!addToConfigSourceUpdates
) {
1744 addToConfigSourceUpdates
=
1745 !String
.IsNullOrEmpty(configSection
.SectionInformation
.ConfigSource
)
1746 && (sectionContentModified
|| configSection
.SectionInformation
.ConfigSourceModified
);
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
)) {
1762 ConfigurationSection parentConfigSection
= FindImmediateParentSection(configSection
);
1763 updatedXml
= configSection
.SerializeSection(parentConfigSection
, configSection
.SectionInformation
.Name
, saveMode
);
1765 ValidateSectionXml(updatedXml
, configKey
);
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;
1791 if (sectionRecord
.HasFileInput
) {
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();
1801 // configSection.ElementPresent = true;
1802 if (sectionAttributesModified
|| moved
|| String
.IsNullOrEmpty(configSection
.SectionInformation
.ConfigSource
)) {
1806 // Encrypt if required.
1807 if (configSection
.SectionInformation
.ProtectionProvider
!= null) {
1808 ProtectedConfigurationSection protectedConfig
= GetSection(BaseConfigurationRecord
.RESERVED_SECTION_PROTECTED_CONFIGURATION
) as ProtectedConfigurationSection
;
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
) {
1826 if (sectionRecord
.HasFileInput
) {
1831 catch (Exception e
) {
1832 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_exception_in_config_section_handler
, configSection
.SectionInformation
.SectionName
), e
);
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
1858 definitionUpdates
.RequireLocation
= true;
1861 if (_flags
[ SuggestLocationRemoval
]) {
1862 // We should try to remove location
1867 definitionUpdates
.CompleteUpdates();
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
) {
1883 sb
= new StringBuilder();
1887 sb
.Append(ElementName
);
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;
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
);
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
;
1979 configSection
.SectionInformation
.RawXml
= null;
1980 bool addUpdate
= sectionRecord
.AddUpdate
;
1981 sectionRecord
.AddUpdate
= false;
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
);
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
;
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) {
2049 while (i
< _locationSections
.Count
) {
2050 LocationSectionRecord locationSectionRecord
= (LocationSectionRecord
) _locationSections
[i
];
2051 if (_removedSections
.Contains(locationSectionRecord
.ConfigKey
)) {
2052 _locationSections
.RemoveAt(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
;
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
));
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);
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 + "\">");
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)
2171 foreach (LocationUpdates locationUpdates
in configDefinitionUpdates
.LocationUpdatesList
) {
2172 SectionUpdates sectionUpdates
= locationUpdates
.SectionUpdates
;
2173 if (sectionUpdates
.IsEmpty
|| !sectionUpdates
.IsNew
)
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
)));
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);
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;
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
;
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
;
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
;
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);
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);
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
);
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
;
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
);
2457 Debug
.Assert(configurationEndElement
!= null || (reader
.NodeType
== XmlNodeType
.EndElement
&& reader
.Name
== KEYWORD_CONFIGURATION
),
2458 "configurationEndElement != null || (reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGURATION)");
2465 foreach (LocationUpdates l
in definitionUpdates
.LocationUpdatesList
) {
2466 Debug
.Assert(!l
.SectionUpdates
.HasUnretrievedSections(), "!l.SectionUpdates.HasUnretrievedSections()");
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
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
;
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
;
2515 startingLinePosition
= parentLinePosition
;
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()) {
2584 recurseGroup
= configKey
;
2585 recurseDeclarationUpdates
= declarationUpdatesChild
;
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);
2598 string startElementName
= reader
.Name
;
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
2614 closingElement
= xmlUtil
.UpdateStartElement(utilWriter
, null, true, linePosition
, indent
);
2617 if (closingElement
== null) {
2618 // Only if there is a closing element should
2620 xmlUtil
.CopyReaderToNextElement(utilWriter
, true);
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
);
2635 // Copy the end element
2636 xmlUtil
.CopyXmlNode(utilWriter
);
2639 if (recurseWroteASection
|| writeGroupUpdate
) {
2640 wroteASection
= true;
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);
2652 bool skipChildElements
= false;
2653 if (sectionUpdate
== null) {
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) {
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;
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;
2682 wroteASection
= true;
2683 if (sectionUpdate
.UpdatedXml
== null) {
2689 // Write the updated XML on a single line
2690 utilWriter
.Write(sectionUpdate
.UpdatedXml
);
2696 // Skip over the existing element, then
2697 // copy up to the next element, or exit this level.
2699 xmlUtil
.SkipAndCopyReaderToNextElement(utilWriter
, true);
2702 if (skipChildElements
) {
2703 xmlUtil
.SkipChildElementsAndCopyOuterXmlToNextElement(utilWriter
);
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
;
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
;
2741 startingLinePosition
= parentLinePosition
;
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
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;
2819 elementLocationPathApplies
= StringUtil
.EqualsIgnoreCase(ConfigPath
, Host
.GetConfigPathFromLocationSubPath(Parent
.ConfigPath
, locationSubPathAttribute
));
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;
2863 // If not lets leave it
2864 elementLocationPathApplies
= false;
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) {
2874 recurseSectionUpdates
= recurseLocationUpdates
.SectionUpdates
;
2876 // If this is <location path=".">, we don't want to add moved sections
2878 if (_locationSubPath
== null && recurseLocationUpdates
.IsDefault
) {
2879 recurseAddNewSections
= false;
2885 // recurse if necessary to remove items in _removedSections and _removedGroups
2886 if (HasRemovedSectionsOrGroups
&& !IsLocationConfig
&& Host
.SupportsLocation
) {
2888 recurseLocationUpdates
= null;
2889 recurseSectionUpdates
= null;
2890 recurseAddNewSections
= false;
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;
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) {
2917 recurseGroup
= configKey
;
2918 recurseSectionUpdates
= sectionUpdatesChild
;
2921 else if (!elementLocationPathApplies
&& !IsLocationConfig
) {
2922 if (_removedSectionGroups
!= null && _removedSectionGroups
.Contains(configKey
)) {
2923 removedSectionOrGroup
= true;
2927 recurseGroup
= configKey
;
2928 recurseLocationUpdates
= null;
2929 recurseSectionUpdates
= null;
2930 recurseAddNewSections
= false;
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;
2950 string startElementName
= reader
.Name
;
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);
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;
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);
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
;
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
2990 if (update
.UpdatedXml
!= null) {
2991 ConfigurationSection configSection
= (ConfigurationSection
) update
.SectionRecord
.Result
;
2992 if ( String
.IsNullOrEmpty(configSection
.SectionInformation
.ConfigSource
) ||
2993 configSection
.SectionInformation
.ConfigSourceModified
) {
2995 WriteSectionUpdate(utilWriter
, update
, linePosition
, indent
, true);
2996 wroteASection
= true;
3003 // Skip over the existing element, then
3004 // copy up to the next element, or exit this level.
3006 xmlUtil
.SkipAndCopyReaderToNextElement(utilWriter
, true);
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
;
3028 startingLinePosition
= parentLinePosition
;
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
;
3053 if (!String
.IsNullOrEmpty(configSection
.SectionInformation
.ConfigSource
)) {
3054 updatedXml
= string.Format(CultureInfo
.InvariantCulture
, FORMAT_SECTION_CONFIGSOURCE
, configSection
.SectionInformation
.Name
, configSection
.SectionInformation
.ConfigSource
);
3057 updatedXml
= update
.UpdatedXml
;
3060 string formattedXml
= XmlUtil
.FormatXmlElement(updatedXml
, linePosition
, indent
, skipFirstIndent
);
3061 utilWriter
.Write(formattedXml
);
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
;
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;
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;
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");
3120 streamWrite
.Write(readBuffer
, 0, readBuffer
.Length
);
3124 using (StreamWriter streamWriter
= new StreamWriter(streamWrite
)) {
3125 XmlUtilWriter utilWriter
= new XmlUtilWriter(streamWriter
, true);
3127 CopyConfigSource(utilWriter
, update
.UpdatedXml
, configSourceStreamName
, readBuffer
);
3130 CreateNewConfigSource(utilWriter
, update
.UpdatedXml
, DEFAULT_INDENT
);
3138 Host
.WriteCompleted(configSourceStreamName
, false, writeContext
);
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
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
;
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);
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
;
3190 // find the indent of the first sub element, if any
3191 int elementIndent
= 0;
3193 while (reader
.Depth
>= 1) {
3194 if (reader
.NodeType
== XmlNodeType
.Element
) {
3195 elementIndent
= (reader
.LinePosition
- 1) - linePosition
;
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
3216 // Skip over the existing element
3217 while (reader
.Depth
> 0) {
3221 if (reader
.IsEmptyElement
|| reader
.NodeType
== XmlNodeType
.EndElement
) {
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;
3258 // Is the namespace present in the file or not? ...and do you
3261 internal bool NamespacePresent
{
3263 return _flags
[ NamespacePresentCurrent
];
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
{
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
{
3296 return (NamespaceChangeNeeded
!= NamespaceChange
.None
);