XmlSchemaDataImporter.cs 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  1. //
  2. // XmlSchemaDataImporter.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // (C)2004 Novell Inc.
  8. //
  9. //
  10. // ***** The design note became somewhat obsolete. Should be rewritten. *****
  11. //
  12. // * Design Notes
  13. //
  14. // ** Abstract
  15. //
  16. // This class is used to import an XML Schema into a DataSet schema.
  17. //
  18. // Only XmlReader is acceptable as the input to the class.
  19. // This class is not expected to read XML Schema multi time.
  20. //
  21. // ** Targetable Schema Components
  22. //
  23. // Only global global elements that hold complex type are converted
  24. // into a table.
  25. // <del>
  26. // The components of the type of the element are subsequently converted
  27. // into a table, BUT there is an exception. As for "DataSet elements",
  28. // the type is just ignored (see "DataSet Element definition" below).
  29. // </del><ins>
  30. // The components of the type of the element are subsequently converted
  31. // into a table. As for "DataSet elements", its complex type is also
  32. // handled.
  33. // </ins>
  34. //
  35. // Unused complex types are never be converted.
  36. //
  37. // Global simple types and global attributes are never converted.
  38. // They cannot be a table.
  39. // Local complex types are also converted into a table.
  40. //
  41. // Local elements are converted into either a table or a column in
  42. // the "context DataTable". Simple-typed element is not always converted
  43. // into a DataColumn; if maxOccurs > 1, it will be converted as a table.
  44. //
  45. // ** Name Convention
  46. //
  47. // Ignore this section. Microsoft.NET was buggy enough to confuse
  48. // against these name conflicts.
  49. //
  50. // Since local complex types are anonymous, we have to name for each
  51. // component. Thus, and since complex types and elements can have the
  52. // same name each other, we have to manage a table for mappings from
  53. // a name to a component. The names must be also used in DataRelation
  54. // definitions correctly.
  55. //
  56. // ** DataSet element definition
  57. //
  58. // "DataSet element" is 1) such element that has an attribute
  59. // msdata:IsDataSet (where prefix "msdata" is bound to
  60. // urn:schemas-microsoft-com:xml-msdata), or 2) the only one
  61. // element definition in the schema.
  62. //
  63. // There is another complicated rule. 1) If there is only one element EL
  64. // in the schema, and 2) if the type of EL is complex named CT, and 3)
  65. // the content of the CT is a group base, and 4) the group base contains
  66. // an element EL2, and finally 5) if EL2 is complex, THEN the element is
  67. // the DataSet element.
  68. //
  69. // Only the first global element that matches the condition above is
  70. // regarded as DataSet element (by necessary design or just a bug?)
  71. // instead of handling as an error.
  72. //
  73. // All global elements are considered as an alternative in the dataset
  74. // element.
  75. //
  76. // For local elements, msdata:IsDataSet are just ignored.
  77. //
  78. // ** Importing Complex Types as Columns
  79. //
  80. // When an xs:element is going to be mapped, its complex type (remember
  81. // that only complex-typed elements are targettable) are expanded to
  82. // DataColumn.
  83. //
  84. // DataColumn has a property MappingType that shows whether this column
  85. // came from attribute or element.
  86. //
  87. // [Question: How about MappingType.Simple? How is it used?]
  88. //
  89. // Additionally, for particle elements, it might also create another
  90. // DataTable (but for the particle elements in context DataTable, it
  91. // will create an index to the new table).
  92. //
  93. // For group base particles (XmlSchemaGroupBase; sequence, choice, all)
  94. // each component in those groups are mapped to a column. Even if you
  95. // import "choice" or "all" components, DataSet.WriteXmlSchema() will
  96. // output them just as a "sequence".
  97. //
  98. // Columns cannot be added directly to current context DataTable; they
  99. // need to be added after processing all the columns, because they may
  100. // have msdata:Ordinal attribute that specifies the order of the columns
  101. // in the DataTable.
  102. //
  103. // "Nested elements" are not allowed. (Clarification required?)
  104. //
  105. // ** Identity Constraints and DataRelations
  106. //
  107. // *** DataRelations from element identity constraints
  108. //
  109. // Only constraints on "DataSet element" is considered. All other
  110. // constraint definitions are ignored. Note that it is DataSet that has
  111. // the property Relations (of type DataRelationCollection).
  112. //
  113. // xs:key and xs:unique are handled as the same (then both will be
  114. // serialized as xs:unique).
  115. //
  116. // The XPath expressions in the constraints are strictly limited; they
  117. // are expected to be expandable enough to be mappable for each
  118. //
  119. // * selector to "any_valid_XPath/is/OK/blah"
  120. // where "blah" is one of the DataTable name. It looks that
  121. // only the last QName section is significant and any heading
  122. // XPath step is OK (even if the mapped node does not exist).
  123. // * field to QName that is mapped to DataColumn in the DataTable
  124. // (even ./QName is not allowed)
  125. //
  126. // *** DataRelations from annotations
  127. //
  128. // See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_mapping_relationship_specified_for_nested_elements.asp and http://msdn.microsoft.com/library/en-us/cpguide/html/_specifying_relationship_between_elements_with_no_nesting.asp
  129. //
  130. // ** Informative References
  131. //
  132. // Generating DataSet Relational Structure from XML Schema (XSD)
  133. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_generating_dataset_relational_structure_from_xsd.asp
  134. //
  135. //
  136. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  137. //
  138. // Permission is hereby granted, free of charge, to any person obtaining
  139. // a copy of this software and associated documentation files (the
  140. // "Software"), to deal in the Software without restriction, including
  141. // without limitation the rights to use, copy, modify, merge, publish,
  142. // distribute, sublicense, and/or sell copies of the Software, and to
  143. // permit persons to whom the Software is furnished to do so, subject to
  144. // the following conditions:
  145. //
  146. // The above copyright notice and this permission notice shall be
  147. // included in all copies or substantial portions of the Software.
  148. //
  149. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  150. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  151. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  152. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  153. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  154. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  155. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  156. //
  157. using System;
  158. using System.Collections;
  159. using System.Data;
  160. using System.Data.Common;
  161. using System.Globalization;
  162. using System.Xml;
  163. using System.Xml.Schema;
  164. namespace System.Data
  165. {
  166. internal class TableStructureCollection : CollectionBase
  167. {
  168. public void Add (TableStructure table)
  169. {
  170. List.Add (table);
  171. }
  172. public TableStructure this [int i] {
  173. get { return List [i] as TableStructure; }
  174. }
  175. public TableStructure this [string name] {
  176. get {
  177. foreach (TableStructure ts in List)
  178. if (ts.Table.TableName == name)
  179. return ts;
  180. return null;
  181. }
  182. }
  183. }
  184. internal class RelationStructureCollection : CollectionBase
  185. {
  186. public void Add (RelationStructure rel)
  187. {
  188. List.Add (rel);
  189. }
  190. public RelationStructure this [int i] {
  191. get { return List [i] as RelationStructure; }
  192. }
  193. public RelationStructure this [string parent, string child] {
  194. get {
  195. foreach (RelationStructure rel in List)
  196. if (rel.ParentTableName == parent && rel.ChildTableName == child)
  197. return rel;
  198. return null;
  199. }
  200. }
  201. }
  202. internal class TableStructure
  203. {
  204. public TableStructure (DataTable table)
  205. {
  206. this.Table = table;
  207. }
  208. // The columns and orders which will be added to the context
  209. // table (See design notes; Because of the ordinal problem)
  210. public DataTable Table;
  211. public Hashtable OrdinalColumns = new Hashtable ();
  212. public ArrayList NonOrdinalColumns = new ArrayList ();
  213. public DataColumn PrimaryKey;
  214. public bool ContainsColumn (string name)
  215. {
  216. foreach (DataColumn col in NonOrdinalColumns)
  217. if (col.ColumnName == name)
  218. return true;
  219. foreach (DataColumn col in OrdinalColumns.Keys)
  220. if (col.ColumnName == name)
  221. return true;
  222. return false;
  223. }
  224. }
  225. internal class RelationStructure
  226. {
  227. public string ExplicitName;
  228. public string ParentTableName;
  229. public string ChildTableName;
  230. public string ParentColumnName;
  231. public string ChildColumnName;
  232. public bool IsNested;
  233. public bool CreateConstraint;
  234. }
  235. internal class ConstraintStructure
  236. {
  237. public readonly string TableName;
  238. public readonly string [] Columns;
  239. public readonly bool [] IsAttribute;
  240. public readonly string ConstraintName;
  241. public readonly bool IsPrimaryKey;
  242. public readonly string ReferName;
  243. public readonly bool IsNested;
  244. public readonly bool IsConstraintOnly;
  245. public ConstraintStructure (string tname, string [] cols, bool [] isAttr, string cname, bool isPK, string refName, bool isNested, bool isConstraintOnly)
  246. {
  247. TableName = tname;
  248. Columns = cols;
  249. IsAttribute = isAttr;
  250. ConstraintName = XmlHelper.Decode (cname);
  251. IsPrimaryKey = isPK;
  252. ReferName = refName;
  253. IsNested = isNested;
  254. IsConstraintOnly = isConstraintOnly;
  255. }
  256. }
  257. internal class XmlSchemaDataImporter
  258. {
  259. static readonly XmlSchemaDatatype schemaIntegerType;
  260. static readonly XmlSchemaDatatype schemaDecimalType;
  261. static readonly XmlSchemaComplexType schemaAnyType;
  262. static XmlSchemaDataImporter ()
  263. {
  264. XmlSchema s = new XmlSchema ();
  265. XmlSchemaAttribute a = new XmlSchemaAttribute ();
  266. a.Name = "foo";
  267. a.SchemaTypeName = new XmlQualifiedName ("integer", XmlSchema.Namespace);
  268. s.Items.Add (a);
  269. XmlSchemaAttribute b = new XmlSchemaAttribute ();
  270. b.Name = "bar";
  271. b.SchemaTypeName = new XmlQualifiedName ("decimal", XmlSchema.Namespace);
  272. s.Items.Add (b);
  273. XmlSchemaElement e = new XmlSchemaElement ();
  274. e.Name = "bar";
  275. s.Items.Add (e);
  276. s.Compile (null);
  277. #if NET_2_0
  278. schemaIntegerType = ((XmlSchemaSimpleType) a.AttributeSchemaType).Datatype;
  279. schemaDecimalType = ((XmlSchemaSimpleType) b.AttributeSchemaType).Datatype;
  280. schemaAnyType = e.ElementSchemaType as XmlSchemaComplexType;
  281. #else
  282. schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
  283. schemaDecimalType = b.AttributeType as XmlSchemaDatatype;
  284. schemaAnyType = e.ElementType as XmlSchemaComplexType;
  285. #endif
  286. }
  287. #region Fields
  288. DataSet dataset;
  289. bool forDataSet;
  290. XmlSchema schema;
  291. ArrayList relations = new ArrayList ();
  292. Hashtable reservedConstraints = new Hashtable ();
  293. // such element that has an attribute msdata:IsDataSet="true"
  294. XmlSchemaElement datasetElement;
  295. // choice alternatives in the "dataset element"
  296. ArrayList topLevelElements = new ArrayList ();
  297. // import target elements
  298. ArrayList targetElements = new ArrayList ();
  299. TableStructure currentTable;
  300. #if NET_2_0
  301. // TODO: Do we need a collection here?
  302. TableAdapterSchemaInfo currentAdapter;
  303. #endif
  304. #endregion
  305. // .ctor()
  306. public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
  307. {
  308. this.dataset = dataset;
  309. this.forDataSet = forDataSet;
  310. dataset.DataSetName = "NewDataSet"; // Initialize always
  311. schema = XmlSchema.Read (reader, null);
  312. if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace)
  313. reader.ReadEndElement ();
  314. schema.Compile (null);
  315. }
  316. #if NET_2_0
  317. // properties
  318. internal TableAdapterSchemaInfo CurrentAdapter {
  319. get { return currentAdapter; }
  320. }
  321. #endif
  322. // methods
  323. public void Process ()
  324. {
  325. if (schema.Id != null)
  326. dataset.DataSetName = schema.Id; // default. Overridable by "DataSet element"
  327. dataset.Namespace = schema.TargetNamespace;
  328. // Find dataset element
  329. foreach (XmlSchemaObject obj in schema.Items) {
  330. XmlSchemaElement el = obj as XmlSchemaElement;
  331. if (el != null) {
  332. if (datasetElement == null &&
  333. IsDataSetElement (el))
  334. datasetElement = el;
  335. #if NET_2_0
  336. if (el.ElementSchemaType is XmlSchemaComplexType &&
  337. el.ElementSchemaType != schemaAnyType)
  338. #else
  339. if (el.ElementType is XmlSchemaComplexType &&
  340. el.ElementType != schemaAnyType)
  341. #endif
  342. targetElements.Add (obj);
  343. }
  344. }
  345. // make reservation of identity constraints
  346. if (datasetElement != null) {
  347. // keys/uniques.
  348. foreach (XmlSchemaObject obj in datasetElement.Constraints)
  349. if (! (obj is XmlSchemaKeyref))
  350. ReserveSelfIdentity ((XmlSchemaIdentityConstraint) obj);
  351. // keyrefs.
  352. foreach (XmlSchemaObject obj in datasetElement.Constraints)
  353. if (obj is XmlSchemaKeyref)
  354. ReserveRelationIdentity (datasetElement, (XmlSchemaKeyref) obj);
  355. }
  356. foreach (XmlSchemaObject obj in schema.Items) {
  357. if (obj is XmlSchemaElement) {
  358. XmlSchemaElement el = obj as XmlSchemaElement;
  359. #if NET_2_0
  360. if (el.ElementSchemaType is XmlSchemaComplexType &&
  361. el.ElementSchemaType != schemaAnyType)
  362. #else
  363. if (el.ElementType is XmlSchemaComplexType &&
  364. el.ElementType != schemaAnyType)
  365. #endif
  366. targetElements.Add (obj);
  367. }
  368. }
  369. // This collection will grow up while processing elements.
  370. int globalElementCount = targetElements.Count;
  371. for (int i = 0; i < globalElementCount; i++)
  372. ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
  373. // Rest are local elements.
  374. for (int i = globalElementCount; i < targetElements.Count; i++)
  375. ProcessDataTableElement ((XmlSchemaElement) targetElements [i]);
  376. // Handle relation definitions written as xs:annotation.
  377. // See detail: http://msdn.microsoft.com/library/shared/happyUrl/fnf_msdn.asp?Redirect=%22http://msdn.microsoft.com/404/default.asp%22
  378. foreach (XmlSchemaObject obj in schema.Items)
  379. if (obj is XmlSchemaAnnotation)
  380. HandleAnnotations ((XmlSchemaAnnotation) obj, false);
  381. if (datasetElement != null) {
  382. // Handle constraints in the DataSet element. First keys.
  383. foreach (XmlSchemaObject obj in datasetElement.Constraints)
  384. if (! (obj is XmlSchemaKeyref))
  385. ProcessSelfIdentity (reservedConstraints [obj] as ConstraintStructure);
  386. // Then keyrefs.
  387. foreach (XmlSchemaObject obj in datasetElement.Constraints)
  388. if (obj is XmlSchemaKeyref)
  389. ProcessRelationIdentity (datasetElement, reservedConstraints [obj] as ConstraintStructure);
  390. }
  391. foreach (RelationStructure rs in this.relations)
  392. dataset.Relations.Add (GenerateRelationship (rs));
  393. }
  394. private bool IsDataSetElement (XmlSchemaElement el)
  395. {
  396. if (el.UnhandledAttributes != null) {
  397. foreach (XmlAttribute attr in el.UnhandledAttributes) {
  398. if (attr.LocalName == "IsDataSet" &&
  399. attr.NamespaceURI == XmlConstants.MsdataNamespace) {
  400. switch (attr.Value) {
  401. case "true": // case sensitive
  402. return true;
  403. case "false":
  404. break;
  405. default:
  406. throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
  407. }
  408. }
  409. }
  410. }
  411. if (schema.Elements.Count != 1)
  412. return false;
  413. if (!(el.SchemaType is XmlSchemaComplexType))
  414. return false;
  415. XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
  416. if (ct.AttributeUses.Count > 0)
  417. return false;
  418. XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
  419. if (gb == null || gb.Items.Count == 0)
  420. return false;
  421. foreach (XmlSchemaParticle p in gb.Items) {
  422. if (ContainsColumn (p))
  423. return false;
  424. }
  425. return true;
  426. }
  427. private bool ContainsColumn (XmlSchemaParticle p)
  428. {
  429. XmlSchemaElement el = p as XmlSchemaElement;
  430. if (el != null) {
  431. XmlSchemaComplexType ct = null;
  432. #if NET_2_0
  433. ct = el.ElementSchemaType as XmlSchemaComplexType;
  434. #else
  435. ct = el.ElementType as XmlSchemaComplexType;
  436. #endif
  437. if (ct == null || ct == schemaAnyType)
  438. return true; // column element
  439. if (ct.AttributeUses.Count > 0)
  440. return false; // table element
  441. if (ct.ContentType == XmlSchemaContentType.TextOnly)
  442. return true; // column element
  443. else
  444. return false; // table element
  445. }
  446. XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
  447. for (int i = 0; i < gb.Items.Count; i++) {
  448. if (ContainsColumn ((XmlSchemaParticle) gb.Items [i]))
  449. return true;
  450. }
  451. return false;
  452. }
  453. private void ProcessGlobalElement (XmlSchemaElement el)
  454. {
  455. // If it is already registered (by resolving reference
  456. // in previously-imported elements), just ignore.
  457. if (dataset.Tables.Contains (el.QualifiedName.Name))
  458. return;
  459. // If type is not complex, just skip this element
  460. #if NET_2_0
  461. if (! (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType))
  462. #else
  463. if (! (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType))
  464. #endif
  465. return;
  466. if (IsDataSetElement (el)) {
  467. ProcessDataSetElement (el);
  468. return;
  469. }
  470. else
  471. dataset.Locale = CultureInfo.CurrentCulture;
  472. // Register as a top-level element
  473. topLevelElements.Add (el);
  474. // Create DataTable for this element
  475. ProcessDataTableElement (el);
  476. }
  477. private void ProcessDataSetElement (XmlSchemaElement el)
  478. {
  479. dataset.DataSetName = el.Name;
  480. this.datasetElement = el;
  481. // Search for locale attributes
  482. bool useCurrent = false;
  483. if (el.UnhandledAttributes != null) {
  484. foreach (XmlAttribute attr in el.UnhandledAttributes) {
  485. #if NET_2_0
  486. if (attr.LocalName == "UseCurrentLocale" &&
  487. attr.NamespaceURI == XmlConstants.MsdataNamespace)
  488. useCurrent = true;
  489. #endif
  490. if (attr.LocalName == "Locale" &&
  491. attr.NamespaceURI == XmlConstants.MsdataNamespace) {
  492. CultureInfo ci = new CultureInfo (attr.Value);
  493. dataset.Locale = ci;
  494. }
  495. }
  496. }
  497. #if NET_2_0
  498. if (!useCurrent && !dataset.LocaleSpecified) // then set current culture instance _explicitly_
  499. dataset.Locale = CultureInfo.CurrentCulture;
  500. #endif
  501. // Process content type particle (and create DataTable)
  502. XmlSchemaComplexType ct = null;
  503. #if NET_2_0
  504. ct = el.ElementSchemaType as XmlSchemaComplexType;
  505. #else
  506. ct = el.ElementType as XmlSchemaComplexType;
  507. #endif
  508. XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
  509. if (p != null)
  510. HandleDataSetContentTypeParticle (p);
  511. }
  512. private void HandleDataSetContentTypeParticle (XmlSchemaParticle p)
  513. {
  514. XmlSchemaElement el = p as XmlSchemaElement;
  515. if (el != null) {
  516. #if NET_2_0
  517. if (el.ElementSchemaType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
  518. #else
  519. if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
  520. #endif
  521. ProcessDataTableElement (el);
  522. }
  523. else if (p is XmlSchemaGroupBase) {
  524. foreach (XmlSchemaParticle pc in ((XmlSchemaGroupBase) p).Items)
  525. HandleDataSetContentTypeParticle (pc);
  526. }
  527. }
  528. private void ProcessDataTableElement (XmlSchemaElement el)
  529. {
  530. string tableName = XmlHelper.Decode (el.QualifiedName.Name);
  531. // If it is already registered, just ignore.
  532. if (dataset.Tables.Contains (tableName))
  533. return;
  534. DataTable table = new DataTable (tableName);
  535. table.Namespace = el.QualifiedName.Namespace;
  536. TableStructure oldTable = currentTable;
  537. currentTable = new TableStructure (table);
  538. dataset.Tables.Add (table);
  539. // Find Locale
  540. if (el.UnhandledAttributes != null) {
  541. foreach (XmlAttribute attr in el.UnhandledAttributes) {
  542. if (attr.LocalName == "Locale" &&
  543. attr.NamespaceURI == XmlConstants.MsdataNamespace)
  544. table.Locale = new CultureInfo (attr.Value);
  545. }
  546. }
  547. // Handle complex type (NOTE: It is (or should be)
  548. // impossible the type is other than complex type).
  549. XmlSchemaComplexType ct = null;
  550. #if NET_2_0
  551. ct = (XmlSchemaComplexType) el.ElementSchemaType;
  552. #else
  553. ct = (XmlSchemaComplexType) el.ElementType;
  554. #endif
  555. // Handle attributes
  556. foreach (DictionaryEntry de in ct.AttributeUses)
  557. ImportColumnAttribute ((XmlSchemaAttribute) de.Value);
  558. // Handle content type particle
  559. if (ct.ContentTypeParticle is XmlSchemaElement)
  560. ImportColumnElement (el, (XmlSchemaElement) ct.ContentTypeParticle);
  561. else if (ct.ContentTypeParticle is XmlSchemaGroupBase)
  562. ImportColumnGroupBase (el, (XmlSchemaGroupBase) ct.ContentTypeParticle);
  563. // else if null then do nothing.
  564. // Handle simple content
  565. switch (ct.ContentType) {
  566. case XmlSchemaContentType.TextOnly:
  567. // case XmlSchemaContentType.Mixed:
  568. // LAMESPEC: When reading from XML Schema, it maps to "_text", while on the data inference, it is mapped to "_Text" (case ignorant).
  569. string simpleName = el.QualifiedName.Name + "_text";
  570. DataColumn simple = new DataColumn (simpleName);
  571. simple.Namespace = el.QualifiedName.Namespace;
  572. simple.AllowDBNull = (el.MinOccurs == 0);
  573. simple.ColumnMapping = MappingType.SimpleContent;
  574. simple.DataType = ConvertDatatype (ct.Datatype);
  575. currentTable.NonOrdinalColumns.Add (simple);
  576. break;
  577. }
  578. // add columns to the table in specified order
  579. // (by msdata:Ordinal attributes)
  580. SortedList sd = new SortedList ();
  581. foreach (DictionaryEntry de in currentTable.OrdinalColumns)
  582. sd.Add (de.Value, de.Key);
  583. foreach (DictionaryEntry de in sd)
  584. table.Columns.Add ((DataColumn) de.Value);
  585. foreach (DataColumn dc in currentTable.NonOrdinalColumns)
  586. table.Columns.Add (dc);
  587. currentTable = oldTable;
  588. }
  589. private DataRelation GenerateRelationship (RelationStructure rs)
  590. {
  591. DataTable ptab = dataset.Tables [rs.ParentTableName];
  592. DataTable ctab = dataset.Tables [rs.ChildTableName];
  593. DataRelation rel ;
  594. string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
  595. // Annotation Relations belonging to a DataSet can contain multiple colnames
  596. // in parentkey and childkey.
  597. if (datasetElement != null) {
  598. String[] pcolnames = rs.ParentColumnName.Split (null);
  599. String[] ccolnames = rs.ChildColumnName.Split (null);
  600. DataColumn[] pcol = new DataColumn [pcolnames.Length];
  601. for (int i=0; i<pcol.Length; ++i)
  602. pcol [i] = ptab.Columns [XmlHelper.Decode (pcolnames [i])];
  603. DataColumn[] ccol = new DataColumn [ccolnames.Length];
  604. for (int i=0; i < ccol.Length; ++i) {
  605. ccol [i] = ctab.Columns [XmlHelper.Decode (ccolnames [i])];
  606. if (ccol [i] == null)
  607. ccol [i] = CreateChildColumn (pcol [i], ctab);
  608. }
  609. rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
  610. } else {
  611. DataColumn pcol = ptab.Columns [XmlHelper.Decode (rs.ParentColumnName)];
  612. DataColumn ccol = ctab.Columns [XmlHelper.Decode (rs.ChildColumnName)];
  613. if (ccol == null)
  614. ccol = CreateChildColumn (pcol, ctab);
  615. rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
  616. }
  617. rel.Nested = rs.IsNested;
  618. if (rs.CreateConstraint)
  619. rel.ParentTable.PrimaryKey = rel.ParentColumns;
  620. return rel;
  621. }
  622. private DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
  623. {
  624. DataColumn col = childTable.Columns.Add (parentColumn.ColumnName,
  625. parentColumn.DataType);
  626. col.Namespace = String.Empty;
  627. col.ColumnMapping = MappingType.Hidden;
  628. return col;
  629. }
  630. private void ImportColumnGroupBase (XmlSchemaElement parent, XmlSchemaGroupBase gb)
  631. {
  632. foreach (XmlSchemaParticle p in gb.Items) {
  633. XmlSchemaElement el = p as XmlSchemaElement;
  634. if (el != null)
  635. ImportColumnElement (parent, el);
  636. else if (p is XmlSchemaGroupBase)
  637. ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
  638. // otherwise p is xs:any
  639. }
  640. }
  641. private XmlSchemaDatatype GetSchemaPrimitiveType (object type)
  642. {
  643. if (type is XmlSchemaComplexType)
  644. return null; // It came here, so that maybe it is xs:anyType
  645. XmlSchemaDatatype dt = type as XmlSchemaDatatype;
  646. if (dt == null && type != null)
  647. dt = ((XmlSchemaSimpleType) type).Datatype;
  648. return dt;
  649. }
  650. // Note that this column might be Hidden
  651. private void ImportColumnAttribute (XmlSchemaAttribute attr)
  652. {
  653. DataColumn col = new DataColumn ();
  654. col.ColumnName = attr.QualifiedName.Name;
  655. col.Namespace = attr.QualifiedName.Namespace;
  656. XmlSchemaDatatype dt = null;
  657. #if NET_2_0
  658. dt = GetSchemaPrimitiveType (((XmlSchemaSimpleType) attr.AttributeSchemaType).Datatype);
  659. #else
  660. dt = GetSchemaPrimitiveType (attr.AttributeType);
  661. #endif
  662. // This complicated check comes from the fact that
  663. // MS.NET fails to map System.Object to anyType (that
  664. // will cause ReadTypedObject() fail on XmlValidatingReader).
  665. // ONLY In DataSet context, we set System.String for
  666. // simple ur-type.
  667. col.DataType = ConvertDatatype (dt);
  668. if (col.DataType == typeof (object))
  669. col.DataType = typeof (string);
  670. // When attribute use="prohibited", then it is regarded as
  671. // Hidden column.
  672. if (attr.Use == XmlSchemaUse.Prohibited)
  673. col.ColumnMapping = MappingType.Hidden;
  674. else {
  675. col.ColumnMapping = MappingType.Attribute;
  676. col.DefaultValue = GetAttributeDefaultValue (attr);
  677. }
  678. if (attr.Use == XmlSchemaUse.Required)
  679. col.AllowDBNull = false;
  680. #if NET_2_0
  681. FillFacet (col, attr.AttributeSchemaType as XmlSchemaSimpleType);
  682. #else
  683. FillFacet (col, attr.AttributeType as XmlSchemaSimpleType);
  684. #endif
  685. // Call this method after filling the name
  686. ImportColumnMetaInfo (attr, attr.QualifiedName, col);
  687. AddColumn (col);
  688. }
  689. private void ImportColumnElement (XmlSchemaElement parent, XmlSchemaElement el)
  690. {
  691. // FIXME: element nest check
  692. DataColumn col = new DataColumn ();
  693. col.DefaultValue = GetElementDefaultValue (el);
  694. col.AllowDBNull = (el.MinOccurs == 0);
  695. #if NET_2_0
  696. if (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType)
  697. #else
  698. if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType)
  699. #endif
  700. FillDataColumnComplexElement (parent, el, col);
  701. else if (el.MaxOccurs != 1)
  702. FillDataColumnRepeatedSimpleElement (parent, el, col);
  703. else
  704. FillDataColumnSimpleElement (el, col);
  705. }
  706. // common process for element and attribute
  707. private void ImportColumnMetaInfo (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
  708. {
  709. if (obj.UnhandledAttributes != null) {
  710. foreach (XmlAttribute attr in obj.UnhandledAttributes) {
  711. if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
  712. continue;
  713. switch (attr.LocalName) {
  714. case XmlConstants.Caption:
  715. col.Caption = attr.Value;
  716. break;
  717. case XmlConstants.DataType:
  718. col.DataType = Type.GetType (attr.Value);
  719. break;
  720. case XmlConstants.AutoIncrement:
  721. col.AutoIncrement = bool.Parse (attr.Value);
  722. break;
  723. case XmlConstants.AutoIncrementSeed:
  724. col.AutoIncrementSeed = int.Parse (attr.Value);
  725. break;
  726. case XmlConstants.AutoIncrementStep:
  727. col.AutoIncrementStep = int.Parse (attr.Value);
  728. break;
  729. case XmlConstants.ReadOnly:
  730. col.ReadOnly = XmlConvert.ToBoolean (attr.Value);
  731. break;
  732. case XmlConstants.Ordinal:
  733. int ordinal = int.Parse (attr.Value);
  734. break;
  735. }
  736. }
  737. }
  738. }
  739. private void FillDataColumnComplexElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
  740. {
  741. if (targetElements.Contains (el))
  742. return; // do nothing
  743. string elName = XmlHelper.Decode (el.QualifiedName.Name);
  744. if (elName == dataset.DataSetName)
  745. // Well, why it is ArgumentException :-?
  746. throw new ArgumentException ("Nested element must not have the same name as DataSet's name.");
  747. if (el.Annotation != null)
  748. HandleAnnotations (el.Annotation, true);
  749. // If xsd:keyref xsd:key for this table exists, then don't add
  750. // relation here manually.
  751. else if (!DataSetDefinesKey (elName)) {
  752. AddParentKeyColumn (parent, el, col);
  753. RelationStructure rel = new RelationStructure ();
  754. rel.ParentTableName = XmlHelper.Decode (parent.QualifiedName.Name);
  755. rel.ChildTableName = elName;
  756. rel.ParentColumnName = col.ColumnName;
  757. rel.ChildColumnName = col.ColumnName;
  758. rel.CreateConstraint = true;
  759. rel.IsNested = true;
  760. relations.Add (rel);
  761. }
  762. // If the element is not referenced one, the element will be handled later.
  763. if (el.RefName == XmlQualifiedName.Empty)
  764. ProcessDataTableElement (el);
  765. }
  766. private bool DataSetDefinesKey (string name)
  767. {
  768. foreach (ConstraintStructure c in reservedConstraints.Values)
  769. if (c.TableName == name && (c.IsPrimaryKey || c.IsNested))
  770. return true;
  771. return false;
  772. }
  773. private void AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
  774. {
  775. if (currentTable.PrimaryKey != null)
  776. return;
  777. // check name identity
  778. string name = XmlHelper.Decode (parent.QualifiedName.Name) + "_Id";
  779. int count = 0;
  780. while (currentTable.ContainsColumn (name))
  781. name = String.Format ("{0}_{1}", name, count++);
  782. // check existing primary key
  783. if (currentTable.Table.PrimaryKey.Length > 0)
  784. throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", currentTable.Table.TableName));
  785. col.ColumnName = name;
  786. col.ColumnMapping = MappingType.Hidden;
  787. col.Namespace = parent.QualifiedName.Namespace;
  788. col.DataType = typeof (int);
  789. col.AutoIncrement = true;
  790. col.AllowDBNull = false;
  791. ImportColumnMetaInfo (el, el.QualifiedName, col);
  792. AddColumn (col);
  793. currentTable.PrimaryKey = col;
  794. }
  795. private void FillDataColumnRepeatedSimpleElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
  796. {
  797. if (targetElements.Contains (el))
  798. return; // do nothing
  799. AddParentKeyColumn (parent, el, col);
  800. DataColumn pkey = currentTable.PrimaryKey;
  801. string elName = XmlHelper.Decode (el.QualifiedName.Name);
  802. string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
  803. DataTable dt = new DataTable ();
  804. dt.TableName = elName;
  805. dt.Namespace = el.QualifiedName.Namespace;
  806. // reference key column to parent
  807. DataColumn cc = new DataColumn ();
  808. cc.ColumnName = parentName + "_Id";
  809. cc.Namespace = parent.QualifiedName.Namespace;
  810. cc.ColumnMapping = MappingType.Hidden;
  811. cc.DataType = typeof (int);
  812. // repeatable content simple element
  813. DataColumn cc2 = new DataColumn ();
  814. cc2.ColumnName = elName + "_Column";
  815. cc2.Namespace = el.QualifiedName.Namespace;
  816. cc2.ColumnMapping = MappingType.SimpleContent;
  817. cc2.AllowDBNull = false;
  818. #if NET_2_0
  819. cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
  820. #else
  821. cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
  822. #endif
  823. dt.Columns.Add (cc2);
  824. dt.Columns.Add (cc);
  825. dataset.Tables.Add (dt);
  826. RelationStructure rel = new RelationStructure ();
  827. rel.ParentTableName = parentName;
  828. rel.ChildTableName = dt.TableName;
  829. rel.ParentColumnName = pkey.ColumnName;
  830. rel.ChildColumnName = cc.ColumnName;
  831. rel.IsNested = true;
  832. rel.CreateConstraint = true;
  833. relations.Add (rel);
  834. }
  835. private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
  836. {
  837. col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
  838. col.Namespace = el.QualifiedName.Namespace;
  839. col.ColumnMapping = MappingType.Element;
  840. #if NET_2_0
  841. col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
  842. FillFacet (col, el.ElementSchemaType as XmlSchemaSimpleType);
  843. #else
  844. col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
  845. FillFacet (col, el.ElementType as XmlSchemaSimpleType);
  846. #endif
  847. ImportColumnMetaInfo (el, el.QualifiedName, col);
  848. AddColumn (col);
  849. }
  850. private void AddColumn (DataColumn col)
  851. {
  852. if (col.Ordinal < 0)
  853. currentTable.NonOrdinalColumns.Add (col);
  854. else
  855. currentTable.OrdinalColumns.Add (col, col.Ordinal);
  856. }
  857. private void FillFacet (DataColumn col, XmlSchemaSimpleType st)
  858. {
  859. if (st == null || st.Content == null)
  860. return;
  861. // Handle restriction facets
  862. XmlSchemaSimpleTypeRestriction restriction = st == null ? null : st.Content as XmlSchemaSimpleTypeRestriction;
  863. if (restriction == null)
  864. throw new DataException ("DataSet does not suport 'list' nor 'union' simple type.");
  865. foreach (XmlSchemaFacet f in restriction.Facets) {
  866. if (f is XmlSchemaMaxLengthFacet)
  867. // There is no reason why MaxLength is limited to int, except for the fact that DataColumn.MaxLength property is int.
  868. col.MaxLength = int.Parse (f.Value);
  869. }
  870. }
  871. private Type ConvertDatatype (XmlSchemaDatatype dt)
  872. {
  873. if (dt == null)
  874. return typeof (string);
  875. else if (dt.ValueType == typeof (decimal)) {
  876. // LAMESPEC: MSDN documentation says it is based
  877. // on ValueType. However, in the System.Xml.Schema
  878. // context, xs:integer is mapped to Decimal, while
  879. // in DataSet context it is mapped to Int64.
  880. if (dt == schemaDecimalType)
  881. return typeof (decimal);
  882. else if (dt == schemaIntegerType)
  883. return typeof (long);
  884. else
  885. return typeof (ulong);
  886. }
  887. else
  888. return dt.ValueType;
  889. }
  890. // This method cuts out the local name of the last step from XPath.
  891. // It is nothing more than hack. However, MS looks to do similar.
  892. private string GetSelectorTarget (string xpath)
  893. {
  894. string tableName = xpath;
  895. int index = tableName.LastIndexOf ('/');
  896. // '>' is enough. If XPath [0] = '/', it is invalid.
  897. // Selector can specify only element axes.
  898. if (index > 0)
  899. tableName = tableName.Substring (index + 1);
  900. // Round QName to NSName
  901. index = tableName.LastIndexOf (':');
  902. if (index > 0)
  903. tableName = tableName.Substring (index + 1);
  904. return XmlHelper.Decode (tableName);
  905. }
  906. private void ReserveSelfIdentity (XmlSchemaIdentityConstraint ic)
  907. {
  908. string tableName = GetSelectorTarget (ic.Selector.XPath);
  909. string [] cols = new string [ic.Fields.Count];
  910. bool [] isAttrSpec = new bool [cols.Length];
  911. int i = 0;
  912. foreach (XmlSchemaXPath Field in ic.Fields) {
  913. string colName = Field.XPath;
  914. bool isAttr = colName.Length > 0 && colName [0] == '@';
  915. int index = colName.LastIndexOf (':');
  916. if (index > 0)
  917. colName = colName.Substring (index + 1);
  918. else if (isAttr)
  919. colName = colName.Substring (1);
  920. colName = XmlHelper.Decode (colName);
  921. cols [i] = colName;
  922. isAttrSpec [i] = isAttr;
  923. i++;
  924. }
  925. bool isPK = false;
  926. // find if there is an attribute with the constraint name
  927. // if not use the XmlSchemaConstraint's name.
  928. string constraintName = ic.Name;
  929. if (ic.UnhandledAttributes != null) {
  930. foreach (XmlAttribute attr in ic.UnhandledAttributes) {
  931. if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
  932. continue;
  933. switch (attr.LocalName) {
  934. case XmlConstants.ConstraintName:
  935. constraintName = attr.Value;
  936. break;
  937. case XmlConstants.PrimaryKey:
  938. isPK = bool.Parse(attr.Value);
  939. break;
  940. }
  941. }
  942. }
  943. reservedConstraints.Add (ic,
  944. new ConstraintStructure (tableName, cols,
  945. isAttrSpec, constraintName, isPK, null, false, false));
  946. }
  947. private void ProcessSelfIdentity (ConstraintStructure c)
  948. {
  949. // Basic concept came from XmlSchemaMapper.cs
  950. string tableName = c.TableName;
  951. DataTable dt = dataset.Tables [tableName];
  952. if (dt == null) {
  953. if (forDataSet)
  954. throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
  955. else
  956. // nonexistent table name. .NET ignores it for DataTable.ReadXmlSchema().
  957. return;
  958. }
  959. DataColumn [] cols = new DataColumn [c.Columns.Length];
  960. for (int i = 0; i < cols.Length; i++) {
  961. string colName = c.Columns [i];
  962. bool isAttr = c.IsAttribute [i];
  963. DataColumn col = dt.Columns [colName];
  964. if (col == null)
  965. throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
  966. if (isAttr && col.ColumnMapping != MappingType.Attribute)
  967. throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
  968. if (!isAttr && col.ColumnMapping != MappingType.Element)
  969. throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
  970. cols [i] = dt.Columns [colName];
  971. }
  972. bool isPK = c.IsPrimaryKey;
  973. string constraintName = c.ConstraintName;
  974. dt.Constraints.Add (new UniqueConstraint (
  975. constraintName, cols, isPK));
  976. }
  977. private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
  978. {
  979. // Basic concept came from XmlSchemaMapper.cs
  980. string tableName = GetSelectorTarget (keyref.Selector.XPath);
  981. string [] cols = new string [keyref.Fields.Count];
  982. bool [] isAttrSpec = new bool [cols.Length];
  983. int i = 0;
  984. foreach (XmlSchemaXPath Field in keyref.Fields) {
  985. string colName = Field.XPath;
  986. bool isAttr = colName.Length > 0 && colName [0] == '@';
  987. int index = colName.LastIndexOf (':');
  988. if (index > 0)
  989. colName = colName.Substring (index + 1);
  990. else if (isAttr)
  991. colName = colName.Substring (1);
  992. colName = XmlHelper.Decode (colName);
  993. cols [i] = colName;
  994. isAttrSpec [i] = isAttr;
  995. i++;
  996. }
  997. string constraintName = keyref.Name;
  998. bool isNested = false;
  999. bool isConstraintOnly = false;
  1000. if (keyref.UnhandledAttributes != null) {
  1001. foreach (XmlAttribute attr in keyref.UnhandledAttributes) {
  1002. if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
  1003. continue;
  1004. switch (attr.LocalName) {
  1005. case XmlConstants.ConstraintName:
  1006. constraintName = attr.Value;
  1007. break;
  1008. case XmlConstants.IsNested:
  1009. if (attr.Value == "true")
  1010. isNested = true;
  1011. break;
  1012. case XmlConstants.ConstraintOnly:
  1013. if (attr.Value == "true")
  1014. isConstraintOnly = true;
  1015. break;
  1016. }
  1017. }
  1018. }
  1019. reservedConstraints.Add (keyref, new ConstraintStructure (
  1020. tableName, cols, isAttrSpec, constraintName,
  1021. false, keyref.Refer.Name, isNested, isConstraintOnly));
  1022. }
  1023. private void ProcessRelationIdentity (XmlSchemaElement element, ConstraintStructure c)
  1024. {
  1025. // Basic concept came from XmlSchemaMapper.cs
  1026. string tableName = c.TableName;
  1027. DataColumn [] cols;
  1028. DataTable dt = dataset.Tables [tableName];
  1029. if (dt == null)
  1030. throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
  1031. cols = new DataColumn [c.Columns.Length];
  1032. for (int i = 0; i < cols.Length; i++) {
  1033. string colName = c.Columns [i];
  1034. bool isAttr = c.IsAttribute [i];
  1035. DataColumn col = dt.Columns [colName];
  1036. if (isAttr && col.ColumnMapping != MappingType.Attribute)
  1037. throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
  1038. if (!isAttr && col.ColumnMapping != MappingType.Element)
  1039. throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
  1040. cols [i] = col;
  1041. }
  1042. string name = c.ReferName;
  1043. // get the unique constraint for the releation
  1044. UniqueConstraint uniq = FindConstraint (name, element);
  1045. // generate the FK.
  1046. ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
  1047. dt.Constraints.Add (fkc);
  1048. if (!c.IsConstraintOnly) {
  1049. // generate the relation.
  1050. DataRelation rel = new DataRelation (c.ConstraintName, uniq.Columns, cols, true);
  1051. rel.Nested = c.IsNested;
  1052. rel.SetParentKeyConstraint (uniq);
  1053. rel.SetChildKeyConstraint (fkc);
  1054. dataset.Relations.Add (rel);
  1055. }
  1056. }
  1057. // get the unique constraint for the relation.
  1058. // name - the name of the XmlSchemaUnique element
  1059. private UniqueConstraint FindConstraint (string name, XmlSchemaElement element)
  1060. {
  1061. // Copied from XmlSchemaMapper.cs
  1062. // find the element in the constraint collection.
  1063. foreach (XmlSchemaIdentityConstraint c in element.Constraints) {
  1064. if (c is XmlSchemaKeyref)
  1065. continue;
  1066. if (c.Name == name) {
  1067. string tableName = GetSelectorTarget (c.Selector.XPath);
  1068. // find the table in the dataset.
  1069. DataTable dt = dataset.Tables [tableName];
  1070. string constraintName = c.Name;
  1071. // find if there is an attribute with the constraint name
  1072. // if not use the XmlSchemaUnique name.
  1073. if (c.UnhandledAttributes != null)
  1074. foreach (XmlAttribute attr in c.UnhandledAttributes)
  1075. if (attr.LocalName == "ConstraintName" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
  1076. constraintName = attr.Value;
  1077. return (UniqueConstraint) dt.Constraints [constraintName];
  1078. }
  1079. }
  1080. throw new DataException ("Target identity constraint was not found: " + name);
  1081. }
  1082. private void HandleAnnotations (XmlSchemaAnnotation an, bool nested)
  1083. {
  1084. foreach (XmlSchemaObject content in an.Items) {
  1085. XmlSchemaAppInfo ai = content as XmlSchemaAppInfo;
  1086. if (ai != null) {
  1087. foreach (XmlNode n in ai.Markup) {
  1088. XmlElement el = n as XmlElement;
  1089. // #325464 debugging
  1090. //Console.WriteLine ("Name: " + el.LocalName + " NS: " + el.NamespaceURI + " Const: " + XmlConstants.MsdataNamespace);
  1091. if (el != null && el.LocalName == "Relationship" && el.NamespaceURI == XmlConstants.MsdataNamespace)
  1092. HandleRelationshipAnnotation (el, nested);
  1093. #if NET_2_0
  1094. if (el != null && el.LocalName == "DataSource" && el.NamespaceURI == XmlConstants.MsdatasourceNamespace)
  1095. HandleDataSourceAnnotation (el, nested);
  1096. #endif
  1097. }
  1098. }
  1099. }
  1100. }
  1101. #if NET_2_0
  1102. private void HandleDataSourceAnnotation (XmlElement el, bool nested)
  1103. {
  1104. // Handle: Connections and Tables
  1105. // For Tables: extract the provider information from connection and use
  1106. // the corresponding providerfactory to create the adapter and et al objects
  1107. // and populate them
  1108. // #325464 debugging
  1109. //Console.WriteLine ("In HandleDataSourceAnnotation... ");
  1110. string providerName = null;
  1111. string connString = null;
  1112. DbProviderFactory provider;
  1113. XmlElement e;
  1114. foreach (XmlNode n in el.ChildNodes) {
  1115. e = n as XmlElement;
  1116. if (e == null)
  1117. continue;
  1118. if (e.LocalName == "Connections") {
  1119. providerName = ((XmlElement)e.FirstChild).GetAttribute ("Provider");
  1120. connString = ((XmlElement)e.FirstChild).GetAttribute ("AppSettingsPropertyName");
  1121. continue;
  1122. }
  1123. // #325464 debugging
  1124. //Console.WriteLine ("ProviderName: " + providerName + "Connstr: " + connString);
  1125. provider = DbProviderFactories.GetFactory (providerName);
  1126. if (e.LocalName == "Tables") {
  1127. foreach (XmlNode node in e.ChildNodes) {
  1128. ProcessTableAdapter (node as XmlElement, provider, connString);
  1129. }
  1130. }
  1131. // #325464 debugging
  1132. //Console.WriteLine (e.LocalName);
  1133. }
  1134. // #325464 debugging
  1135. //Console.WriteLine ("... exit");
  1136. }
  1137. private void ProcessTableAdapter (XmlElement el, DbProviderFactory provider, string connStr)
  1138. {
  1139. XmlElement e;
  1140. string datasetTableName = null;
  1141. if (el == null)
  1142. return;
  1143. // #325464 debugging
  1144. //Console.WriteLine ("in ProcessTableAdapters...");
  1145. currentAdapter = new TableAdapterSchemaInfo (provider);
  1146. currentAdapter.ConnectionString = connStr;
  1147. //Console.WriteLine ("Provider: {0}, connection: {1}, adapter: {2}",
  1148. // provider, currentAdapter.Connection, currentAdapter.Adapter);
  1149. currentAdapter.BaseClass = el.GetAttribute ("BaseClass");
  1150. datasetTableName = el.GetAttribute ("Name");
  1151. currentAdapter.Name = el.GetAttribute ("GeneratorDataComponentClassName");
  1152. if (currentAdapter.Name == null ||
  1153. currentAdapter.Name == String.Empty)
  1154. currentAdapter.Name = el.GetAttribute ("DataAccessorName");
  1155. //Console.WriteLine ("Name: "+currentAdapter.Name);
  1156. foreach (XmlNode n in el.ChildNodes) {
  1157. e = n as XmlElement;
  1158. //Console.WriteLine ("Children of Tables: "+e.LocalName);
  1159. if (e == null)
  1160. continue;
  1161. switch (e.LocalName) {
  1162. case "MainSource":
  1163. case "Sources":
  1164. foreach (XmlNode msn in e.ChildNodes)
  1165. ProcessDbSource (msn as XmlElement);
  1166. break;
  1167. case "Mappings":
  1168. DataTableMapping tableMapping = new DataTableMapping ();
  1169. tableMapping.SourceTable = "Table";
  1170. tableMapping.DataSetTable = datasetTableName;
  1171. foreach (XmlNode mps in e.ChildNodes)
  1172. ProcessColumnMapping (mps as XmlElement, tableMapping);
  1173. currentAdapter.Adapter.TableMappings.Add (tableMapping);
  1174. break;
  1175. }
  1176. }
  1177. }
  1178. private void ProcessDbSource (XmlElement el)
  1179. {
  1180. string cmdType;
  1181. string tmp = null;
  1182. XmlElement e;
  1183. if (el == null)
  1184. return;
  1185. //Console.WriteLine ("ProcessDbSources: "+el.LocalName);
  1186. tmp = el.GetAttribute ("GenerateShortCommands");
  1187. //Console.WriteLine ("GenerateShortCommands: {0}", tmp);
  1188. if (tmp != null && tmp != String.Empty)
  1189. currentAdapter.ShortCommands = Convert.ToBoolean (tmp);
  1190. DbCommandInfo cmdInfo = new DbCommandInfo ();
  1191. tmp = el.GetAttribute ("GenerateMethods");
  1192. if (tmp != null && tmp != String.Empty) {
  1193. DbSourceMethodInfo mthdInfo = null;
  1194. switch ((GenerateMethodsType) Enum.Parse (typeof (GenerateMethodsType), tmp)) {
  1195. case GenerateMethodsType.Get:
  1196. mthdInfo = new DbSourceMethodInfo ();
  1197. mthdInfo.Name = el.GetAttribute ("GetMethodName");
  1198. mthdInfo.Modifier = el.GetAttribute ("GetMethodModifier");
  1199. if (mthdInfo.Modifier == String.Empty)
  1200. mthdInfo.Modifier = "Public";
  1201. mthdInfo.ScalarCallRetval = el.GetAttribute ("ScalarCallRetval");
  1202. mthdInfo.QueryType = el.GetAttribute ("QueryType");
  1203. mthdInfo.MethodType = GenerateMethodsType.Get;
  1204. cmdInfo.Methods = new DbSourceMethodInfo [1];
  1205. cmdInfo.Methods[0] = mthdInfo;
  1206. break;
  1207. case GenerateMethodsType.Fill:
  1208. mthdInfo = new DbSourceMethodInfo ();
  1209. mthdInfo.Name = el.GetAttribute ("FillMethodName");
  1210. mthdInfo.Modifier = el.GetAttribute ("FillMethodModifier");
  1211. if (mthdInfo.Modifier == String.Empty)
  1212. mthdInfo.Modifier = "Public";
  1213. mthdInfo.ScalarCallRetval = null;
  1214. mthdInfo.QueryType = null;
  1215. mthdInfo.MethodType = GenerateMethodsType.Fill;
  1216. cmdInfo.Methods = new DbSourceMethodInfo [1];
  1217. cmdInfo.Methods[0] = mthdInfo;
  1218. break;
  1219. case GenerateMethodsType.Both:
  1220. mthdInfo = new DbSourceMethodInfo ();
  1221. // Get
  1222. mthdInfo.Name = el.GetAttribute ("GetMethodName");
  1223. mthdInfo.Modifier = el.GetAttribute ("GetMethodModifier");
  1224. if (mthdInfo.Modifier == String.Empty)
  1225. mthdInfo.Modifier = "Public";
  1226. mthdInfo.ScalarCallRetval = el.GetAttribute ("ScalarCallRetval");
  1227. mthdInfo.QueryType = el.GetAttribute ("QueryType");
  1228. mthdInfo.MethodType = GenerateMethodsType.Get;
  1229. cmdInfo.Methods = new DbSourceMethodInfo [2];
  1230. cmdInfo.Methods[0] = mthdInfo;
  1231. // Fill
  1232. mthdInfo = new DbSourceMethodInfo ();
  1233. mthdInfo.Name = el.GetAttribute ("FillMethodName");
  1234. mthdInfo.Modifier = el.GetAttribute ("FillMethodModifier");
  1235. if (mthdInfo.Modifier == String.Empty)
  1236. mthdInfo.Modifier = "Public";
  1237. mthdInfo.ScalarCallRetval = null;
  1238. mthdInfo.QueryType = null;
  1239. mthdInfo.MethodType = GenerateMethodsType.Fill;
  1240. cmdInfo.Methods[1] = mthdInfo;
  1241. break;
  1242. }
  1243. } else {
  1244. // no Get or Fill methods - non <MainSource> sources
  1245. DbSourceMethodInfo mthdInfo = new DbSourceMethodInfo ();
  1246. mthdInfo.Name = el.GetAttribute ("Name");
  1247. mthdInfo.Modifier = el.GetAttribute ("Modifier");
  1248. if (mthdInfo.Modifier == String.Empty)
  1249. mthdInfo.Modifier = "Public";
  1250. mthdInfo.ScalarCallRetval = el.GetAttribute ("ScalarCallRetval");
  1251. mthdInfo.QueryType = el.GetAttribute ("QueryType");
  1252. mthdInfo.MethodType = GenerateMethodsType.None;
  1253. // Add MethodInfo to DbCommandInfo
  1254. cmdInfo.Methods = new DbSourceMethodInfo [1];
  1255. cmdInfo.Methods[0] = mthdInfo;
  1256. }
  1257. foreach (XmlNode n in el.ChildNodes) {
  1258. e = n as XmlElement;
  1259. if (e == null)
  1260. continue;
  1261. switch (e.LocalName) {
  1262. case "SelectCommand":
  1263. cmdInfo.Command = ProcessDbCommand (e.FirstChild as XmlElement);
  1264. currentAdapter.Commands.Add (cmdInfo);
  1265. break;
  1266. case "InsertCommand":
  1267. currentAdapter.Adapter.InsertCommand = ProcessDbCommand (e.FirstChild as XmlElement);
  1268. break;
  1269. case "UpdateCommand":
  1270. currentAdapter.Adapter.UpdateCommand = ProcessDbCommand (e.FirstChild as XmlElement);
  1271. break;
  1272. case "DeleteCommand":
  1273. currentAdapter.Adapter.DeleteCommand = ProcessDbCommand (e.FirstChild as XmlElement);
  1274. break;
  1275. }
  1276. }
  1277. }
  1278. private DbCommand ProcessDbCommand (XmlElement el)
  1279. {
  1280. XmlElement e;
  1281. //Console.WriteLine (el.LocalName);
  1282. string cmdText = null;
  1283. string cmdType = null;
  1284. ArrayList parameters = null;
  1285. if (el == null)
  1286. return null;
  1287. cmdType = el.GetAttribute ("CommandType");
  1288. foreach (XmlNode n in el.ChildNodes) {
  1289. e = n as XmlElement;
  1290. if (e != null && e.LocalName == "CommandText")
  1291. cmdText = e.InnerText;
  1292. else if (e != null && e.LocalName == "Parameters" && !e.IsEmpty)
  1293. parameters = ProcessDbParameters (e);
  1294. }
  1295. DbCommand cmd = currentAdapter.Provider.CreateCommand ();
  1296. cmd.CommandText = cmdText;
  1297. if (cmdType == "StoredProcedure")
  1298. cmd.CommandType = CommandType.StoredProcedure;
  1299. else
  1300. cmd.CommandType = CommandType.Text;
  1301. if (parameters != null)
  1302. cmd.Parameters.AddRange (parameters.ToArray ());
  1303. //Console.WriteLine ("Parameters count: {0}", cmd.Parameters.Count);
  1304. return cmd;
  1305. }
  1306. private ArrayList ProcessDbParameters (XmlElement el)
  1307. {
  1308. //Console.WriteLine ("ProcessDbParameters: "+el.LocalName);
  1309. string tmp = null;
  1310. XmlElement e;
  1311. DbParameter param = null;
  1312. ArrayList parameters = new ArrayList ();
  1313. if (el == null)
  1314. return parameters;
  1315. foreach (XmlNode n in el.ChildNodes) {
  1316. e = n as XmlElement;
  1317. if (e == null)
  1318. continue;
  1319. param = currentAdapter.Provider.CreateParameter ();
  1320. tmp = e.GetAttribute ("AllowDbNull");
  1321. if (tmp != null && tmp != String.Empty)
  1322. param.IsNullable = Convert.ToBoolean (tmp);
  1323. param.ParameterName = e.GetAttribute ("ParameterName");
  1324. tmp = e.GetAttribute ("ProviderType");
  1325. if (tmp == null || tmp == String.Empty)
  1326. tmp = e.GetAttribute ("DbType");
  1327. param.FrameworkDbType = tmp;
  1328. tmp = e.GetAttribute ("Direction");
  1329. param.Direction = (ParameterDirection) Enum.Parse (typeof (ParameterDirection), tmp);
  1330. ((IDbDataParameter)param).Precision = Convert.ToByte (e.GetAttribute ("Precision"));
  1331. ((IDbDataParameter)param).Scale = Convert.ToByte (e.GetAttribute ("Scale"));
  1332. param.Size = Convert.ToInt32 (e.GetAttribute ("Size"));
  1333. param.SourceColumn = e.GetAttribute ("SourceColumn");
  1334. tmp = e.GetAttribute ("SourceColumnNullMapping");
  1335. if (tmp != null && tmp != String.Empty)
  1336. param.SourceColumnNullMapping = Convert.ToBoolean (tmp);
  1337. tmp = e.GetAttribute ("SourceVersion");
  1338. param.SourceVersion = (DataRowVersion) Enum.Parse (typeof (DataRowVersion), tmp);
  1339. parameters.Add (param);
  1340. }
  1341. return parameters;
  1342. }
  1343. private void ProcessColumnMapping (XmlElement el, DataTableMapping tableMapping)
  1344. {
  1345. if (el == null)
  1346. return;
  1347. tableMapping.ColumnMappings.Add (el.GetAttribute ("SourceColumn"),
  1348. el.GetAttribute ("DataSetColumn"));
  1349. }
  1350. #endif
  1351. private void HandleRelationshipAnnotation (XmlElement el, bool nested)
  1352. {
  1353. string name = el.GetAttribute ("name");
  1354. string ptn = el.GetAttribute ("parent", XmlConstants.MsdataNamespace);
  1355. string ctn = el.GetAttribute ("child", XmlConstants.MsdataNamespace);
  1356. string pkn = el.GetAttribute ("parentkey", XmlConstants.MsdataNamespace);
  1357. string fkn = el.GetAttribute ("childkey", XmlConstants.MsdataNamespace);
  1358. RelationStructure rel = new RelationStructure ();
  1359. rel.ExplicitName = XmlHelper.Decode (name);
  1360. rel.ParentTableName = XmlHelper.Decode (ptn);
  1361. rel.ChildTableName = XmlHelper.Decode (ctn);
  1362. // ColumnNames will be decoded wherever they are used as they can
  1363. // contain 'space' separated list of column-names.
  1364. rel.ParentColumnName = pkn;
  1365. rel.ChildColumnName = fkn;
  1366. rel.IsNested = nested;
  1367. rel.CreateConstraint = false; // by default?
  1368. relations.Add (rel);
  1369. }
  1370. private object GetElementDefaultValue (XmlSchemaElement elem)
  1371. {
  1372. // Unlike attribute, element cannot have a default value.
  1373. if (elem.RefName == XmlQualifiedName.Empty)
  1374. return elem.DefaultValue;
  1375. XmlSchemaElement referenced = schema.Elements [elem.RefName] as XmlSchemaElement;
  1376. if (referenced == null) // considering missing sub components
  1377. return null;
  1378. return referenced.DefaultValue;
  1379. }
  1380. private object GetAttributeDefaultValue (XmlSchemaAttribute attr)
  1381. {
  1382. #if BUGGY_MS_COMPATIBLE
  1383. if (attr == null)
  1384. return null;
  1385. else if (attr.RefName != XmlQualifiedName.Empty) {
  1386. XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
  1387. if (referenced != null)
  1388. return referenced.DefaultValue;
  1389. else
  1390. return null;
  1391. }
  1392. if (attr.DefaultValue != null)
  1393. return attr.DefaultValue;
  1394. return attr.FixedValue;
  1395. #else
  1396. if (attr.DefaultValue != null)
  1397. return attr.DefaultValue;
  1398. else if (attr.FixedValue != null)
  1399. return attr.FixedValue;
  1400. else if (attr.RefName == XmlQualifiedName.Empty)
  1401. return null;
  1402. XmlSchemaAttribute referenced = schema.Attributes [attr.RefName] as XmlSchemaAttribute;
  1403. if (referenced == null) // considering missing sub components
  1404. return null;
  1405. if (referenced.DefaultValue != null)
  1406. return referenced.DefaultValue;
  1407. return referenced.FixedValue;
  1408. #endif
  1409. }
  1410. }
  1411. }