1 //------------------------------------------------------------------------------
2 // <copyright file="ConfigurationLockCollection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 using System;
8 using System.Configuration.Internal;
9 using System.Collections;
10 using System.Collections.Specialized;
11 using System.Collections.Generic;
12 using System.IO;
13 using System.Reflection;
14 using System.Security.Permissions;
15 using System.Xml;
16 using System.Globalization;
17 using System.ComponentModel;
18 using System.Security;
19 using System.Text;
20 
21 namespace System.Configuration {
22 
23     public sealed class ConfigurationLockCollection : IEnumerable, ICollection {
24         private HybridDictionary internalDictionary;
25         private ArrayList internalArraylist;
26         private bool _bModified = false;
27         private bool _bExceptionList = false;
28         private string _ignoreName = String.Empty;
29         private ConfigurationElement _thisElement = null;
30         private ConfigurationLockCollectionType _lockType;
31         private string SeedList = String.Empty;
32         private const string LockAll = "*";
33 
ConfigurationLockCollection(ConfigurationElement thisElement)34         internal ConfigurationLockCollection(ConfigurationElement thisElement)
35             : this(thisElement, ConfigurationLockCollectionType.LockedAttributes) {
36         }
37 
ConfigurationLockCollection(ConfigurationElement thisElement, ConfigurationLockCollectionType lockType)38         internal ConfigurationLockCollection(ConfigurationElement thisElement, ConfigurationLockCollectionType lockType)
39             : this(thisElement, lockType, String.Empty) {
40         }
41 
ConfigurationLockCollection(ConfigurationElement thisElement, ConfigurationLockCollectionType lockType, string ignoreName)42         internal ConfigurationLockCollection(ConfigurationElement thisElement, ConfigurationLockCollectionType lockType, string ignoreName)
43             : this(thisElement, lockType, ignoreName, null) {
44         }
45 
ConfigurationLockCollection(ConfigurationElement thisElement, ConfigurationLockCollectionType lockType, string ignoreName, ConfigurationLockCollection parentCollection)46         internal ConfigurationLockCollection(ConfigurationElement thisElement, ConfigurationLockCollectionType lockType,
47                     string ignoreName, ConfigurationLockCollection parentCollection) {
48             _thisElement = thisElement;
49             _lockType = lockType;
50             internalDictionary = new HybridDictionary();
51             internalArraylist = new ArrayList();
52             _bModified = false;
53 
54             _bExceptionList = _lockType == ConfigurationLockCollectionType.LockedExceptionList ||
55                               _lockType == ConfigurationLockCollectionType.LockedElementsExceptionList;
56             _ignoreName = ignoreName;
57 
58             if (parentCollection != null) {
59                 foreach (string key in parentCollection) // seed the new collection
60                 {
61                     Add(key, ConfigurationValueFlags.Inherited);  // add the local copy
62                     if (_bExceptionList) {
63                         if (SeedList.Length != 0)
64                             SeedList += ",";
65                         SeedList += key;
66                     }
67                 }
68             }
69 
70         }
71 
ClearSeedList()72         internal void ClearSeedList()
73         {
74             SeedList = String.Empty;
75         }
76 
77         internal ConfigurationLockCollectionType LockType {
78             get { return _lockType; }
79         }
80 
Add(string name)81         public void Add(string name) {
82 
83             if (((_thisElement.ItemLocked & ConfigurationValueFlags.Locked) != 0) &&
84                 ((_thisElement.ItemLocked & ConfigurationValueFlags.Inherited) != 0)) {
85                 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, name));
86             }
87 
88             ConfigurationValueFlags flags = ConfigurationValueFlags.Modified;
89 
90             string attribToLockTrim = name.Trim();
91             ConfigurationProperty propToLock = _thisElement.Properties[attribToLockTrim];
92             if (propToLock == null && attribToLockTrim != LockAll) {
93                 ConfigurationElementCollection collection = _thisElement as ConfigurationElementCollection;
94                 if (collection == null && _thisElement.Properties.DefaultCollectionProperty != null) { // this is not a collection but it may contain a default collection
95                     collection = _thisElement[_thisElement.Properties.DefaultCollectionProperty] as ConfigurationElementCollection;
96                 }
97 
98                 if (collection == null ||
99                     _lockType == ConfigurationLockCollectionType.LockedAttributes || // If the collection type is not element then the lock is bogus
100                     _lockType == ConfigurationLockCollectionType.LockedExceptionList) {
101                     _thisElement.ReportInvalidLock(attribToLockTrim, _lockType, null, null);
102                 }
103                 else if (!collection.IsLockableElement(attribToLockTrim)) {
104                     _thisElement.ReportInvalidLock(attribToLockTrim, _lockType, null, collection.LockableElements);
105                 }
106             }
107             else { // the lock is in the property bag but is it the correct type?
108                 if (propToLock != null && propToLock.IsRequired)
109                     throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_required_attribute_lock_attempt, propToLock.Name));
110 
111                 if (attribToLockTrim != LockAll) {
112                     if ((_lockType == ConfigurationLockCollectionType.LockedElements) ||
113                          (_lockType == ConfigurationLockCollectionType.LockedElementsExceptionList)) {
114                         // If it is an element then it must be derived from ConfigurationElement
115                         if (!typeof(ConfigurationElement).IsAssignableFrom(propToLock.Type)) {
116                             _thisElement.ReportInvalidLock(attribToLockTrim, _lockType, null, null);
117                         }
118                     }
119                     else {
120                         // if it is a property then it cannot be derived from ConfigurationElement
121                         if (typeof(ConfigurationElement).IsAssignableFrom(propToLock.Type)) {
122                             _thisElement.ReportInvalidLock(attribToLockTrim, _lockType, null, null);
123                         }
124                     }
125                 }
126             }
127 
128             if (internalDictionary.Contains(name)) {
129                 flags = ConfigurationValueFlags.Modified | (ConfigurationValueFlags)internalDictionary[name];
130                 internalDictionary.Remove(name); // not from parent
131                 internalArraylist.Remove(name);
132             }
133             internalDictionary.Add(name, flags); // not from parent
134             internalArraylist.Add(name);
135             _bModified = true;
136         }
137 
138 
Add(string name, ConfigurationValueFlags flags)139         internal void Add(string name, ConfigurationValueFlags flags) {
140             if ((flags != ConfigurationValueFlags.Inherited) && (internalDictionary.Contains(name))) {
141                 // the user has an item declared as locked below a level where it is already locked
142                 // keep enough info so we can write out the lock if they save in modified mode
143                 flags = ConfigurationValueFlags.Modified | (ConfigurationValueFlags)internalDictionary[name];
144                 internalDictionary.Remove(name);
145                 internalArraylist.Remove(name);
146             }
147 
148             internalDictionary.Add(name, flags); // not from parent
149             internalArraylist.Add(name);
150         }
151 
DefinedInParent(string name)152         internal bool DefinedInParent(string name) {
153             if (name == null)
154                 return false;
155             if (_bExceptionList)
156             {
157                 string ParentListEnclosed = "," + SeedList + ",";
158                 if (name.Equals(_ignoreName) || ParentListEnclosed.IndexOf("," + name + ",", StringComparison.Ordinal) >= 0) {
159                     return true;
160                 }
161             }
162             return (internalDictionary.Contains(name) &&
163                 ((ConfigurationValueFlags)internalDictionary[name] & ConfigurationValueFlags.Inherited) != 0);
164         }
165 
IsValueModified(string name)166         internal bool IsValueModified(string name) {
167             return (internalDictionary.Contains(name) &&
168                 ((ConfigurationValueFlags)internalDictionary[name] & ConfigurationValueFlags.Modified) != 0);
169         }
170 
RemoveInheritedLocks()171         internal void RemoveInheritedLocks() {
172             StringCollection removeList = new StringCollection();
173             foreach (string key in this) {
174                 if (DefinedInParent(key)) {
175                     removeList.Add(key);
176                 }
177             }
178             foreach (string key in removeList) {
179                 internalDictionary.Remove(key);
180                 internalArraylist.Remove(key);
181             }
182         }
183 
184 
Remove(string name)185         public void Remove(string name) {
186             if (!internalDictionary.Contains(name)) {
187                 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found, name));
188             }
189             // in a locked list you cannot remove items that were locked in the parent
190             // in an exception list this is legal because it makes the list more restrictive
191 
192             if (_bExceptionList == false &&
193                 ((ConfigurationValueFlags)internalDictionary[name] & ConfigurationValueFlags.Inherited) != 0) {
194                 if (((ConfigurationValueFlags)internalDictionary[name] & ConfigurationValueFlags.Modified) != 0) {
195                     // allow the local one to be "removed" so it won't write out but throw if they try and remove
196                     // one that is only inherited
197                     ConfigurationValueFlags flags = (ConfigurationValueFlags)internalDictionary[name];
198                     flags &= ~ConfigurationValueFlags.Modified;
199                     internalDictionary[name] = flags;
200                     _bModified = true;
201                     return;
202                 }
203                 else {
204                     throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, name));
205                 }
206             }
207 
208             internalDictionary.Remove(name);
209             internalArraylist.Remove(name);
210             _bModified = true;
211         }
212 
GetEnumerator()213         public IEnumerator GetEnumerator() {
214             return internalArraylist.GetEnumerator();
215         }
216 
ClearInternal(bool useSeedIfAvailble)217         internal void ClearInternal(bool useSeedIfAvailble) {
218             ArrayList removeList = new ArrayList();
219             foreach (DictionaryEntry de in internalDictionary) {
220                 if ((((ConfigurationValueFlags)de.Value & ConfigurationValueFlags.Inherited) == 0)
221                     || _bExceptionList == true) {
222                     removeList.Add(de.Key);
223                 }
224             }
225             foreach (object removeKey in removeList) {
226                 internalDictionary.Remove(removeKey);
227                 internalArraylist.Remove(removeKey);
228             }
229 
230             // Clearing an Exception list really means revert to parent
231             if (useSeedIfAvailble && !String.IsNullOrEmpty(SeedList)) {
232                 string[] Keys = SeedList.Split(new char[] { ',' });
233                 foreach (string key in Keys) {
234                     Add(key, ConfigurationValueFlags.Inherited); //
235                 }
236             }
237             _bModified = true;
238         }
239 
Clear()240         public void Clear() {
241             ClearInternal(true);
242         }
243 
Contains(string name)244         public bool Contains(string name) {
245             if (_bExceptionList && name.Equals(_ignoreName)) {
246                 return true;
247             }
248             return internalDictionary.Contains(name);
249         }
250 
251         public int Count {
252             get {
253                 return internalDictionary.Count;
254             }
255         }
256 
257         public bool IsSynchronized {
258             get {
259                 return false;
260             }
261         }
262 
263         public object SyncRoot {
264             get {
265                 return this;
266             }
267         }
268 
CopyTo(string[] array, int index)269         public void CopyTo(string[] array, int index) {
270             ((ICollection)this).CopyTo(array, index);
271         }
272 
ICollection.CopyTo(Array array, int index)273         void ICollection.CopyTo(Array array, int index) {
274             internalArraylist.CopyTo(array, index);
275         }
276 
277         public bool IsModified {
278             get {
279                 return _bModified;
280             }
281         }
282 
ResetModified()283         internal void ResetModified() {
284             _bModified = false;
285         }
286 
IsReadOnly(string name)287         public bool IsReadOnly(string name) {
288             if (!internalDictionary.Contains(name)) {
289                 throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found, name));
290             }
291             return (bool)(((ConfigurationValueFlags)internalDictionary[name] & ConfigurationValueFlags.Inherited) != 0);
292         }
293 
294         internal bool ExceptionList {
295             get {
296                 return _bExceptionList;
297             }
298         }
299 
300         public string AttributeList {
301             get {
302                 StringBuilder sb;
303                 sb = new StringBuilder();
304 
305                 foreach (DictionaryEntry de in internalDictionary) {
306                     if (sb.Length != 0) {
307                         sb.Append(',');
308                     }
309                     sb.Append(de.Key);
310                 }
311                 return sb.ToString();
312             }
313         }
314 
SetFromList(string attributeList)315         public void SetFromList(string attributeList) {
316             string[] splits = attributeList.Split(new char[] { ',', ';', ':' });
317             Clear();
318             foreach (string name in splits) {
319                 string attribTrim = name.Trim();
320                 if (!Contains(attribTrim)) {
321                     Add(attribTrim);
322                 }
323             }
324         }
325         public bool HasParentElements {
326             get {
327                 // return true if there is at least one element that was defined in the parent
328                 bool result = false;
329                 // Check to see if the exception list is empty as a result of a merge from config
330                 // If so the there were some parent elements because empty string is invalid in config.
331                 // and the only way to get an empty list is for the merged config to have no elements
332                 // in common.
333                 if (ExceptionList && internalDictionary.Count == 0 && !String.IsNullOrEmpty(SeedList))
334                     return true;
335 
336                 foreach (DictionaryEntry de in internalDictionary) {
337                     if ((bool)(((ConfigurationValueFlags)de.Value & ConfigurationValueFlags.Inherited) != 0)) {
338                         result = true;
339                         break;
340                     }
341                 }
342 
343                 return result;
344             }
345         }
346     }
347 }
348