2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Schema / Dbml / Adapter / EnumType.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System.Collections;\r
28 using System.Collections.Generic;\r
29 using System.Linq;\r
30 using System.Reflection;\r
31 using DbLinq.Util;\r
32 \r
33 namespace DbLinq.Schema.Dbml.Adapter\r
34 {\r
35 #if MONO_STRICT\r
36     internal\r
37 #else\r
38     public\r
39 #endif\r
40     class EnumType : IDictionary<string, int>, INamedType\r
41     {\r
42         private string name;\r
43         public string Name\r
44         {\r
45             get { return name; }\r
46             set\r
47             {\r
48                 name = value;\r
49                 UpdateMember();\r
50             }\r
51         }\r
52 \r
53         private readonly IDictionary<string, int> dictionary;\r
54         private readonly object owner;\r
55         private readonly MemberInfo memberInfo;\r
56 \r
57         internal static bool IsEnum(string literalType)\r
58         {\r
59             string enumName;\r
60             IDictionary<string, int> values;\r
61             return Extract(literalType, out enumName, out values);\r
62         }\r
63 \r
64         /// <summary>\r
65         /// Extracts enum name and value from a given string.\r
66         /// The string is in the following form:\r
67         /// enumName key1[=value1]{,keyN[=valueN]}\r
68         /// if enumName is 'enum', then the enum is anonymous\r
69         /// </summary>\r
70         /// <param name="literalType"></param>\r
71         /// <param name="enumName"></param>\r
72         /// <param name="values"></param>\r
73         /// <returns></returns>\r
74         private static bool Extract(string literalType, out string enumName, out IDictionary<string, int> values)\r
75         {\r
76             enumName = null;\r
77             values = new Dictionary<string, int>();\r
78 \r
79             if (string.IsNullOrEmpty(literalType))\r
80                 return false;\r
81 \r
82             var nameValues = literalType.Split(new[] { ' ' }, 2);\r
83             if (nameValues.Length == 2)\r
84             {\r
85                 // extract the name\r
86                 string name = nameValues[0].Trim();\r
87                 if (!name.IsIdentifier())\r
88                     return false;\r
89 \r
90                 // now extract the values\r
91                 IDictionary<string, int> readValues = new Dictionary<string, int>();\r
92                 int currentValue = 1;\r
93                 var keyValues = nameValues[1].Split(',');\r
94                 foreach (var keyValue in keyValues)\r
95                 {\r
96                     // a value may indicate its numeric equivalent, or not (in this case, we work the same way as C# enums, with an implicit counter)\r
97                     var keyValueParts = keyValue.Split(new[] { '=' }, 2);\r
98                     var key = keyValueParts[0].Trim();\r
99 \r
100                     if (!key.IsIdentifier())\r
101                         return false;\r
102 \r
103                     if (keyValueParts.Length > 1)\r
104                     {\r
105                         if (!int.TryParse(keyValueParts[1], out currentValue))\r
106                             return false;\r
107                     }\r
108                     readValues[key] = currentValue++;\r
109                 }\r
110                 if (name == "enum")\r
111                     enumName = string.Empty;\r
112                 else\r
113                     enumName = name;\r
114                 values = readValues;\r
115                 return true;\r
116             }\r
117             return false;\r
118         }\r
119 \r
120         /// <summary>\r
121         /// Does the opposite: creates a literal string from values\r
122         /// </summary>\r
123         private void UpdateMember()\r
124         {\r
125             var pairs = from kvp in dictionary orderby kvp.Value select kvp;\r
126             int currentValue = 1;\r
127             var keyValues = new List<string>();\r
128             foreach (var pair in pairs)\r
129             {\r
130                 string keyValue;\r
131                 if (pair.Value == currentValue)\r
132                     keyValue = pair.Key;\r
133                 else\r
134                 {\r
135                     currentValue = pair.Value;\r
136                     keyValue = string.Format("{0}={1}", pair.Key, pair.Value);\r
137                 }\r
138                 keyValues.Add(keyValue);\r
139                 currentValue++;\r
140             }\r
141             string literalType = string.IsNullOrEmpty(Name) ? "enum" : Name;\r
142             literalType += " ";\r
143             literalType += string.Join(", ", keyValues.ToArray());\r
144             MemberInfoExtensions.SetMemberValue(memberInfo, owner, literalType);\r
145         }\r
146 \r
147         internal EnumType(object owner, MemberInfo memberInfo)\r
148         {\r
149             this.owner = owner;\r
150             this.memberInfo = memberInfo;\r
151             string name;\r
152             Extract((string)memberInfo.GetMemberValue(owner), out name, out dictionary);\r
153             Name = name;\r
154         }\r
155 \r
156         #region IDictionary implementation\r
157 \r
158         public void Add(KeyValuePair<string, int> item)\r
159         {\r
160             dictionary.Add(item);\r
161             UpdateMember();\r
162         }\r
163 \r
164         public void Clear()\r
165         {\r
166             dictionary.Clear();\r
167             UpdateMember();\r
168         }\r
169 \r
170         public bool Contains(KeyValuePair<string, int> item)\r
171         {\r
172             return dictionary.Contains(item);\r
173         }\r
174 \r
175         public void CopyTo(KeyValuePair<string, int>[] array, int arrayIndex)\r
176         {\r
177             dictionary.CopyTo(array, arrayIndex);\r
178         }\r
179 \r
180         public bool Remove(KeyValuePair<string, int> item)\r
181         {\r
182             bool removed = dictionary.Remove(item);\r
183             UpdateMember();\r
184             return removed;\r
185         }\r
186 \r
187         public int Count\r
188         {\r
189             get { return dictionary.Count; }\r
190         }\r
191 \r
192         public bool IsReadOnly\r
193         {\r
194             get { return dictionary.IsReadOnly; }\r
195         }\r
196 \r
197         public bool ContainsKey(string key)\r
198         {\r
199             return dictionary.ContainsKey(key);\r
200         }\r
201 \r
202         public void Add(string key, int value)\r
203         {\r
204             dictionary.Add(key, value);\r
205             UpdateMember();\r
206         }\r
207 \r
208         public bool Remove(string key)\r
209         {\r
210             bool removed = dictionary.Remove(key);\r
211             UpdateMember();\r
212             return removed;\r
213         }\r
214 \r
215         public bool TryGetValue(string key, out int value)\r
216         {\r
217             return dictionary.TryGetValue(key, out value);\r
218         }\r
219 \r
220         public int this[string key]\r
221         {\r
222             get { return dictionary[key]; }\r
223             set\r
224             {\r
225                 dictionary[key] = value;\r
226                 UpdateMember();\r
227             }\r
228         }\r
229 \r
230         public ICollection<string> Keys\r
231         {\r
232             get { return dictionary.Keys; }\r
233         }\r
234 \r
235         public ICollection<int> Values\r
236         {\r
237             get { return dictionary.Values; }\r
238         }\r
239 \r
240         IEnumerator IEnumerable.GetEnumerator()\r
241         {\r
242             return ((IEnumerable<KeyValuePair<string, int>>)this).GetEnumerator();\r
243         }\r
244 \r
245         public IEnumerator<KeyValuePair<string, int>> GetEnumerator()\r
246         {\r
247             return dictionary.GetEnumerator();\r
248         }\r
249 \r
250         #endregion\r
251     }\r
252 }