1 // 2 // System.Configuration.ConfigurationElement.cs 3 // 4 // Authors: 5 // Duncan Mak (duncan@ximian.com) 6 // Lluis Sanchez Gual (lluis@novell.com) 7 // Martin Baulig <martin.baulig@xamarin.com> 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining 10 // a copy of this software and associated documentation files (the 11 // "Software"), to deal in the Software without restriction, including 12 // without limitation the rights to use, copy, modify, merge, publish, 13 // distribute, sublicense, and/or sell copies of the Software, and to 14 // permit persons to whom the Software is furnished to do so, subject to 15 // the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 // 28 // Copyright (C) 2004 Novell, Inc (http://www.novell.com) 29 // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com) 30 // 31 32 using System.Collections; 33 using System.Xml; 34 using System.Reflection; 35 using System.IO; 36 using System.ComponentModel; 37 38 namespace System.Configuration 39 { 40 public abstract class ConfigurationElement 41 { 42 string rawXml; 43 bool modified; 44 ElementMap map; 45 ConfigurationPropertyCollection keyProps; 46 ConfigurationElementCollection defaultCollection; 47 bool readOnly; 48 ElementInformation elementInfo; 49 ConfigurationElementProperty elementProperty; 50 Configuration _configuration; 51 bool elementPresent; 52 53 internal Configuration Configuration { 54 get { return _configuration; } 55 set { _configuration = value; } 56 } 57 ConfigurationElement()58 protected ConfigurationElement () 59 { 60 } 61 InitFromProperty(PropertyInformation propertyInfo)62 internal virtual void InitFromProperty (PropertyInformation propertyInfo) 63 { 64 elementInfo = new ElementInformation (this, propertyInfo); 65 Init (); 66 } 67 68 public ElementInformation ElementInformation { 69 get { 70 if (elementInfo == null) 71 elementInfo = new ElementInformation (this, null); 72 return elementInfo; 73 } 74 } 75 76 internal string RawXml { 77 get { return rawXml; } 78 set { 79 // FIXME: this hack is nasty. We should make 80 // some refactory on the entire assembly. 81 if (rawXml == null || value != null) 82 rawXml = value; 83 } 84 } 85 Init()86 protected internal virtual void Init () 87 { 88 } 89 90 protected internal virtual ConfigurationElementProperty ElementProperty { 91 get { 92 if (elementProperty == null) 93 elementProperty = new ConfigurationElementProperty (ElementInformation.Validator); 94 return elementProperty; 95 } 96 } 97 98 protected ContextInformation EvaluationContext { 99 get { 100 if (Configuration != null) 101 return Configuration.EvaluationContext; 102 throw new ConfigurationErrorsException ( 103 "This element is not currently associated with any context."); 104 } 105 } 106 107 ConfigurationLockCollection lockAllAttributesExcept; 108 public ConfigurationLockCollection LockAllAttributesExcept { 109 get { 110 if (lockAllAttributesExcept == null) { 111 lockAllAttributesExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute | ConfigurationLockType.Exclude); 112 } 113 114 return lockAllAttributesExcept; 115 } 116 } 117 118 ConfigurationLockCollection lockAllElementsExcept; 119 public ConfigurationLockCollection LockAllElementsExcept { 120 get { 121 if (lockAllElementsExcept == null) { 122 lockAllElementsExcept = new ConfigurationLockCollection (this, ConfigurationLockType.Element | ConfigurationLockType.Exclude); 123 } 124 125 return lockAllElementsExcept; 126 } 127 } 128 129 ConfigurationLockCollection lockAttributes; 130 public ConfigurationLockCollection LockAttributes { 131 get { 132 if (lockAttributes == null) { 133 lockAttributes = new ConfigurationLockCollection (this, ConfigurationLockType.Attribute); 134 } 135 136 return lockAttributes; 137 } 138 } 139 140 ConfigurationLockCollection lockElements; 141 public ConfigurationLockCollection LockElements { 142 get { 143 if (lockElements == null) { 144 lockElements = new ConfigurationLockCollection (this, ConfigurationLockType.Element); 145 } 146 147 return lockElements; 148 } 149 } 150 151 bool lockItem; 152 public bool LockItem { 153 get { return lockItem; } 154 set { lockItem = value; } 155 } 156 157 [MonoTODO] ListErrors(IList errorList)158 protected virtual void ListErrors (IList errorList) 159 { 160 throw new NotImplementedException (); 161 } 162 163 [MonoTODO] SetPropertyValue(ConfigurationProperty prop, object value, bool ignoreLocks)164 protected void SetPropertyValue (ConfigurationProperty prop, object value, bool ignoreLocks) 165 { 166 try { 167 if (value != null) { 168 /* XXX all i know for certain is that Validation happens here */ 169 prop.Validate (value); 170 171 /* XXX presumably the actual setting of the 172 * property happens here instead of in the 173 * set_Item code below, but that would mean 174 * the Value needs to be stuffed in the 175 * property, not the propertyinfo (or else the 176 * property needs a ref to the property info 177 * to correctly set the value). */ 178 } 179 } 180 catch (Exception e) { 181 throw new ConfigurationErrorsException (String.Format ("The value for the property '{0}' on type {1} is not valid.", prop.Name, this.ElementInformation.Type), e); 182 } 183 } 184 GetKeyProperties()185 internal ConfigurationPropertyCollection GetKeyProperties () 186 { 187 if (keyProps != null) return keyProps; 188 189 ConfigurationPropertyCollection tmpkeyProps = new ConfigurationPropertyCollection (); 190 foreach (ConfigurationProperty prop in Properties) { 191 if (prop.IsKey) 192 tmpkeyProps.Add (prop); 193 } 194 195 return keyProps = tmpkeyProps; 196 } 197 GetDefaultCollection()198 internal ConfigurationElementCollection GetDefaultCollection () 199 { 200 if (defaultCollection != null) return defaultCollection; 201 202 ConfigurationProperty defaultCollectionProp = null; 203 204 foreach (ConfigurationProperty prop in Properties) { 205 if (prop.IsDefaultCollection) { 206 defaultCollectionProp = prop; 207 break; 208 } 209 } 210 211 if (defaultCollectionProp != null) { 212 defaultCollection = this [defaultCollectionProp] as ConfigurationElementCollection; 213 } 214 215 return defaultCollection; 216 } 217 218 protected internal object this [ConfigurationProperty prop] { 219 get { return this [prop.Name]; } 220 set { this [prop.Name] = value; } 221 } 222 223 protected internal object this [string propertyName] { 224 get { 225 PropertyInformation pi = ElementInformation.Properties [propertyName]; 226 if (pi == null) 227 throw new InvalidOperationException ("Property '" + propertyName + "' not found in configuration element"); 228 229 return pi.Value; 230 } 231 232 set { 233 PropertyInformation pi = ElementInformation.Properties [propertyName]; 234 if (pi == null) 235 throw new InvalidOperationException ("Property '" + propertyName + "' not found in configuration element"); 236 237 SetPropertyValue (pi.Property, value, false); 238 239 pi.Value = value; 240 modified = true; 241 } 242 } 243 244 protected internal virtual ConfigurationPropertyCollection Properties { 245 get { 246 if (map == null) 247 map = ElementMap.GetMap (GetType()); 248 return map.Properties; 249 } 250 } 251 Equals(object compareTo)252 public override bool Equals (object compareTo) 253 { 254 ConfigurationElement other = compareTo as ConfigurationElement; 255 if (other == null) return false; 256 if (GetType() != other.GetType()) return false; 257 258 foreach (ConfigurationProperty prop in Properties) { 259 if (!object.Equals (this [prop], other [prop])) 260 return false; 261 } 262 return true; 263 } 264 GetHashCode()265 public override int GetHashCode () 266 { 267 int code = 0; 268 object o; 269 270 foreach (ConfigurationProperty prop in Properties) { 271 o = this [prop]; 272 if (o == null) 273 continue; 274 275 code += o.GetHashCode (); 276 } 277 278 return code; 279 } 280 HasLocalModifications()281 internal virtual bool HasLocalModifications () 282 { 283 foreach (PropertyInformation pi in ElementInformation.Properties) 284 if (pi.ValueOrigin == PropertyValueOrigin.SetHere && pi.IsModified) 285 return true; 286 287 return false; 288 } 289 DeserializeElement(XmlReader reader, bool serializeCollectionKey)290 protected internal virtual void DeserializeElement (XmlReader reader, bool serializeCollectionKey) 291 { 292 Hashtable readProps = new Hashtable (); 293 294 reader.MoveToContent (); 295 elementPresent = true; 296 297 while (reader.MoveToNextAttribute ()) 298 { 299 PropertyInformation prop = ElementInformation.Properties [reader.LocalName]; 300 if (prop == null || (serializeCollectionKey && !prop.IsKey)) { 301 /* handle the built in ConfigurationElement attributes here */ 302 if (reader.LocalName == "lockAllAttributesExcept") { 303 LockAllAttributesExcept.SetFromList (reader.Value); 304 } 305 else if (reader.LocalName == "lockAllElementsExcept") { 306 LockAllElementsExcept.SetFromList (reader.Value); 307 } 308 else if (reader.LocalName == "lockAttributes") { 309 LockAttributes.SetFromList (reader.Value); 310 } 311 else if (reader.LocalName == "lockElements") { 312 LockElements.SetFromList (reader.Value); 313 } 314 else if (reader.LocalName == "lockItem") { 315 LockItem = (reader.Value.ToLowerInvariant () == "true"); 316 } 317 else if (reader.LocalName == "xmlns") { 318 /* ignore */ 319 } else if (this is ConfigurationSection && reader.LocalName == "configSource") { 320 /* ignore */ 321 } else if (!OnDeserializeUnrecognizedAttribute (reader.LocalName, reader.Value)) 322 throw new ConfigurationErrorsException ("Unrecognized attribute '" + reader.LocalName + "'.", reader); 323 324 continue; 325 } 326 327 if (readProps.ContainsKey (prop)) 328 throw new ConfigurationErrorsException ("The attribute '" + prop.Name + "' may only appear once in this element.", reader); 329 330 string value = null; 331 try { 332 value = reader.Value; 333 ValidateValue (prop.Property, value); 334 prop.SetStringValue (value); 335 } catch (ConfigurationErrorsException) { 336 throw; 337 } catch (ConfigurationException) { 338 throw; 339 } catch (Exception ex) { 340 string msg = String.Format ("The value for the property '{0}' is not valid. The error is: {1}", prop.Name, ex.Message); 341 throw new ConfigurationErrorsException (msg, reader); 342 } 343 readProps [prop] = prop.Name; 344 345 ConfigXmlTextReader _reader = reader as ConfigXmlTextReader; 346 if (_reader != null){ 347 prop.Source = _reader.Filename; 348 prop.LineNumber = _reader.LineNumber; 349 } 350 } 351 352 reader.MoveToElement (); 353 if (reader.IsEmptyElement) { 354 reader.Skip (); 355 } else { 356 int depth = reader.Depth; 357 358 reader.ReadStartElement (); 359 reader.MoveToContent (); 360 361 do { 362 if (reader.NodeType != XmlNodeType.Element) { 363 reader.Skip (); 364 continue; 365 } 366 367 PropertyInformation prop = ElementInformation.Properties [reader.LocalName]; 368 if (prop == null || (serializeCollectionKey && !prop.IsKey)) { 369 if (!OnDeserializeUnrecognizedElement (reader.LocalName, reader)) { 370 if (prop == null) { 371 ConfigurationElementCollection c = GetDefaultCollection (); 372 if (c != null && c.OnDeserializeUnrecognizedElement (reader.LocalName, reader)) 373 continue; 374 } 375 throw new ConfigurationErrorsException ("Unrecognized element '" + reader.LocalName + "'.", reader); 376 } 377 continue; 378 } 379 380 if (!prop.IsElement) 381 throw new ConfigurationErrorsException ("Property '" + prop.Name + "' is not a ConfigurationElement."); 382 383 if (readProps.Contains (prop)) 384 throw new ConfigurationErrorsException ("The element <" + prop.Name + "> may only appear once in this section.", reader); 385 386 ConfigurationElement val = (ConfigurationElement) prop.Value; 387 val.DeserializeElement (reader, serializeCollectionKey); 388 readProps [prop] = prop.Name; 389 390 if(depth == reader.Depth) 391 reader.Read(); 392 393 } while (depth < reader.Depth); 394 } 395 396 modified = false; 397 398 foreach (PropertyInformation prop in ElementInformation.Properties) 399 if (!String.IsNullOrEmpty(prop.Name) && prop.IsRequired && !readProps.ContainsKey (prop)) { 400 PropertyInformation p = ElementInformation.Properties [prop.Name]; 401 if (p == null) { 402 object val = OnRequiredPropertyNotFound (prop.Name); 403 if (!object.Equals (val, prop.DefaultValue)) { 404 prop.Value = val; 405 prop.IsModified = false; 406 } 407 } 408 } 409 410 PostDeserialize (); 411 } 412 OnDeserializeUnrecognizedAttribute(string name, string value)413 protected virtual bool OnDeserializeUnrecognizedAttribute (string name, string value) 414 { 415 return false; 416 } 417 OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)418 protected virtual bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader) 419 { 420 return false; 421 } 422 OnRequiredPropertyNotFound(string name)423 protected virtual object OnRequiredPropertyNotFound (string name) 424 { 425 throw new ConfigurationErrorsException ("Required attribute '" + name + "' not found."); 426 } 427 PreSerialize(XmlWriter writer)428 protected virtual void PreSerialize (XmlWriter writer) 429 { 430 } 431 PostDeserialize()432 protected virtual void PostDeserialize () 433 { 434 } 435 InitializeDefault()436 protected internal virtual void InitializeDefault () 437 { 438 } 439 IsModified()440 protected internal virtual bool IsModified () 441 { 442 if (modified) 443 return true; 444 445 foreach (PropertyInformation prop in ElementInformation.Properties) { 446 if (!prop.IsElement) 447 continue; 448 var element = prop.Value as ConfigurationElement; 449 if ((element == null) || !element.IsModified ()) 450 continue; 451 452 modified = true; 453 break; 454 } 455 456 return modified; 457 } 458 SetReadOnly()459 protected internal virtual void SetReadOnly () 460 { 461 readOnly = true; 462 } 463 IsReadOnly()464 public virtual bool IsReadOnly () 465 { 466 return readOnly; 467 } 468 Reset(ConfigurationElement parentElement)469 protected internal virtual void Reset (ConfigurationElement parentElement) 470 { 471 elementPresent = false; 472 473 if (parentElement != null) 474 ElementInformation.Reset (parentElement.ElementInformation); 475 else 476 InitializeDefault (); 477 } 478 ResetModified()479 protected internal virtual void ResetModified () 480 { 481 modified = false; 482 483 foreach (PropertyInformation p in ElementInformation.Properties) { 484 p.IsModified = false; 485 486 var element = p.Value as ConfigurationElement; 487 if (element != null) 488 element.ResetModified (); 489 } 490 } 491 SerializeElement(XmlWriter writer, bool serializeCollectionKey)492 protected internal virtual bool SerializeElement (XmlWriter writer, bool serializeCollectionKey) 493 { 494 PreSerialize (writer); 495 496 if (serializeCollectionKey) { 497 ConfigurationPropertyCollection props = GetKeyProperties (); 498 foreach (ConfigurationProperty prop in props) 499 writer.WriteAttributeString (prop.Name, prop.ConvertToString (this[prop.Name])); 500 return props.Count > 0; 501 } 502 503 bool wroteData = false; 504 505 foreach (PropertyInformation prop in ElementInformation.Properties) 506 { 507 if (prop.IsElement) 508 continue; 509 510 if (saveContext == null) 511 throw new InvalidOperationException (); 512 if (!saveContext.HasValue (prop)) 513 continue; 514 515 writer.WriteAttributeString (prop.Name, prop.GetStringValue ()); 516 wroteData = true; 517 } 518 519 foreach (PropertyInformation prop in ElementInformation.Properties) 520 { 521 if (!prop.IsElement) 522 continue; 523 524 ConfigurationElement val = (ConfigurationElement) prop.Value; 525 if (val != null) 526 wroteData = val.SerializeToXmlElement (writer, prop.Name) || wroteData; 527 } 528 return wroteData; 529 } 530 SerializeToXmlElement( XmlWriter writer, string elementName)531 protected internal virtual bool SerializeToXmlElement ( 532 XmlWriter writer, string elementName) 533 { 534 if (saveContext == null) 535 throw new InvalidOperationException (); 536 if (!saveContext.HasValues ()) 537 return false; 538 539 if (elementName != null && elementName != "") 540 writer.WriteStartElement (elementName); 541 bool res = SerializeElement (writer, false); 542 if (elementName != null && elementName != "") 543 writer.WriteEndElement (); 544 return res; 545 } 546 Unmerge( ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode saveMode)547 protected internal virtual void Unmerge ( 548 ConfigurationElement sourceElement, ConfigurationElement parentElement, 549 ConfigurationSaveMode saveMode) 550 { 551 if (parentElement != null && sourceElement.GetType() != parentElement.GetType()) 552 throw new ConfigurationErrorsException ("Can't unmerge two elements of different type"); 553 554 bool isMinimalOrModified = saveMode == ConfigurationSaveMode.Minimal || 555 saveMode == ConfigurationSaveMode.Modified; 556 557 foreach (PropertyInformation prop in sourceElement.ElementInformation.Properties) 558 { 559 if (prop.ValueOrigin == PropertyValueOrigin.Default) 560 continue; 561 562 PropertyInformation unmergedProp = ElementInformation.Properties [prop.Name]; 563 564 object sourceValue = prop.Value; 565 if (parentElement == null || !parentElement.HasValue (prop.Name)) { 566 unmergedProp.Value = sourceValue; 567 continue; 568 } 569 570 if (sourceValue == null) 571 continue; 572 573 object parentValue = parentElement [prop.Name]; 574 if (!prop.IsElement) { 575 if (!object.Equals (sourceValue, parentValue) || 576 (saveMode == ConfigurationSaveMode.Full) || 577 (saveMode == ConfigurationSaveMode.Modified && prop.ValueOrigin == PropertyValueOrigin.SetHere)) 578 unmergedProp.Value = sourceValue; 579 continue; 580 } 581 582 var sourceElementValue = (ConfigurationElement) sourceValue; 583 if (isMinimalOrModified && !sourceElementValue.IsModified ()) 584 continue; 585 if (parentValue == null) { 586 unmergedProp.Value = sourceValue; 587 continue; 588 } 589 590 var parentElementValue = (ConfigurationElement) parentValue; 591 ConfigurationElement copy = (ConfigurationElement) unmergedProp.Value; 592 copy.Unmerge (sourceElementValue, parentElementValue, saveMode); 593 } 594 } 595 HasValue(string propName)596 internal bool HasValue (string propName) 597 { 598 PropertyInformation info = ElementInformation.Properties [propName]; 599 return info != null && info.ValueOrigin != PropertyValueOrigin.Default; 600 } 601 IsReadFromConfig(string propName)602 internal bool IsReadFromConfig (string propName) 603 { 604 PropertyInformation info = ElementInformation.Properties [propName]; 605 return info != null && info.ValueOrigin == PropertyValueOrigin.SetHere; 606 } 607 608 internal bool IsElementPresent 609 { 610 get { return elementPresent; } 611 } 612 ValidateValue(ConfigurationProperty p, string value)613 void ValidateValue (ConfigurationProperty p, string value) 614 { 615 ConfigurationValidatorBase validator; 616 if (p == null || (validator = p.Validator) == null) 617 return; 618 619 if (!validator.CanValidate (p.Type)) 620 throw new ConfigurationErrorsException ( 621 String.Format ("Validator does not support type {0}", p.Type)); 622 validator.Validate (p.ConvertFromString (value)); 623 } 624 625 /* 626 * FIXME: LAMESPEC 627 * 628 * SerializeElement() and SerializeToXmlElement() need to emit different output 629 * based on the ConfigurationSaveMode that's being used. Unfortunately, neither 630 * of these methods take it as an argument and there seems to be no documented way 631 * how to get it. 632 * 633 * The parent element is needed because the element could be set to a different 634 * than the default value in a parent configuration file, then set locally to that 635 * same value. This makes the element appear locally modified (so it's included 636 * with ConfigurationSaveMode.Modified), but it should not be emitted with 637 * ConfigurationSaveMode.Minimal. 638 * 639 * In theory, we could save it into some private field in Unmerge(), but the 640 * problem is that Unmerge() is kinda expensive and we also need a way of 641 * determining whether or not the configuration has changed in Configuration.Save(), 642 * prior to opening the output file for writing. 643 * 644 * There are two places from where HasValues() is called: 645 * a) From Configuration.Save() / SaveAs() to check whether the configuration needs 646 * to be saved. This check is done prior to opening the file for writing. 647 * b) From SerializeToXmlElement() to check whether to emit the element, using the 648 * parent and mode values from the cached 'SaveContext'. 649 * 650 */ 651 652 /* 653 * Check whether property 'prop' should be included in the serialized XML 654 * based on the current ConfigurationSaveMode. 655 */ HasValue(ConfigurationElement parent, PropertyInformation prop, ConfigurationSaveMode mode)656 internal bool HasValue (ConfigurationElement parent, PropertyInformation prop, 657 ConfigurationSaveMode mode) 658 { 659 if (prop.ValueOrigin == PropertyValueOrigin.Default) 660 return false; 661 662 if (mode == ConfigurationSaveMode.Modified && 663 prop.ValueOrigin == PropertyValueOrigin.SetHere && prop.IsModified) { 664 // Value has been modified locally, so we always emit it 665 // with ConfigurationSaveMode.Modified. 666 return true; 667 } 668 669 /* 670 * Ok, now we have to check whether we're different from the inherited 671 * value - which could either be a value that's set in a parent 672 * configuration file or the default value. 673 */ 674 675 var hasParentValue = parent != null && parent.HasValue (prop.Name); 676 var parentOrDefault = hasParentValue ? parent [prop.Name] : prop.DefaultValue; 677 678 if (!prop.IsElement) 679 return !object.Equals (prop.Value, parentOrDefault); 680 681 /* 682 * Ok, it's an element that has been set in a parent configuration file. * 683 * Recursively call HasValues() to check whether it's been locally modified. 684 */ 685 var element = (ConfigurationElement) prop.Value; 686 var parentElement = (ConfigurationElement) parentOrDefault; 687 688 return element.HasValues (parentElement, mode); 689 } 690 691 /* 692 * Check whether this element should be included in the serialized XML 693 * based on the current ConfigurationSaveMode. 694 * 695 * The 'parent' value is needed to determine whether the element currently 696 * has a different value from what's been set in the parent configuration 697 * hierarchy. 698 */ HasValues(ConfigurationElement parent, ConfigurationSaveMode mode)699 internal virtual bool HasValues (ConfigurationElement parent, ConfigurationSaveMode mode) 700 { 701 if (mode == ConfigurationSaveMode.Full) 702 return true; 703 if (modified && (mode == ConfigurationSaveMode.Modified)) 704 return true; 705 706 foreach (PropertyInformation prop in ElementInformation.Properties) { 707 if (HasValue (parent, prop, mode)) 708 return true; 709 } 710 711 return false; 712 } 713 714 /* 715 * Cache the current 'parent' and 'mode' values for later use in SerializeToXmlElement() 716 * and SerializeElement(). 717 * 718 * Make sure to call base when overriding this in a derived class. 719 */ PrepareSave(ConfigurationElement parent, ConfigurationSaveMode mode)720 internal virtual void PrepareSave (ConfigurationElement parent, ConfigurationSaveMode mode) 721 { 722 saveContext = new SaveContext (this, parent, mode); 723 724 foreach (PropertyInformation prop in ElementInformation.Properties) 725 { 726 if (!prop.IsElement) 727 continue; 728 729 var elem = (ConfigurationElement)prop.Value; 730 if (parent == null || !parent.HasValue (prop.Name)) 731 elem.PrepareSave (null, mode); 732 else { 733 var parentValue = (ConfigurationElement)parent [prop.Name]; 734 elem.PrepareSave (parentValue, mode); 735 } 736 } 737 } 738 739 SaveContext saveContext; 740 741 class SaveContext { 742 public readonly ConfigurationElement Element; 743 public readonly ConfigurationElement Parent; 744 public readonly ConfigurationSaveMode Mode; 745 SaveContext(ConfigurationElement element, ConfigurationElement parent, ConfigurationSaveMode mode)746 public SaveContext (ConfigurationElement element, ConfigurationElement parent, 747 ConfigurationSaveMode mode) 748 { 749 this.Element = element; 750 this.Parent = parent; 751 this.Mode = mode; 752 } 753 HasValues()754 public bool HasValues () 755 { 756 if (Mode == ConfigurationSaveMode.Full) 757 return true; 758 return Element.HasValues (Parent, Mode); 759 } 760 HasValue(PropertyInformation prop)761 public bool HasValue (PropertyInformation prop) 762 { 763 if (Mode == ConfigurationSaveMode.Full) 764 return true; 765 return Element.HasValue (Parent, prop, Mode); 766 } 767 } 768 } 769 770 internal class ElementMap 771 { 772 static readonly Hashtable elementMaps = Hashtable.Synchronized (new Hashtable ()); 773 774 readonly ConfigurationPropertyCollection properties; 775 readonly ConfigurationCollectionAttribute collectionAttribute; 776 GetMap(Type t)777 public static ElementMap GetMap (Type t) 778 { 779 ElementMap map = elementMaps [t] as ElementMap; 780 if (map != null) return map; 781 map = new ElementMap (t); 782 elementMaps [t] = map; 783 return map; 784 } 785 ElementMap(Type t)786 public ElementMap (Type t) 787 { 788 properties = new ConfigurationPropertyCollection (); 789 790 collectionAttribute = Attribute.GetCustomAttribute (t, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; 791 792 PropertyInfo[] props = t.GetProperties (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance); 793 foreach (PropertyInfo prop in props) 794 { 795 ConfigurationPropertyAttribute at = Attribute.GetCustomAttribute (prop, typeof(ConfigurationPropertyAttribute)) as ConfigurationPropertyAttribute; 796 if (at == null) continue; 797 string name = at.Name != null ? at.Name : prop.Name; 798 799 ConfigurationValidatorAttribute validatorAttr = Attribute.GetCustomAttribute (prop, typeof (ConfigurationValidatorAttribute)) as ConfigurationValidatorAttribute; 800 ConfigurationValidatorBase validator = validatorAttr != null ? validatorAttr.ValidatorInstance : null; 801 802 TypeConverterAttribute convertAttr = (TypeConverterAttribute) Attribute.GetCustomAttribute (prop, typeof (TypeConverterAttribute)); 803 TypeConverter converter = convertAttr != null ? (TypeConverter) Activator.CreateInstance (Type.GetType (convertAttr.ConverterTypeName), true) : null; 804 ConfigurationProperty cp = new ConfigurationProperty (name, prop.PropertyType, at.DefaultValue, converter, validator, at.Options); 805 806 cp.CollectionAttribute = Attribute.GetCustomAttribute (prop, typeof(ConfigurationCollectionAttribute)) as ConfigurationCollectionAttribute; 807 properties.Add (cp); 808 } 809 } 810 811 public ConfigurationCollectionAttribute CollectionAttribute 812 { 813 get { return collectionAttribute; } 814 } 815 816 public bool HasProperties 817 { 818 get { return properties.Count > 0; } 819 } 820 821 public ConfigurationPropertyCollection Properties 822 { 823 get { 824 return properties; 825 } 826 } 827 } 828 } 829 830