ConfigurationSettings.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. //
  2. // System.Configuration.ConfigurationSettings.cs
  3. //
  4. // Author:
  5. // Christopher Podurgiel ([email protected])
  6. // Gonzalo Paniagua Javier ([email protected])
  7. // Eric Lindvall ([email protected])
  8. //
  9. // (c) Christopher Podurgiel
  10. // (c) 2002 Ximian, Inc. (http://www.ximian.com)
  11. // (c) 2003 Novell, Inc. (http://www.novell.com)
  12. //
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining
  15. // a copy of this software and associated documentation files (the
  16. // "Software"), to deal in the Software without restriction, including
  17. // without limitation the rights to use, copy, modify, merge, publish,
  18. // distribute, sublicense, and/or sell copies of the Software, and to
  19. // permit persons to whom the Software is furnished to do so, subject to
  20. // the following conditions:
  21. //
  22. // The above copyright notice and this permission notice shall be
  23. // included in all copies or substantial portions of the Software.
  24. //
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. //
  33. using System;
  34. using System.Collections;
  35. using System.Collections.Specialized;
  36. using System.IO;
  37. using System.Runtime.CompilerServices;
  38. #if (XML_DEP)
  39. using System.Xml;
  40. using System.Xml.XPath;
  41. #endif
  42. namespace System.Configuration
  43. {
  44. public sealed class ConfigurationSettings
  45. {
  46. static IConfigurationSystem config;
  47. private ConfigurationSettings ()
  48. {
  49. }
  50. public static object GetConfig (string sectionName)
  51. {
  52. lock (typeof (ConfigurationSettings)) {
  53. if (config == null)
  54. config = DefaultConfig.GetInstance ();
  55. }
  56. return config.GetConfig (sectionName);
  57. }
  58. public static NameValueCollection AppSettings
  59. {
  60. get {
  61. object appSettings = GetConfig ("appSettings");
  62. if (appSettings == null)
  63. appSettings = new NameValueCollection ();
  64. return (NameValueCollection) appSettings;
  65. }
  66. }
  67. // Invoked from System.Web
  68. static IConfigurationSystem ChangeConfigurationSystem (IConfigurationSystem newSystem)
  69. {
  70. if (newSystem == null)
  71. throw new ArgumentNullException ("newSystem");
  72. lock (typeof (ConfigurationSettings)) {
  73. if (config == null)
  74. config = DefaultConfig.GetInstance ();
  75. IConfigurationSystem old = config;
  76. config = newSystem;
  77. return old;
  78. }
  79. }
  80. }
  81. //
  82. // class DefaultConfig: read configuration from machine.config file and application
  83. // config file if available.
  84. //
  85. class DefaultConfig : IConfigurationSystem
  86. {
  87. static DefaultConfig instance;
  88. ConfigurationData config;
  89. private DefaultConfig ()
  90. {
  91. }
  92. public static DefaultConfig GetInstance ()
  93. {
  94. lock (typeof (DefaultConfig)) {
  95. if (instance == null)
  96. instance = new DefaultConfig ();
  97. }
  98. return instance;
  99. }
  100. public object GetConfig (string sectionName)
  101. {
  102. Init ();
  103. return config.GetConfig (sectionName);
  104. }
  105. public void Init ()
  106. {
  107. lock (this) {
  108. if (config != null)
  109. return;
  110. ConfigurationData data = new ConfigurationData ();
  111. if (!data.Load (GetMachineConfigPath ()))
  112. throw new ConfigurationException ("Cannot find " + GetMachineConfigPath ());
  113. string appfile = GetAppConfigPath ();
  114. if (appfile == null) {
  115. config = data;
  116. return;
  117. }
  118. ConfigurationData appData = new ConfigurationData (data);
  119. if (appData.Load (appfile))
  120. config = appData;
  121. else
  122. config = data;
  123. }
  124. }
  125. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  126. extern private static string get_machine_config_path ();
  127. internal static string GetMachineConfigPath ()
  128. {
  129. return get_machine_config_path ();
  130. }
  131. private static string GetAppConfigPath ()
  132. {
  133. AppDomainSetup currentInfo = AppDomain.CurrentDomain.SetupInformation;
  134. string configFile = currentInfo.ConfigurationFile;
  135. if (configFile == null || configFile.Length == 0)
  136. return null;
  137. return configFile;
  138. }
  139. }
  140. enum AllowDefinition
  141. {
  142. Everywhere,
  143. MachineOnly,
  144. MachineToApplication
  145. }
  146. class SectionData
  147. {
  148. public readonly string SectionName;
  149. public readonly string TypeName;
  150. public readonly bool AllowLocation;
  151. public readonly AllowDefinition AllowDefinition;
  152. public string FileName;
  153. public SectionData (string sectionName, string typeName,
  154. bool allowLocation, AllowDefinition allowDefinition)
  155. {
  156. SectionName = sectionName;
  157. TypeName = typeName;
  158. AllowLocation = allowLocation;
  159. AllowDefinition = allowDefinition;
  160. }
  161. }
  162. class ConfigurationData
  163. {
  164. ConfigurationData parent;
  165. Hashtable factories;
  166. Hashtable pending;
  167. string fileName;
  168. static object removedMark = new object ();
  169. static object groupMark = new object ();
  170. static object emptyMark = new object ();
  171. Hashtable cache;
  172. Hashtable FileCache {
  173. get {
  174. if (cache != null)
  175. return cache;
  176. cache = new Hashtable ();
  177. return cache;
  178. }
  179. }
  180. public ConfigurationData () : this (null)
  181. {
  182. }
  183. public ConfigurationData (ConfigurationData parent)
  184. {
  185. this.parent = (parent == this) ? null : parent;
  186. factories = new Hashtable ();
  187. }
  188. public bool Load (string fileName)
  189. {
  190. this.fileName = fileName;
  191. if (fileName == null || !File.Exists (fileName))
  192. return false;
  193. #if (XML_DEP)
  194. XmlTextReader reader = null;
  195. try {
  196. FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read);
  197. reader = new XmlTextReader (fs);
  198. InitRead (reader);
  199. ReadConfigFile (reader);
  200. } catch (ConfigurationException) {
  201. throw;
  202. } catch (Exception e) {
  203. throw new ConfigurationException ("Error reading " + fileName, e);
  204. } finally {
  205. if (reader != null)
  206. reader.Close();
  207. }
  208. #endif
  209. return true;
  210. }
  211. object GetHandler (string sectionName)
  212. {
  213. lock (factories) {
  214. object o = factories [sectionName];
  215. if (o == null || o == removedMark) {
  216. if (parent != null)
  217. return parent.GetHandler (sectionName);
  218. return null;
  219. }
  220. if (o is IConfigurationSectionHandler)
  221. return (IConfigurationSectionHandler) o;
  222. o = CreateNewHandler (sectionName, (SectionData) o);
  223. factories [sectionName] = o;
  224. return o;
  225. }
  226. }
  227. object CreateNewHandler (string sectionName, SectionData section)
  228. {
  229. Type t = Type.GetType (section.TypeName);
  230. if (t == null)
  231. throw new ConfigurationException ("Cannot get Type for " + section.TypeName);
  232. Type iconfig = typeof (IConfigurationSectionHandler);
  233. if (!iconfig.IsAssignableFrom (t))
  234. throw new ConfigurationException (sectionName + " does not implement " + iconfig);
  235. object o = Activator.CreateInstance (t, true);
  236. if (o == null)
  237. throw new ConfigurationException ("Cannot get instance for " + t);
  238. return o;
  239. }
  240. #if (XML_DEP)
  241. XmlDocument GetInnerDoc (XmlDocument doc, int i, string [] sectionPath)
  242. {
  243. if (++i >= sectionPath.Length)
  244. return doc;
  245. if (doc.DocumentElement == null)
  246. return null;
  247. XmlNode node = doc.DocumentElement.FirstChild;
  248. while (node != null) {
  249. if (node.Name == sectionPath [i]) {
  250. ConfigXmlDocument result = new ConfigXmlDocument ();
  251. result.Load (new StringReader (node.OuterXml));
  252. return GetInnerDoc (result, i, sectionPath);
  253. }
  254. node = node.NextSibling;
  255. }
  256. return null;
  257. }
  258. XmlDocument GetDocumentForSection (string sectionName)
  259. {
  260. ConfigXmlDocument doc = new ConfigXmlDocument ();
  261. if (pending == null)
  262. return doc;
  263. string [] sectionPath = sectionName.Split ('/');
  264. string outerxml = pending [sectionPath [0]] as string;
  265. if (outerxml == null)
  266. return doc;
  267. StringReader reader = new StringReader (outerxml);
  268. XmlTextReader rd = new XmlTextReader (reader);
  269. rd.MoveToContent ();
  270. doc.LoadSingleElement (fileName, rd);
  271. return GetInnerDoc (doc, 0, sectionPath);
  272. }
  273. object GetConfigInternal (string sectionName)
  274. {
  275. object handler = GetHandler (sectionName);
  276. IConfigurationSectionHandler iconf = handler as IConfigurationSectionHandler;
  277. if (iconf == null)
  278. return handler;
  279. object parentConfig = null;
  280. if (parent != null)
  281. parentConfig = parent.GetConfig (sectionName);
  282. XmlDocument doc = GetDocumentForSection (sectionName);
  283. if (doc == null || doc.DocumentElement == null)
  284. return parentConfig;
  285. return iconf.Create (parentConfig, fileName, doc.DocumentElement);
  286. }
  287. #else
  288. object GetConfigInternal (string sectionName)
  289. {
  290. return null;
  291. }
  292. #endif
  293. public object GetConfig (string sectionName)
  294. {
  295. object config;
  296. lock (this) {
  297. config = this.FileCache [sectionName];
  298. }
  299. if (config == emptyMark)
  300. return null;
  301. if (config != null)
  302. return config;
  303. lock (this) {
  304. config = GetConfigInternal (sectionName);
  305. this.FileCache [sectionName] = (config == null) ? emptyMark : config;
  306. }
  307. return config;
  308. }
  309. private object LookForFactory (string key)
  310. {
  311. object o = factories [key];
  312. if (o != null)
  313. return o;
  314. if (parent != null)
  315. return parent.LookForFactory (key);
  316. return null;
  317. }
  318. #if (XML_DEP)
  319. private void InitRead (XmlTextReader reader)
  320. {
  321. reader.MoveToContent ();
  322. if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
  323. ThrowException ("Configuration file does not have a valid root element", reader);
  324. if (reader.HasAttributes)
  325. ThrowException ("Unrecognized attribute in root element", reader);
  326. MoveToNextElement (reader);
  327. }
  328. private void MoveToNextElement (XmlTextReader reader)
  329. {
  330. while (reader.Read ()) {
  331. XmlNodeType ntype = reader.NodeType;
  332. if (ntype == XmlNodeType.Element)
  333. return;
  334. if (ntype != XmlNodeType.Whitespace &&
  335. ntype != XmlNodeType.Comment &&
  336. ntype != XmlNodeType.SignificantWhitespace &&
  337. ntype != XmlNodeType.EndElement)
  338. ThrowException ("Unrecognized element", reader);
  339. }
  340. }
  341. private void ReadSection (XmlTextReader reader, string sectionName)
  342. {
  343. string attName;
  344. string nameValue = null;
  345. string typeValue = null;
  346. string allowLoc = null, allowDef = null;
  347. bool allowLocation = true;
  348. AllowDefinition allowDefinition = AllowDefinition.Everywhere;
  349. while (reader.MoveToNextAttribute ()) {
  350. attName = reader.Name;
  351. if (attName == null)
  352. continue;
  353. if (attName == "allowLocation") {
  354. if (allowLoc != null)
  355. ThrowException ("Duplicated allowLocation attribute.", reader);
  356. allowLoc = reader.Value;
  357. allowLocation = (allowLoc == "true");
  358. if (!allowLocation && allowLoc != "false")
  359. ThrowException ("Invalid attribute value", reader);
  360. continue;
  361. }
  362. if (attName == "allowDefinition") {
  363. if (allowDef != null)
  364. ThrowException ("Duplicated allowDefinition attribute.", reader);
  365. allowDef = reader.Value;
  366. try {
  367. allowDefinition = (AllowDefinition) Enum.Parse (
  368. typeof (AllowDefinition), allowDef);
  369. } catch {
  370. ThrowException ("Invalid attribute value", reader);
  371. }
  372. continue;
  373. }
  374. if (attName == "type") {
  375. if (typeValue != null)
  376. ThrowException ("Duplicated type attribute.", reader);
  377. typeValue = reader.Value;
  378. continue;
  379. }
  380. if (attName == "name") {
  381. if (nameValue != null)
  382. ThrowException ("Duplicated name attribute.", reader);
  383. nameValue = reader.Value;
  384. if (nameValue == "location")
  385. ThrowException ("location is a reserved section name", reader);
  386. continue;
  387. }
  388. ThrowException ("Unrecognized attribute.", reader);
  389. }
  390. if (nameValue == null || typeValue == null)
  391. ThrowException ("Required attribute missing", reader);
  392. if (sectionName != null)
  393. nameValue = sectionName + '/' + nameValue;
  394. reader.MoveToElement();
  395. object o = LookForFactory (nameValue);
  396. if (o != null && o != removedMark)
  397. ThrowException ("Already have a factory for " + nameValue, reader);
  398. SectionData section = new SectionData (nameValue, typeValue, allowLocation, allowDefinition);
  399. section.FileName = fileName;
  400. factories [nameValue] = section;
  401. MoveToNextElement (reader);
  402. }
  403. private void ReadRemoveSection (XmlTextReader reader, string sectionName)
  404. {
  405. if (!reader.MoveToNextAttribute () || reader.Name != "name")
  406. ThrowException ("Unrecognized attribute.", reader);
  407. string removeValue = reader.Value;
  408. if (removeValue == null || removeValue.Length == 0)
  409. ThrowException ("Empty name to remove", reader);
  410. reader.MoveToElement ();
  411. if (sectionName != null)
  412. removeValue = sectionName + '/' + removeValue;
  413. object o = LookForFactory (removeValue);
  414. if (o != null && o == removedMark)
  415. ThrowException ("No factory for " + removeValue, reader);
  416. factories [removeValue] = removedMark;
  417. MoveToNextElement (reader);
  418. }
  419. private void ReadSectionGroup (XmlTextReader reader, string configSection)
  420. {
  421. if (!reader.MoveToNextAttribute ())
  422. ThrowException ("sectionGroup must have a 'name' attribute.", reader);
  423. if (reader.Name != "name")
  424. ThrowException ("Unrecognized attribute.", reader);
  425. if (reader.MoveToNextAttribute ())
  426. ThrowException ("Unrecognized attribute.", reader);
  427. string value = reader.Value;
  428. if (value == "location")
  429. ThrowException ("location is a reserved section name", reader);
  430. if (configSection != null)
  431. value = configSection + '/' + value;
  432. object o = LookForFactory (value);
  433. if (o != null && o != removedMark && o != groupMark)
  434. ThrowException ("Already have a factory for " + value, reader);
  435. factories [value] = groupMark;
  436. MoveToNextElement (reader);
  437. ReadSections (reader, value);
  438. }
  439. private void ReadSections (XmlTextReader reader, string configSection)
  440. {
  441. int depth = reader.Depth;
  442. while (reader.Depth == depth) {
  443. string name = reader.Name;
  444. if (name == "section") {
  445. ReadSection (reader, configSection);
  446. continue;
  447. }
  448. if (name == "remove") {
  449. ReadRemoveSection (reader, configSection);
  450. continue;
  451. }
  452. if (name == "clear") {
  453. if (reader.HasAttributes)
  454. ThrowException ("Unrecognized attribute.", reader);
  455. factories.Clear ();
  456. MoveToNextElement (reader);
  457. continue;
  458. }
  459. if (name == "sectionGroup") {
  460. ReadSectionGroup (reader, configSection);
  461. continue;
  462. }
  463. ThrowException ("Unrecognized element: " + reader.Name, reader);
  464. }
  465. }
  466. void StorePending (string name, XmlTextReader reader)
  467. {
  468. if (pending == null)
  469. pending = new Hashtable ();
  470. pending [name] = reader.ReadOuterXml ();
  471. }
  472. private void ReadConfigFile (XmlTextReader reader)
  473. {
  474. int depth = reader.Depth;
  475. while (!reader.EOF && reader.Depth == depth) {
  476. string name = reader.Name;
  477. if (name == "configSections") {
  478. if (reader.HasAttributes)
  479. ThrowException ("Unrecognized attribute in <configSections>.", reader);
  480. MoveToNextElement (reader);
  481. if (reader.Depth > depth)
  482. ReadSections (reader, null);
  483. } else if (name != null && name != "") {
  484. StorePending (name, reader);
  485. MoveToNextElement (reader);
  486. } else {
  487. MoveToNextElement (reader);
  488. }
  489. }
  490. }
  491. private void ThrowException (string text, XmlTextReader reader)
  492. {
  493. throw new ConfigurationException (text, fileName, reader.LineNumber);
  494. }
  495. #endif
  496. }
  497. }