2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[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     public\r
37 #endif\r
38     class EnumType : IDictionary<string, int>, INamedType\r
39     {\r
40         private string name;\r
41         public string Name\r
42         {\r
43             get { return name; }\r
44             set\r
45             {\r
46                 name = value;\r
47                 UpdateMember();\r
48             }\r
49         }\r
50 \r
51         private readonly IDictionary<string, int> dictionary;\r
52         private readonly object owner;\r
53         private readonly MemberInfo memberInfo;\r
54 \r
55         internal static bool IsEnum(string literalType)\r
56         {\r
57             string enumName;\r
58             IDictionary<string, int> values;\r
59             return Extract(literalType, out enumName, out values);\r
60         }\r
61 \r
62         /// <summary>\r
63         /// Extracts enum name and value from a given string.\r
64         /// The string is in the following form:\r
65         /// enumName key1[=value1]{,keyN[=valueN]}\r
66         /// if enumName is 'enum', then the enum is anonymous\r
67         /// </summary>\r
68         /// <param name="literalType"></param>\r
69         /// <param name="enumName"></param>\r
70         /// <param name="values"></param>\r
71         /// <returns></returns>\r
72         private static bool Extract(string literalType, out string enumName, out IDictionary<string, int> values)\r
73         {\r
74             enumName = null;\r
75             values = new Dictionary<string, int>();\r
76 \r
77             if (string.IsNullOrEmpty(literalType))\r
78                 return false;\r
79 \r
80             var nameValues = literalType.Split(new[] { ' ' }, 2);\r
81             if (nameValues.Length == 2)\r
82             {\r
83                 // extract the name\r
84                 string name = nameValues[0].Trim();\r
85                 if (!name.IsIdentifier())\r
86                     return false;\r
87 \r
88                 // now extract the values\r
89                 IDictionary<string, int> readValues = new Dictionary<string, int>();\r
90                 int currentValue = 1;\r
91                 var keyValues = nameValues[1].Split(',');\r
92                 foreach (var keyValue in keyValues)\r
93                 {\r
94                     // 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
95                     var keyValueParts = keyValue.Split(new[] { '=' }, 2);\r
96                     var key = keyValueParts[0].Trim();\r
97 \r
98                     if (!key.IsIdentifier())\r
99                         return false;\r
100 \r
101                     if (keyValueParts.Length > 1)\r
102                     {\r
103                         if (!int.TryParse(keyValueParts[1], out currentValue))\r
104                             return false;\r
105                     }\r
106                     readValues[key] = currentValue++;\r
107                 }\r
108                 if (name == "enum")\r
109                     enumName = string.Empty;\r
110                 else\r
111                     enumName = name;\r
112                 values = readValues;\r
113                 return true;\r
114             }\r
115             return false;\r
116         }\r
117 \r
118         /// <summary>\r
119         /// Does the opposite: creates a literal string from values\r
120         /// </summary>\r
121         private void UpdateMember()\r
122         {\r
123             var pairs = from kvp in dictionary orderby kvp.Value select kvp;\r
124             int currentValue = 1;\r
125             var keyValues = new List<string>();\r
126             foreach (var pair in pairs)\r
127             {\r
128                 string keyValue;\r
129                 if (pair.Value == currentValue)\r
130                     keyValue = pair.Key;\r
131                 else\r
132                 {\r
133                     currentValue = pair.Value;\r
134                     keyValue = string.Format("{0}={1}", pair.Key, pair.Value);\r
135                 }\r
136                 keyValues.Add(keyValue);\r
137                 currentValue++;\r
138             }\r
139             string literalType = string.IsNullOrEmpty(Name) ? "enum" : Name;\r
140             literalType += " ";\r
141             literalType += string.Join(", ", keyValues.ToArray());\r
142             MemberInfoExtensions.SetMemberValue(memberInfo, owner, literalType);\r
143         }\r
144 \r
145         internal EnumType(object owner, MemberInfo memberInfo)\r
146         {\r
147             this.owner = owner;\r
148             this.memberInfo = memberInfo;\r
149             string name;\r
150             Extract((string)memberInfo.GetMemberValue(owner), out name, out dictionary);\r
151             Name = name;\r
152         }\r
153 \r
154         #region IDictionary implementation\r
155 \r
156         public void Add(KeyValuePair<string, int> item)\r
157         {\r
158             dictionary.Add(item);\r
159             UpdateMember();\r
160         }\r
161 \r
162         public void Clear()\r
163         {\r
164             dictionary.Clear();\r
165             UpdateMember();\r
166         }\r
167 \r
168         public bool Contains(KeyValuePair<string, int> item)\r
169         {\r
170             return dictionary.Contains(item);\r
171         }\r
172 \r
173         public void CopyTo(KeyValuePair<string, int>[] array, int arrayIndex)\r
174         {\r
175             dictionary.CopyTo(array, arrayIndex);\r
176         }\r
177 \r
178         public bool Remove(KeyValuePair<string, int> item)\r
179         {\r
180             bool removed = dictionary.Remove(item);\r
181             UpdateMember();\r
182             return removed;\r
183         }\r
184 \r
185         public int Count\r
186         {\r
187             get { return dictionary.Count; }\r
188         }\r
189 \r
190         public bool IsReadOnly\r
191         {\r
192             get { return dictionary.IsReadOnly; }\r
193         }\r
194 \r
195         public bool ContainsKey(string key)\r
196         {\r
197             return dictionary.ContainsKey(key);\r
198         }\r
199 \r
200         public void Add(string key, int value)\r
201         {\r
202             dictionary.Add(key, value);\r
203             UpdateMember();\r
204         }\r
205 \r
206         public bool Remove(string key)\r
207         {\r
208             bool removed = dictionary.Remove(key);\r
209             UpdateMember();\r
210             return removed;\r
211         }\r
212 \r
213         public bool TryGetValue(string key, out int value)\r
214         {\r
215             return dictionary.TryGetValue(key, out value);\r
216         }\r
217 \r
218         public int this[string key]\r
219         {\r
220             get { return dictionary[key]; }\r
221             set\r
222             {\r
223                 dictionary[key] = value;\r
224                 UpdateMember();\r
225             }\r
226         }\r
227 \r
228         public ICollection<string> Keys\r
229         {\r
230             get { return dictionary.Keys; }\r
231         }\r
232 \r
233         public ICollection<int> Values\r
234         {\r
235             get { return dictionary.Values; }\r
236         }\r
237 \r
238         IEnumerator IEnumerable.GetEnumerator()\r
239         {\r
240             return ((IEnumerable<KeyValuePair<string, int>>)this).GetEnumerator();\r
241         }\r
242 \r
243         public IEnumerator<KeyValuePair<string, int>> GetEnumerator()\r
244         {\r
245             return dictionary.GetEnumerator();\r
246         }\r
247 \r
248         #endregion\r
249     }\r
250 }