2009-04-24 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / System.Data.Linq / System.Data.Linq.Mapping / XmlMappingSource.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Stefan Klinger\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 #region grammar\r
28 /* ----------------\r
29 default namespace = "http://schemas.microsoft.com/linqtosql/mapping/2007"\r
30 grammar {\r
31 \r
32 start = element Database { Database }\r
33 \r
34 Database = {\r
35   element Table { Table }*,\r
36   element Function { Function }*,\r
37   attribute Name { text }?,\r
38   attribute Provider { text }?\r
39 }\r
40 \r
41 Table = {\r
42   element Type { Type },\r
43   attribute Name { text }?,\r
44   attribute Member { text }?\r
45 }\r
46 \r
47 Type = {\r
48   {\r
49     element Column { Column }* |\r
50     element Association { Association }*\r
51   }*,\r
52   element Type { Type }*,\r
53   attribute Name { text },\r
54   attribute InheritanceCode { text }?,\r
55   attribute IsInheritanceDefault { boolean }?\r
56 }\r
57 \r
58 Column = {\r
59   attribute Name { text }?,\r
60   attribute Member { text },\r
61   attribute Storage { text }?,\r
62   attribute DbType { text }?,\r
63   attribute IsPrimaryKey { boolean }?,\r
64   attribute IsDbGenerated { boolean }?,\r
65   attribute CanBeNull { boolean }?,\r
66   attribute UpdateCheck { UpdateCheck }?,\r
67   attribute IsDiscriminator { boolean }?,\r
68   attribute Expression { text }?,\r
69   attribute IsVersion { boolean }?,\r
70   attribute AutoSync { AutoSync}?\r
71 }\r
72 \r
73 Association = {\r
74   attribute Name { text }?,\r
75   attribute Member { text },\r
76   attribute Storage { text }?,\r
77   attribute ThisKey { text }?,\r
78   attribute OtherKey { text }?,\r
79   attribute IsForeignKey { boolean }?,\r
80   attribute IsUnique { boolean }?,\r
81   attribute DeleteRule { text }?,\r
82   attribute DeleteOnNull { boolean }?\r
83 }\r
84 \r
85 Function = {\r
86   element Parameter { Parameter }*,\r
87   {\r
88     element ElementType { Type }* |\r
89     element Return { Return }\r
90   },\r
91   attribute Name { text }?,\r
92   attribute Method { text },\r
93   attribute IsComposable { boolean }?\r
94 }\r
95 \r
96 Parameter = {\r
97   attribute Name { text }?,\r
98   attribute Parameter { text },\r
99   attribute DbType { text }?,\r
100   attribute Direction { ParameterDirection }?\r
101 }\r
102 \r
103 Return = attribute DbType { text}?\r
104 \r
105 UpdateCheck = "Always" | "Never" | "WhenChanged"\r
106 ParameterDirection = "In" | "Out" | "InOut"\r
107 AutoSync = "Never" | "OnInsert" | "OnUpdate" | "Always" | "Default"\r
108 \r
109 }\r
110 ---------------- */\r
111 #endregion\r
112 \r
113 using System;\r
114 using System.Collections.Generic;\r
115 using System.Collections.ObjectModel;\r
116 using System.IO;\r
117 using System.Linq;\r
118 using System.Reflection;\r
119 using System.Xml;\r
120 using DbLinq;\r
121 using DbLinq.Schema.Dbml;\r
122 using DbLinq.Util;\r
123 \r
124 #if MONO_STRICT\r
125 namespace System.Data.Linq.Mapping\r
126 #else\r
127 namespace DbLinq.Data.Linq.Mapping\r
128 #endif\r
129 {\r
130     public sealed class XmlMappingSource : MappingSource\r
131     {\r
132         DbmlDatabase db;\r
133 \r
134         XmlMappingSource(XmlReader reader)\r
135         {\r
136             db = new DbmlDatabase(reader);\r
137         }\r
138 \r
139         public static XmlMappingSource FromReader(XmlReader reader)\r
140         {\r
141             return new XmlMappingSource(reader);\r
142         }\r
143 \r
144         public static XmlMappingSource FromStream(Stream stream)\r
145         {\r
146             return FromReader(XmlReader.Create(stream));\r
147         }\r
148 \r
149         public static XmlMappingSource FromUrl(string url)\r
150         {\r
151             return FromReader(XmlReader.Create(url));\r
152         }\r
153 \r
154         public static XmlMappingSource FromXml(string xml)\r
155         {\r
156             return FromReader(XmlReader.Create(new StringReader(xml)));\r
157         }\r
158 \r
159         protected override MetaModel CreateModel(Type dataContextType)\r
160         {\r
161             return new XmlMetaModel(this, db, dataContextType);\r
162         }\r
163 \r
164 \r
165         abstract class DbmlItem\r
166         {\r
167             public const string DbmlNamespace = "http://schemas.microsoft.com/linqtosql/mapping/2007";\r
168 \r
169             public void ReadEmptyContent(XmlReader r, string name)\r
170             {\r
171                 if (r.IsEmptyElement)\r
172                     r.ReadStartElement(name, DbmlNamespace);\r
173                 else\r
174                 {\r
175                     r.ReadStartElement(name, DbmlNamespace);\r
176                     for (r.MoveToContent(); r.NodeType != XmlNodeType.EndElement; r.MoveToContent())\r
177                     {\r
178                         if (r.NamespaceURI != DbmlNamespace)\r
179                             r.Skip();\r
180                         throw UnexpectedItemError(r);\r
181                     }\r
182                     r.ReadEndElement();\r
183                 }\r
184             }\r
185             public bool GetBooleanAttribute(XmlReader r, string attributeName)\r
186             {\r
187                 return r.GetAttribute(attributeName) == "true";\r
188             }\r
189             public UpdateCheck GetUpdateCheckAttribute(XmlReader r, string attributeName)\r
190             {\r
191                 var s = r.GetAttribute(attributeName);\r
192                 return s != null ? (UpdateCheck)Enum.Parse(typeof(UpdateCheck), s) : default(UpdateCheck);\r
193             }\r
194             public AutoSync GetAutoSyncAttribute(XmlReader r, string attributeName)\r
195             {\r
196                 var s = r.GetAttribute(attributeName);\r
197                 return s != null ? (AutoSync)Enum.Parse(typeof(AutoSync), s) : default(AutoSync);\r
198             }\r
199             public T GetEnumAttribute<T>(XmlReader r, string attributeName)\r
200             {\r
201                 var s = r.GetAttribute(attributeName);\r
202                 return s != null ? (T)Enum.Parse(typeof(T), s) : default(T);\r
203             }\r
204             public XmlException UnexpectedItemError(XmlReader r)\r
205             {\r
206                 return new XmlException(String.Format("Unexpected dbml element '{0}'", r.LocalName));\r
207             }\r
208         }\r
209         class DbmlDatabase : DbmlItem\r
210         {\r
211             public string Name;\r
212             public string Provider;\r
213             public List<DbmlTable> Tables = new List<DbmlTable>();\r
214             public List<DbmlFunction> Functions = new List<DbmlFunction>();\r
215 \r
216             public DbmlDatabase(XmlReader r)\r
217             {\r
218                 r.MoveToContent();\r
219                 Name = r.GetAttribute("Name");\r
220                 Provider = r.GetAttribute("Provider");\r
221                 if (r.IsEmptyElement)\r
222                     r.ReadStartElement("Database", DbmlNamespace);\r
223                 else\r
224                 {\r
225                     r.ReadStartElement("Database", DbmlNamespace);\r
226                     for (r.MoveToContent(); r.NodeType != XmlNodeType.EndElement; r.MoveToContent())\r
227                     {\r
228                         if (r.NamespaceURI != DbmlNamespace)\r
229                             r.Skip();\r
230                         else\r
231                         {\r
232                             switch (r.LocalName)\r
233                             {\r
234                                 case "Table":\r
235                                     Tables.Add(new DbmlTable(r));\r
236                                     break;\r
237                                 case "Function":\r
238                                     Functions.Add(new DbmlFunction(r));\r
239                                     break;\r
240                                 default:\r
241                                     throw UnexpectedItemError(r);\r
242                             }\r
243                         }\r
244                     }\r
245                     r.ReadEndElement();\r
246                 }\r
247             }\r
248         }\r
249         class DbmlTable : DbmlItem\r
250         {\r
251             public DbmlType Type;\r
252             public string Name;\r
253             public string Member;\r
254 \r
255             public DbmlTable(XmlReader r)\r
256             {\r
257                 Name = r.GetAttribute("Name");\r
258                 Member = r.GetAttribute("Member");\r
259                 if (r.IsEmptyElement)\r
260                     r.ReadStartElement("Table", DbmlNamespace);\r
261                 else\r
262                 {\r
263                     r.ReadStartElement("Table", DbmlNamespace);\r
264                     for (r.MoveToContent(); r.NodeType != XmlNodeType.EndElement; r.MoveToContent())\r
265                     {\r
266                         if (r.NamespaceURI != DbmlNamespace)\r
267                             r.Skip();\r
268                         else\r
269                         {\r
270                             switch (r.LocalName)\r
271                             {\r
272                                 case "Type":\r
273                                     Type = new DbmlType(r);\r
274                                     break;\r
275                                 default:\r
276                                     throw UnexpectedItemError(r);\r
277                             }\r
278                         }\r
279                     }\r
280                     r.ReadEndElement();\r
281                 }\r
282             }\r
283         }\r
284         class DbmlType : DbmlItem\r
285         {\r
286             public List<DbmlColumn> Columns = new List<DbmlColumn>();\r
287             public List<DbmlAssociation> Associations = new List<DbmlAssociation>();\r
288             public List<DbmlType> Types = new List<DbmlType>();\r
289             public string Name;\r
290             public string InheritanceCode;\r
291             public bool IsInheritanceDefault;\r
292 \r
293             public DbmlType(XmlReader r)\r
294             {\r
295                 Name = r.GetAttribute("Name");\r
296                 InheritanceCode = r.GetAttribute("InheritanceCode");\r
297                 IsInheritanceDefault = GetBooleanAttribute(r, "IsInheritanceDefault");\r
298                 if (r.IsEmptyElement)\r
299                     r.ReadStartElement("Type", DbmlNamespace);\r
300                 else\r
301                 {\r
302                     r.ReadStartElement("Type", DbmlNamespace);\r
303                     for (r.MoveToContent(); r.NodeType != XmlNodeType.EndElement; r.MoveToContent())\r
304                     {\r
305                         if (r.NamespaceURI != DbmlNamespace)\r
306                             r.Skip();\r
307                         else\r
308                         {\r
309                             switch (r.LocalName)\r
310                             {\r
311                                 case "Column":\r
312                                     Columns.Add(new DbmlColumn(r));\r
313                                     break;\r
314                                 case "Association":\r
315                                     Associations.Add(new DbmlAssociation(r));\r
316                                     break;\r
317                                 case "Type":\r
318                                     Types.Add(new DbmlType(r));\r
319                                     break;\r
320                                 default:\r
321                                     throw UnexpectedItemError(r);\r
322                             }\r
323                         }\r
324                     }\r
325                     r.ReadEndElement();\r
326                 }\r
327             }\r
328         }\r
329 \r
330         class DbmlMemberBase : DbmlItem\r
331         {\r
332             public string Name;\r
333             public string Member;\r
334             public string Storage;\r
335         }\r
336 \r
337         class DbmlColumn : DbmlMemberBase\r
338         {\r
339             public string DbType;\r
340             public bool IsPrimaryKey;\r
341             public bool IsDbGenerated;\r
342             public bool CanBeNull;\r
343             public UpdateCheck UpdateCheck;\r
344             public bool IsDiscriminator;\r
345             public string Expression;\r
346             public bool IsVersion;\r
347             public AutoSync AutoSync;\r
348 \r
349             public DbmlColumn(XmlReader r)\r
350             {\r
351                 Member = r.GetAttribute("Member");\r
352                 Name = r.GetAttribute("Name") ?? Member;\r
353                 Storage = r.GetAttribute("Storage");\r
354                 DbType = r.GetAttribute("DbType");\r
355                 IsPrimaryKey = GetBooleanAttribute(r, "IsPrimaryKey");\r
356                 IsDbGenerated = GetBooleanAttribute(r, "IsDbGenerated");\r
357                 CanBeNull = GetBooleanAttribute(r, "CanBeNull");\r
358                 UpdateCheck = GetEnumAttribute<UpdateCheck>(r, "UpdateCheck");\r
359                 IsDiscriminator = GetBooleanAttribute(r, "IsDiscriminator");\r
360                 Expression = r.GetAttribute("Expression");\r
361                 IsVersion = GetBooleanAttribute(r, "IsVersion");\r
362                 AutoSync = GetEnumAttribute<AutoSync>(r, "AutoSync");\r
363                 ReadEmptyContent(r, "Column");\r
364             }\r
365         }\r
366         class DbmlAssociation : DbmlMemberBase\r
367         {\r
368             public string ThisKey;\r
369             public string OtherKey;\r
370             public bool IsForeignKey;\r
371             public bool IsUnique;\r
372             public string DeleteRule;\r
373             public bool DeleteOnNull;\r
374 \r
375             public DbmlAssociation(XmlReader r)\r
376             {\r
377                 Name = r.GetAttribute("Name");\r
378                 Member = r.GetAttribute("Member");\r
379                 Storage = r.GetAttribute("Storage");\r
380                 ThisKey = r.GetAttribute("ThisKey");\r
381                 OtherKey = r.GetAttribute("OtherKey");\r
382                 IsForeignKey = GetBooleanAttribute(r, "IsForeignKey");\r
383                 IsUnique = GetBooleanAttribute(r, "IsUnique");\r
384                 DeleteRule = r.GetAttribute("DeleteRule");\r
385                 DeleteOnNull = GetBooleanAttribute(r, "DeleteOnNull");\r
386                 ReadEmptyContent(r, "Association");\r
387             }\r
388         }\r
389         class DbmlFunction : DbmlItem\r
390         {\r
391             public string Name;\r
392             public string Method;\r
393             public bool IsComposable;\r
394             public List<DbmlParameter> Parameters = new List<DbmlParameter>();\r
395             public List<DbmlType> ElementTypes = new List<DbmlType>();\r
396             public DbmlReturn Return;\r
397 \r
398             public DbmlFunction(XmlReader r)\r
399             {\r
400                 Name = r.GetAttribute("Name");\r
401                 Method = r.GetAttribute("Method");\r
402                 IsComposable = GetBooleanAttribute(r, "IsComposable");\r
403                 if (r.IsEmptyElement)\r
404                     r.ReadStartElement("Function", DbmlNamespace);\r
405                 else\r
406                 {\r
407                     r.ReadStartElement("Function", DbmlNamespace);\r
408                     for (r.MoveToContent(); r.NodeType != XmlNodeType.EndElement; r.MoveToContent())\r
409                     {\r
410                         if (r.NamespaceURI != DbmlNamespace)\r
411                             r.Skip();\r
412                         else\r
413                         {\r
414                             switch (r.LocalName)\r
415                             {\r
416                                 case "Parameter":\r
417                                     Parameters.Add(new DbmlParameter(r));\r
418                                     break;\r
419                                 case "ElementType":\r
420                                     ElementTypes.Add(new DbmlType(r));\r
421                                     break;\r
422                                 case "Return":\r
423                                     Return = new DbmlReturn(r);\r
424                                     break;\r
425                                 default:\r
426                                     throw UnexpectedItemError(r);\r
427                             }\r
428                         }\r
429                     }\r
430                     r.ReadEndElement();\r
431                 }\r
432             }\r
433         }\r
434         class DbmlParameter : DbmlItem\r
435         {\r
436             public string Name;\r
437             public string Parameter;\r
438             public string DbType;\r
439             public ParameterDirection Direction;\r
440 \r
441             public DbmlParameter(XmlReader r)\r
442             {\r
443                 Name = r.GetAttribute("Name");\r
444                 Parameter = r.GetAttribute("Parameter");\r
445                 DbType = r.GetAttribute("DbType");\r
446                 Direction = GetEnumAttribute<ParameterDirection>(r, "Direction");\r
447                 ReadEmptyContent(r, "Parameter");\r
448             }\r
449         }\r
450         class DbmlReturn : DbmlItem\r
451         {\r
452             public string DbType;\r
453 \r
454             public DbmlReturn(XmlReader r)\r
455             {\r
456                 DbType = r.GetAttribute("DbType");\r
457                 ReadEmptyContent(r, "Return");\r
458             }\r
459         }\r
460 \r
461         class XmlMetaModel : MetaModel\r
462         {\r
463             MappingSource source;\r
464             DbmlDatabase d;\r
465             Type context_type;\r
466             MetaFunction[] functions;\r
467             MetaTable[] tables;\r
468             Dictionary<Type, XmlMetaType> types;\r
469 \r
470             public XmlMetaModel(MappingSource source, DbmlDatabase database, Type contextType)\r
471             {\r
472                 this.source = source;\r
473                 this.d = database;\r
474                 this.context_type = contextType;\r
475                 RegisterTypes();\r
476             }\r
477 \r
478             void RegisterTypes()\r
479             {\r
480                 types = new Dictionary<Type, XmlMetaType>();\r
481                 foreach (var t in d.Tables)\r
482                     RegisterTypeAndDescendants(t.Type);\r
483             }\r
484 \r
485             void RegisterTypeAndDescendants(DbmlType dt)\r
486             {\r
487 \r
488                 Type t = GetTypeFromName(dt.Name);\r
489                 if (t == null)\r
490                     throw new ArgumentException(String.Format("type '{0}' not found", dt.Name));\r
491                 if (types.ContainsKey(t))\r
492                     return;\r
493                 types.Add(t, new XmlMetaType(this, dt));\r
494                 foreach (var cdt in dt.Types)\r
495                     RegisterTypeAndDescendants(cdt);\r
496             }\r
497 \r
498             public override Type ContextType\r
499             {\r
500                 get { return context_type; }\r
501             }\r
502 \r
503             public override string DatabaseName\r
504             {\r
505                 get { return d.Name; }\r
506             }\r
507 \r
508             public override MappingSource MappingSource\r
509             {\r
510                 get { return source; }\r
511             }\r
512 \r
513             public override Type ProviderType\r
514             {\r
515                 get { return GetTypeFromName(d.Provider); }\r
516             }\r
517 \r
518             public override MetaFunction GetFunction(MethodInfo method)\r
519             {\r
520                 foreach (var f in GetFunctions())\r
521                     if (f.Method == method)\r
522                         return f;\r
523                 throw new ArgumentException(String.Format("Corresponding MetaFunction for method '{0}' was not found", method));\r
524             }\r
525 \r
526             public override IEnumerable<MetaFunction> GetFunctions()\r
527             {\r
528                 if (functions == null)\r
529                 {\r
530                     var l = new List<MetaFunction>();\r
531                     foreach (var f in d.Functions)\r
532                         l.Add(new XmlMetaFunction(this, f));\r
533                     functions = l.ToArray();\r
534                 }\r
535                 return functions;\r
536             }\r
537 \r
538             public Type GetTypeFromName(string name)\r
539             {\r
540                 string ns = context_type.Namespace;\r
541                 string full = !name.Contains('.') && !String.IsNullOrEmpty(ns) ? String.Concat(ns, ".", name) : name;\r
542                 var t = this.context_type.Assembly.GetType(full);\r
543                 if (t == null)\r
544                     throw new ArgumentException(String.Format("Type '{0}' was not found", full));\r
545                 return t;\r
546             }\r
547 \r
548             public override MetaType GetMetaType(Type type)\r
549             {\r
550                 if (!types.ContainsKey(type))\r
551                     throw new ArgumentException(String.Format("Type '{0}' is not found in the mapping", type));\r
552                 return types[type];\r
553             }\r
554 \r
555             public override MetaTable GetTable(Type rowType)\r
556             {\r
557                 foreach (var t in GetTables())\r
558                     if (t.RowType.Type == rowType)\r
559                         return t;\r
560                 //throw new ArgumentException(String.Format("Corresponding MetaTable for row type '{0}' was not found", rowType));\r
561                 return null;\r
562             }\r
563 \r
564             public override IEnumerable<MetaTable> GetTables()\r
565             {\r
566                 if (tables == null)\r
567                 {\r
568                     var l = new List<MetaTable>();\r
569                     foreach (var t in d.Tables)\r
570                         l.Add(new XmlMetaTable(this, t));\r
571                     tables = l.ToArray();\r
572                 }\r
573                 return tables;\r
574             }\r
575         }\r
576 \r
577         class XmlMetaParameter : MetaParameter\r
578         {\r
579             string dbtype, mapped_name;\r
580             ParameterInfo pi;\r
581 \r
582             public XmlMetaParameter(DbmlParameter p, ParameterInfo parameterInfo)\r
583                 : this(p.DbType, p.Parameter, parameterInfo)\r
584             {\r
585             }\r
586 \r
587             public XmlMetaParameter(string dbType, string mappedName, ParameterInfo parameterInfo)\r
588             {\r
589                 this.dbtype = dbType;\r
590                 this.mapped_name = mappedName;\r
591                 this.pi = parameterInfo;\r
592             }\r
593 \r
594             public override string DbType { get { return dbtype; } }\r
595             public override string MappedName { get { return mapped_name; } }\r
596             public override string Name { get { return Parameter.Name; } }\r
597             public override ParameterInfo Parameter { get { return pi; } }\r
598             public override Type ParameterType { get { return pi.ParameterType; } }\r
599         }\r
600 \r
601         class XmlMetaTable : MetaTable\r
602         {\r
603             public XmlMetaTable(XmlMetaModel model, DbmlTable table)\r
604             {\r
605                 this.model = model;\r
606                 this.t = table;\r
607                 foreach (var member in model.ContextType.GetMember(t.Member))\r
608                 {\r
609                     if (table_member != null)\r
610                         throw new ArgumentException(String.Format("The context type '{0}' contains non-identical member '{1}'", model.ContextType, t.Member));\r
611                     table_member = member;\r
612                 }\r
613                 if (table_member == null)\r
614                     table_member = GetFieldsAndProperties(model.ContextType).First(pi => pi.GetMemberType().IsGenericType &&\r
615                         pi.GetMemberType().GetGenericTypeDefinition() == typeof(Table<>) &&\r
616                         pi.GetMemberType().GetGenericArguments()[0] == model.GetTypeFromName(t.Type.Name));\r
617                 if (table_member == null)\r
618                     throw new ArgumentException(String.Format("The context type '{0}' does not contain member '{1}' which is specified in dbml", model.ContextType, t.Member));\r
619                 member_type = table_member.GetMemberType();\r
620                 if (member_type.GetGenericTypeDefinition() != typeof(Table<>))\r
621                     throw new ArgumentException(String.Format("The table member type was unexpected: '{0}'", member_type));\r
622                 var rt = member_type.GetGenericArguments()[0];\r
623                 row_type = model.GetMetaType(rt);\r
624                 if (row_type == null)\r
625                     throw new ArgumentException(String.Format("MetaType for '{0}' was not found", rt));\r
626             }\r
627             static IEnumerable<MemberInfo> GetFieldsAndProperties(Type type)\r
628             {\r
629                 foreach (var f in type.GetFields())\r
630                     yield return f;\r
631                 foreach (var p in type.GetProperties())\r
632                     yield return p;\r
633             }\r
634 \r
635             XmlMetaModel model;\r
636             DbmlTable t;\r
637             MemberInfo table_member;\r
638             Type member_type;\r
639             MetaType row_type;\r
640 \r
641             [DbLinqToDo]\r
642             public override MethodInfo DeleteMethod\r
643             {\r
644                 get { throw new NotImplementedException(); }\r
645             }\r
646             [DbLinqToDo]\r
647             public override MethodInfo InsertMethod\r
648             {\r
649                 get { throw new NotImplementedException(); }\r
650             }\r
651             public override MetaModel Model { get { return model; } }\r
652             public override MetaType RowType { get { return row_type; } }\r
653             Type MemberType { get { return member_type; } }\r
654             public override string TableName { get { return t.Name; } }\r
655             [DbLinqToDo]\r
656             public override MethodInfo UpdateMethod\r
657             {\r
658                 get { throw new NotImplementedException(); }\r
659             }\r
660 \r
661             // not used yet\r
662             MethodInfo GetMethod(TableFunction f)\r
663             {\r
664                 if (f == null)\r
665                     return null;\r
666                 foreach (var mf in model.GetFunctions())\r
667                     if (mf.Name == f.FunctionId)\r
668                         return mf.Method;\r
669                 return null;\r
670             }\r
671         }\r
672 \r
673         class XmlMetaType : MetaType\r
674         {\r
675             XmlMetaModel model;\r
676             DbmlType t;\r
677             ReadOnlyCollection<MetaAssociation> associations;\r
678             Type runtime_type;\r
679             ReadOnlyCollection<MetaDataMember> members, identity_members, persistent_members;\r
680 \r
681             public XmlMetaType(XmlMetaModel model, DbmlType type)\r
682             {\r
683                 this.model = model;\r
684                 this.t = type;\r
685                 runtime_type = model.GetTypeFromName(t.Name);\r
686                 int i = 0;\r
687                 var l = new List<MetaDataMember>();\r
688                 l.AddRange(Array.ConvertAll<DbmlColumn, MetaDataMember>(\r
689                     t.Columns.ToArray(), c => new XmlColumnMetaDataMember(model, this, c, i++)));\r
690                 members = new ReadOnlyCollection<MetaDataMember>(l);\r
691             }\r
692 \r
693             public override ReadOnlyCollection<MetaAssociation> Associations\r
694             {\r
695                 get\r
696                 {\r
697                     if (associations == null)\r
698                     {\r
699                         var l = new List<MetaAssociation>();\r
700                         // FIXME: Ordinal?\r
701                         foreach (var a in t.Associations)\r
702                             l.Add(new XmlMetaAssociation(this, new XmlAssociationMetaDataMember(model, this, a, -1), a));\r
703                         associations = new ReadOnlyCollection<MetaAssociation>(l.ToArray());\r
704                     }\r
705                     return associations;\r
706                 }\r
707             }\r
708             public override bool CanInstantiate { get { return !runtime_type.IsAbstract; } }\r
709             public override ReadOnlyCollection<MetaDataMember> DataMembers { get { return members; } }\r
710             public override MetaDataMember DBGeneratedIdentityMember\r
711             {\r
712                 get { return members.First(m => m.IsDbGenerated && m.IsPrimaryKey); }\r
713             }\r
714             [DbLinqToDo]\r
715             public override ReadOnlyCollection<MetaType> DerivedTypes\r
716             {\r
717                 get { throw new NotImplementedException(); }\r
718             }\r
719             public override MetaDataMember Discriminator\r
720             {\r
721                 get { return members.First(m => m.IsDiscriminator); }\r
722             }\r
723             public override bool HasAnyLoadMethod\r
724             {\r
725                 get { return members.Any(m => m.LoadMethod != null); }\r
726             }\r
727             [DbLinqToDo]\r
728             public override bool HasAnyValidateMethod\r
729             {\r
730                 get { throw new NotImplementedException(); }\r
731             }\r
732             [DbLinqToDo]\r
733             public override bool HasInheritance\r
734             {\r
735                 get { throw new NotImplementedException(); }\r
736             }\r
737             public override bool HasInheritanceCode\r
738             {\r
739                 get { return t.InheritanceCode != null; }\r
740             }\r
741             public override bool HasUpdateCheck { get { return members.Any(m => m.UpdateCheck != UpdateCheck.Never); } }\r
742             public override ReadOnlyCollection<MetaDataMember> IdentityMembers\r
743             {\r
744                 get\r
745                 {\r
746                     if (identity_members == null)\r
747                     {\r
748                         identity_members = new ReadOnlyCollection<MetaDataMember>(\r
749                             members.TakeWhile(m => m.IsPrimaryKey).ToArray());\r
750                     }\r
751                     return identity_members;\r
752                 }\r
753             }\r
754             [DbLinqToDo]\r
755             public override MetaType InheritanceBase\r
756             {\r
757                 get { throw new NotImplementedException(); }\r
758             }\r
759             public override Object InheritanceCode { get { return t.InheritanceCode; } }\r
760             [DbLinqToDo]\r
761             public override MetaType InheritanceDefault\r
762             {\r
763                 get { throw new NotImplementedException(); }\r
764             }\r
765             [DbLinqToDo]\r
766             public override MetaType InheritanceRoot\r
767             {\r
768                 get { throw new NotImplementedException(); }\r
769             }\r
770             [DbLinqToDo]\r
771             public override ReadOnlyCollection<MetaType> InheritanceTypes\r
772             {\r
773                 get { throw new NotImplementedException(); }\r
774             }\r
775             [DbLinqToDo]\r
776             public override bool IsEntity { get { return true; } }\r
777             public override bool IsInheritanceDefault { get { return t.IsInheritanceDefault; } }\r
778             public override MetaModel Model { get { return model; } }\r
779             public override string Name { get { return t.Name; } }\r
780             [DbLinqToDo]\r
781             public override MethodInfo OnLoadedMethod\r
782             {\r
783                 get { throw new NotImplementedException(); }\r
784             }\r
785             [DbLinqToDo]\r
786             public override MethodInfo OnValidateMethod\r
787             {\r
788                 get { throw new NotImplementedException(); }\r
789             }\r
790             public override ReadOnlyCollection<MetaDataMember> PersistentDataMembers\r
791             {\r
792                 get\r
793                 {\r
794                     if (persistent_members == null)\r
795                     {\r
796                         persistent_members = new ReadOnlyCollection<MetaDataMember>(\r
797                             members.TakeWhile(m => m.IsPersistent).ToArray());\r
798                     }\r
799                     return persistent_members;\r
800                 }\r
801             }\r
802             public override MetaTable Table { get { return model.GetTable(runtime_type); } }\r
803             public override Type Type { get { return runtime_type; } }\r
804             public override MetaDataMember VersionMember { get { return members.First(m => m.IsVersion); } }\r
805 \r
806             public override MetaDataMember GetDataMember(MemberInfo member)\r
807             {\r
808                 //return members.First(m => m.Member == member);\r
809                 foreach (var m in members) if (m.Member == member) return m;\r
810                 throw new ArgumentException(String.Format("No corresponding metadata member for '{0}'", member));\r
811             }\r
812 \r
813             public override MetaType GetInheritanceType(Type type)\r
814             {\r
815                 return InheritanceTypes.First(t => t.Type == type);\r
816             }\r
817 \r
818             [DbLinqToDo]\r
819             public override MetaType GetTypeForInheritanceCode(object code)\r
820             {\r
821                 throw new NotImplementedException();\r
822             }\r
823         }\r
824 \r
825         class XmlMetaAssociation : MetaAssociation\r
826         {\r
827             //XmlMetaType owner;\r
828             DbmlAssociation a;\r
829             ReadOnlyCollection<MetaDataMember> these_keys, other_keys;\r
830             MetaDataMember member;\r
831 \r
832             public XmlMetaAssociation(XmlMetaType owner, XmlMetaDataMember member, DbmlAssociation a)\r
833             {\r
834                 //this.owner = owner;\r
835                 this.member = member;\r
836                 this.a = a;\r
837                 SetupRelationship();\r
838             }\r
839 \r
840             /// <summary>\r
841             /// This function sets up the relationship information based on the attribute <see cref="XmlMetaModel"/>.\r
842             /// </summary>\r
843             private void SetupRelationship()\r
844             {\r
845                 //Get the association target type\r
846                 Type targetType = member.Member.GetFirstInnerReturnType();\r
847 \r
848                 var metaModel = ThisMember.DeclaringType.Model as XmlMetaModel;\r
849                 if (metaModel == null)\r
850                 {\r
851                     throw new InvalidOperationException("Internal Error: MetaModel is not a XmlMetaModel");\r
852                 }\r
853 \r
854                 MetaTable otherTable = metaModel.GetTable(targetType);\r
855 \r
856                 //Setup "this key"\r
857                 these_keys = GetKeys(a.ThisKey ?? String.Empty, ThisMember.DeclaringType);\r
858 \r
859                 //Setup other key\r
860                 other_keys = GetKeys(a.OtherKey ?? String.Empty, otherTable.RowType);\r
861             }\r
862 \r
863             //Seperator used for key lists\r
864             private static readonly char[] STRING_SEPERATOR = new[] { ',' };\r
865 \r
866             /// <summary>\r
867             /// Returns a list of keys from the given meta type based on the key list string.\r
868             /// </summary>\r
869             /// <param name="keyListString">The key list string.</param>\r
870             /// <param name="parentType">Type of the parent.</param>\r
871             /// <returns></returns>\r
872             private static ReadOnlyCollection<MetaDataMember> GetKeys(string keyListString, MetaType parentType)\r
873             {\r
874                 if (keyListString != null)\r
875                 {\r
876                     var thisKeyList = new List<MetaDataMember>();\r
877 \r
878                     string[] keyNames = keyListString.Split(STRING_SEPERATOR, StringSplitOptions.RemoveEmptyEntries);\r
879 \r
880                     foreach (string rawKeyName in keyNames)\r
881                     {\r
882                         string keyName = rawKeyName.Trim();\r
883 \r
884                         //TODO: maybe speed the lookup up\r
885                         MetaDataMember key = (from dataMember in parentType.PersistentDataMembers\r
886                                               where dataMember.Name == keyName\r
887                                               select dataMember).SingleOrDefault();\r
888 \r
889                         if (key == null)\r
890                         {\r
891                             string errorMessage = string.Format("Could not find key member '{0}' of key '{1}' on type '{2}'. The key may be wrong or the field or property on '{2}' has changed names.",\r
892                                 keyName, keyListString, parentType.Type.Name);\r
893 \r
894                             throw new InvalidOperationException(errorMessage);\r
895                         }\r
896 \r
897                         thisKeyList.Add(key);\r
898                     }\r
899 \r
900                     return new ReadOnlyCollection<MetaDataMember>(thisKeyList);\r
901                 }\r
902                 else //Key is the primary key of this table\r
903                 {\r
904                     return parentType.IdentityMembers;\r
905                 }\r
906             }\r
907 \r
908             public override bool DeleteOnNull { get { return a.DeleteOnNull; } }\r
909             public override string DeleteRule { get { return a.DeleteRule; } }\r
910             public override bool IsForeignKey { get { return a.IsForeignKey; } }\r
911             [DbLinqToDo]\r
912             public override bool IsMany\r
913             {\r
914                 get { throw new NotImplementedException(); }\r
915             }\r
916             public override bool IsNullable { get { return member.Member.GetMemberType().IsNullable(); } }\r
917             public override bool IsUnique { get { return a.IsUnique; } }\r
918             public override ReadOnlyCollection<MetaDataMember> OtherKey { get { return other_keys; } }\r
919             public override bool OtherKeyIsPrimaryKey { get { return OtherKey.All(m => m.IsPrimaryKey); } }\r
920             [DbLinqToDo]\r
921             public override MetaDataMember OtherMember\r
922             {\r
923                 get { throw new NotImplementedException(); }\r
924             }\r
925             public override MetaType OtherType { get { return OtherMember.DeclaringType; } }\r
926             public override ReadOnlyCollection<MetaDataMember> ThisKey { get { return these_keys; } }\r
927             public override bool ThisKeyIsPrimaryKey { get { return ThisKey.All(m => m.IsPrimaryKey); } }\r
928             public override MetaDataMember ThisMember { get { return member; } }\r
929         }\r
930 \r
931         abstract class XmlMetaDataMember : MetaDataMember\r
932         {\r
933             internal XmlMetaModel model;\r
934             internal XmlMetaType type;\r
935             internal MemberInfo member, storage;\r
936             MetaAccessor member_accessor, storage_accessor;\r
937             int ordinal;\r
938 \r
939             protected XmlMetaDataMember(XmlMetaModel model, XmlMetaType type, string memberName, string storageName, int ordinal)\r
940             {\r
941                 this.model = model;\r
942                 this.type = type;\r
943                 BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;\r
944                 if (type.Type.GetMember(memberName, bf).Length == 0)\r
945                     throw new ArgumentException(String.Format("Specified member '{0}' was not found in type '{1}'", memberName, type.Name));\r
946                 if (type.Type.GetMember(storageName, bf).Length == 0)\r
947                     throw new ArgumentException(String.Format("Specified member '{0}' was not found in type '{1}'", storageName, type.Name));\r
948                 this.member = type.Type.GetMember(memberName, bf)[0];\r
949                 this.storage = type.Type.GetMember(storageName, bf)[0];\r
950                 this.ordinal = ordinal;\r
951             }\r
952 \r
953             public override bool CanBeNull { get { return member.GetMemberType().IsNullable(); } }\r
954             public override MetaType DeclaringType { get { return type; } }\r
955             public override bool IsDeferred { get { return false; } }\r
956             public override bool IsPersistent { get { return true; } }\r
957             public override MemberInfo Member { get { return member; } }\r
958             public override MetaAccessor MemberAccessor\r
959             {\r
960                 get\r
961                 {\r
962                     if (member_accessor == null)\r
963                         member_accessor = new XmlMetaAccessor(this, Member);\r
964                     return member_accessor;\r
965                 }\r
966             }\r
967             public override int Ordinal { get { return ordinal; } }\r
968             public override MetaAccessor StorageAccessor\r
969             {\r
970                 get\r
971                 {\r
972                     if (storage_accessor == null)\r
973                         storage_accessor = new XmlMetaAccessor(this, StorageMember);\r
974                     return storage_accessor;\r
975                 }\r
976             }\r
977             public override MemberInfo StorageMember { get { return storage; } }\r
978             public override Type Type { get { return member.GetMemberType(); } }\r
979 \r
980             public override bool IsDeclaredBy(MetaType type)\r
981             {\r
982                 return this.type == type;\r
983             }\r
984         }\r
985 \r
986         class XmlColumnMetaDataMember : XmlMetaDataMember\r
987         {\r
988             DbmlColumn c;\r
989 \r
990             public XmlColumnMetaDataMember(XmlMetaModel model, XmlMetaType type, DbmlColumn column, int ordinal)\r
991                 : base(model, type, column.Member, column.Storage, ordinal)\r
992             {\r
993                 this.c = column;\r
994             }\r
995 \r
996             public override MetaAssociation Association { get { return null; } }\r
997             public override AutoSync AutoSync { get { return (AutoSync)c.AutoSync; } }\r
998             public override string DbType { get { return c.DbType; } }\r
999             [DbLinqToDo]\r
1000             public override MetaAccessor DeferredSourceAccessor\r
1001             {\r
1002                 get { throw new NotImplementedException(); }\r
1003             }\r
1004             [DbLinqToDo]\r
1005             public override MetaAccessor DeferredValueAccessor\r
1006             {\r
1007                 get { throw new NotImplementedException(); }\r
1008             }\r
1009 \r
1010             public override string Expression { get { return c.Expression; } }\r
1011             public override bool IsAssociation { get { return false; } }\r
1012             public override bool IsDbGenerated { get { return c.IsDbGenerated; } }\r
1013             public override bool IsDiscriminator { get { return c.IsDiscriminator; } }\r
1014             public override bool IsPrimaryKey { get { return c.IsPrimaryKey; } }\r
1015             public override bool IsVersion { get { return c.IsVersion; } }\r
1016             [DbLinqToDo]\r
1017             public override MethodInfo LoadMethod\r
1018             {\r
1019                 get\r
1020                 {\r
1021                     throw new NotImplementedException();\r
1022                 }\r
1023             }\r
1024             public override string MappedName { get { return c.Name; } }\r
1025             public override string Name { get { return c.Name ?? c.Member; } }\r
1026             public override UpdateCheck UpdateCheck { get { return c.UpdateCheck; } }\r
1027         }\r
1028 \r
1029         class XmlAssociationMetaDataMember : XmlMetaDataMember\r
1030         {\r
1031             DbmlAssociation a;\r
1032             XmlMetaAssociation ma;\r
1033 \r
1034             public XmlAssociationMetaDataMember(XmlMetaModel model, XmlMetaType type, DbmlAssociation association, int ordinal)\r
1035                 : base(model, type, association.Member, association.Storage, ordinal)\r
1036             {\r
1037                 this.a = association;\r
1038             }\r
1039 \r
1040             public override MetaAssociation Association\r
1041             {\r
1042                 get\r
1043                 {\r
1044                     if (ma == null)\r
1045                         this.ma = new XmlMetaAssociation(type, this, a);\r
1046                     return ma;\r
1047                 }\r
1048             }\r
1049             public override AutoSync AutoSync { get { return AutoSync.Never; } }\r
1050             public override string DbType { get { return String.Empty; } }\r
1051             [DbLinqToDo]\r
1052             public override MetaAccessor DeferredSourceAccessor\r
1053             {\r
1054                 get { throw new NotImplementedException(); }\r
1055             }\r
1056             [DbLinqToDo]\r
1057             public override MetaAccessor DeferredValueAccessor\r
1058             {\r
1059                 get { throw new NotImplementedException(); }\r
1060             }\r
1061 \r
1062             public override string Expression { get { return String.Empty; } }\r
1063             public override bool IsAssociation { get { return true; } }\r
1064             public override bool IsDbGenerated { get { return false; } }\r
1065             public override bool IsDiscriminator { get { return false; } }\r
1066             [DbLinqToDo]\r
1067             public override bool IsPrimaryKey\r
1068             {\r
1069                 get { throw new NotImplementedException(); }\r
1070             }\r
1071             [DbLinqToDo]\r
1072             public override bool IsVersion\r
1073             {\r
1074                 get { throw new NotImplementedException(); }\r
1075             }\r
1076             [DbLinqToDo]\r
1077             public override MethodInfo LoadMethod\r
1078             {\r
1079                 get\r
1080                 {\r
1081                     throw new NotImplementedException();\r
1082                 }\r
1083             }\r
1084             public override string MappedName { get { return a.Member; } }\r
1085             public override string Name { get { return a.Name; } }\r
1086             public override UpdateCheck UpdateCheck\r
1087             {\r
1088                 get\r
1089                 {\r
1090                     throw new NotImplementedException();\r
1091                 }\r
1092             }\r
1093         }\r
1094 \r
1095         class XmlMetaAccessor : MetaAccessor\r
1096         {\r
1097             XmlMetaDataMember member;\r
1098             MemberInfo member_info;\r
1099 \r
1100             public XmlMetaAccessor(XmlMetaDataMember member, MemberInfo memberInfo)\r
1101             {\r
1102                 this.member = member;\r
1103                 this.member_info = memberInfo;\r
1104             }\r
1105 \r
1106             public override Type Type\r
1107             {\r
1108                 get { return member_info is FieldInfo ? ((FieldInfo)member_info).FieldType : ((PropertyInfo)member_info).PropertyType; }\r
1109             }\r
1110 \r
1111             [DbLinqToDo]\r
1112             public override object GetBoxedValue(object instance)\r
1113             {\r
1114                 throw new NotImplementedException();\r
1115             }\r
1116 \r
1117             [DbLinqToDo]\r
1118             public override void SetBoxedValue(ref object instance, object value)\r
1119             {\r
1120                 throw new NotImplementedException();\r
1121             }\r
1122         }\r
1123 \r
1124         class XmlMetaFunction : MetaFunction\r
1125         {\r
1126             XmlMetaModel model;\r
1127             DbmlFunction f;\r
1128             MethodInfo method;\r
1129             ReadOnlyCollection<MetaParameter> parameters;\r
1130             ReadOnlyCollection<MetaType> result_types;\r
1131             MetaParameter return_param;\r
1132 \r
1133             public XmlMetaFunction(XmlMetaModel model, DbmlFunction function)\r
1134             {\r
1135                 this.model = model;\r
1136                 this.f = function;\r
1137                 method = model.ContextType.GetMethod(function.Method);\r
1138                 return_param = new XmlMetaParameter(function.Return.DbType, String.Empty, method.ReturnParameter);\r
1139             }\r
1140 \r
1141             public override bool HasMultipleResults { get { return f.ElementTypes.Count > 0; } }\r
1142             public override bool IsComposable { get { return f.IsComposable; } }\r
1143             public override string MappedName { get { return f.Name; } }\r
1144             public override MethodInfo Method { get { return method; } }\r
1145             public override MetaModel Model { get { return model; } }\r
1146             public override string Name { get { return f.Name; } }\r
1147             public override ReadOnlyCollection<MetaParameter> Parameters\r
1148             {\r
1149                 get\r
1150                 {\r
1151                     if (parameters == null)\r
1152                     {\r
1153                         var l = new List<MetaParameter>();\r
1154                         int i = 0;\r
1155                         ParameterInfo[] mparams = method.GetParameters();\r
1156                         foreach (var p in f.Parameters)\r
1157                             l.Add(new XmlMetaParameter(p, mparams[i++]));\r
1158                         parameters = new ReadOnlyCollection<MetaParameter>(l);\r
1159                     }\r
1160                     return parameters;\r
1161                 }\r
1162             }\r
1163             public override ReadOnlyCollection<MetaType> ResultRowTypes\r
1164             {\r
1165                 get\r
1166                 {\r
1167                     if (result_types == null)\r
1168                     {\r
1169                         var l = new List<MetaType>();\r
1170                         foreach (var p in f.ElementTypes)\r
1171                             l.Add(model.GetMetaType(model.GetTypeFromName(p.Name)));\r
1172                         result_types = new ReadOnlyCollection<MetaType>(l.ToArray());\r
1173                     }\r
1174                     return result_types;\r
1175                 }\r
1176             }\r
1177             public override MetaParameter ReturnParameter { get { return return_param; } }\r
1178         }\r
1179     }\r
1180 }\r