Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/mcs
diff options
context:
space:
mode:
authorAtsushi Eno <atsushieno@gmail.com>2004-04-19 06:25:47 +0400
committerAtsushi Eno <atsushieno@gmail.com>2004-04-19 06:25:47 +0400
commitc57d2819bef08b8622ec717a8c5a28b956becb62 (patch)
tree001ada1404549c6d13ea4423fbf97f695d1ccb05 /mcs
parentd2e97ea2dd056552fb65a984bace1261ba5d0907 (diff)
2004-04-19 Atsushi Enomoto <atsushi@ximian.com>
* Added XmlSchemaDataImporter.cs (new schema reader). * DataSet.cs : Use XmlSchemaDataImporter (right now for DataSet only) for ReadXmlSchema(). Let schema reading work to ReadXmlSchema(). Don't return ReadSchema when argument read mode was DiffGram. svn path=/trunk/mcs/; revision=25673
Diffstat (limited to 'mcs')
-rw-r--r--mcs/class/System.Data/System.Data/ChangeLog8
-rw-r--r--mcs/class/System.Data/System.Data/DataSet.cs24
-rwxr-xr-xmcs/class/System.Data/System.Data/XmlSchemaDataImporter.cs656
3 files changed, 678 insertions, 10 deletions
diff --git a/mcs/class/System.Data/System.Data/ChangeLog b/mcs/class/System.Data/System.Data/ChangeLog
index defe11c3dd0..1e113a43504 100644
--- a/mcs/class/System.Data/System.Data/ChangeLog
+++ b/mcs/class/System.Data/System.Data/ChangeLog
@@ -1,3 +1,11 @@
+2004-04-19 Atsushi Enomoto <atsushi@ximian.com>
+
+ * Added XmlSchemaDataImporter.cs (new schema reader).
+ * DataSet.cs : Use XmlSchemaDataImporter (right now for DataSet only)
+ for ReadXmlSchema().
+ Let schema reading work to ReadXmlSchema().
+ Don't return ReadSchema when argument read mode was DiffGram.
+
2004-04-15 Umadevi S (sumadevi@novell.com)
* Updated Clone/Copy methods in DataSet along with Testcase
* ForeignKeyConstraint.cs - special ctor implemented
diff --git a/mcs/class/System.Data/System.Data/DataSet.cs b/mcs/class/System.Data/System.Data/DataSet.cs
index 01f76127627..669d3bcd3db 100644
--- a/mcs/class/System.Data/System.Data/DataSet.cs
+++ b/mcs/class/System.Data/System.Data/DataSet.cs
@@ -751,8 +751,12 @@ namespace System.Data {
public void ReadXmlSchema (XmlReader reader)
{
+#if true
+ new XmlSchemaDataImporter (this, reader);
+#else
XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
SchemaMapper.Read (reader);
+#endif
}
public XmlReadMode ReadXml (Stream stream)
@@ -837,7 +841,6 @@ namespace System.Data {
return mode;
}
}
- XmlSchemaMapper SchemaMapper = null;
// If schema, then read the first element as schema
if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
switch (mode) {
@@ -847,21 +850,22 @@ namespace System.Data {
// (and break up read)
return mode;
case XmlReadMode.Fragment:
- SchemaMapper = new XmlSchemaMapper (this);
- SchemaMapper.Read (reader);
+ ReadXmlSchema (reader);
// (and continue to read)
break;
case XmlReadMode.Auto:
- if (Tables.Count == 0)
- goto default;
+ if (Tables.Count == 0) {
+ ReadXmlSchema (reader);
+ return XmlReadMode.ReadSchema;
+ } else {
// otherwise just ignore and return IgnoreSchema
- reader.Skip ();
- return XmlReadMode.IgnoreSchema;
+ reader.Skip ();
+ return XmlReadMode.IgnoreSchema;
+ }
default:
- SchemaMapper = new XmlSchemaMapper (this);
- SchemaMapper.Read (reader);
+ ReadXmlSchema (reader);
// (and leave rest of the reader as is)
- return XmlReadMode.ReadSchema;
+ return mode; // When DiffGram, return DiffGram
}
}
// Otherwise, read as dataset... but only when required.
diff --git a/mcs/class/System.Data/System.Data/XmlSchemaDataImporter.cs b/mcs/class/System.Data/System.Data/XmlSchemaDataImporter.cs
new file mode 100755
index 00000000000..d9ae1ea6081
--- /dev/null
+++ b/mcs/class/System.Data/System.Data/XmlSchemaDataImporter.cs
@@ -0,0 +1,656 @@
+//
+// XmlSchemaDataImporter.cs
+//
+// Author:
+// Atsushi Enomoto <atsushi@ximian.com>
+//
+// (C)2004 Novell Inc.
+//
+//
+//
+// * Design Notes
+//
+// ** Abstract
+//
+// This class is used to import an XML Schema into a DataSet schema.
+//
+// Only XmlReader is acceptable as the input to the class.
+// This class is not expected to read XML Schema multi time.
+//
+// ** Targetable Schema Components
+//
+// Only global global elements that hold complex type are converted
+// into a table.
+// <del>
+// The components of the type of the element are subsequently converted
+// into a table, BUT there is an exception. As for "DataSet elements",
+// the type is just ignored (see "DataSet Element definition" below).
+// </del><ins>
+// The components of the type of the element are subsequently converted
+// into a table. As for "DataSet elements", its complex type is also
+// handled.
+// </ins>
+//
+// Unused complex types are never be converted.
+//
+// Global simple types and global attributes are never converted.
+// They cannot be a table.
+// Local complex types are also converted into a table.
+//
+// Local elements are converted into either a table or a column in
+// the "context DataTable". Simple-typed element is not always converted
+// into a DataColumn; if maxOccurs > 1, it will be converted as a table.
+// (FIXME: Not implemented yet)
+//
+// ** Name Convention
+//
+// Ignore this section. Microsoft.NET was buggy enough to confuse
+// against these name conflicts.
+//
+// Since local complex types are anonymous, we have to name for each
+// component. Thus, and since complex types and elements can have the
+// same name each other, we have to manage a table for mappings from
+// a name to a component. The names must be also used in DataRelation
+// definitions correctly.
+//
+// ** DataSet element definition
+//
+// "DataSet element" is 1) such element that has an attribute
+// msdata:IsDataSet (where prefix "msdata" is bound to
+// urn:schemas-microsoft-com:xml-msdata), or 2) the only one
+// element definition in the schema.
+//
+// There is another complicated rule. 1) If there is only one element EL
+// in the schema, and 2) if the type of EL is complex named CT, and 3)
+// the content of the CT is a group base, and 4) the group base contains
+// an element EL2, and finally 5) if EL2 is complex, THEN the element is
+// the DataSet element.
+//
+// Only the first global element that matches the condition above is
+// regarded as DataSet element (by necessary design or just a bug?)
+// instead of handling as an error.
+//
+// All global elements are considered as an alternative in the dataset
+// element.
+//
+// For local elements, msdata:IsDataSet are just ignored.
+//
+// ** Importing Complex Types as Columns
+//
+// When an xs:element is going to be mapped, its complex type (remember
+// that only complex-typed elements are targettable) are expanded to
+// DataColumn.
+//
+// DataColumn has a property MappingType that shows whether this column
+// came from attribute or element.
+//
+// [Question: How about MappingType.Simple? How is it used?]
+//
+// Additionally, for particle elements, it might also create another
+// DataTable (but for the particle elements in context DataTable, it
+// will create an index to the new table).
+//
+// For group base particles (XmlSchemaGroupBase; sequence, choice, all)
+// each component in those groups are mapped to a column. Even if you
+// import "choice" or "all" components, DataSet.WriteXmlSchema() will
+// output them just as a "sequence".
+//
+// Columns cannot be added directly to current context DataTable; they
+// need to be added after processing all the columns, because they may
+// have msdata:Ordinal attribute that specifies the order of the columns
+// in the DataTable.
+//
+// "Nested elements" are not allowed. (Clarification required?)
+//
+// ** Identity Constraints and DataRelations
+//
+// Only constraints on "DataSet element" is considered. All other
+// constraint definitions are ignored. Note that it is DataSet that has
+// the property Relations (of type DataRelationCollection).
+//
+// xs:key and xs:unique are handled as the same (then both will be
+// serialized as xs:unique).
+//
+// The XPath expressions in the constraints are strictly limited; they
+// are expected to be expandable enough to be mappable for each
+//
+// * selector to "any_valid_XPath/is/OK/blah"
+// where "blah" is one of the DataTable name. It looks that
+// only the last QName section is significant and any heading
+// XPath step is OK (even if the mapped node does not exist).
+// * field to QName that is mapped to DataColumn in the DataTable
+// (even ./QName is not allowed)
+//
+// ** Informative References
+//
+// Generating DataSet Relational Structure from XML Schema (XSD)
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_generating_dataset_relational_structure_from_xsd.asp
+//
+
+using System;
+using System.Collections;
+using System.Data;
+using System.Globalization;
+using System.Xml;
+using System.Xml.Schema;
+
+
+namespace System.Data
+{
+ internal class XmlSchemaDataImporter
+ {
+ static XmlSchemaDataImporter ()
+ {
+ XmlSchema s = new XmlSchema ();
+ XmlSchemaAttribute a = new XmlSchemaAttribute ();
+ a.Name = "foo";
+ // FIXME: mcs looks to have a bug around static
+ // reference resolution. XmlSchema.Namespace should work.
+ a.SchemaTypeName = new XmlQualifiedName ("integer", System.Xml.Schema.XmlSchema.Namespace);
+ s.Items.Add (a);
+ s.Compile (null);
+ schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
+ }
+
+ static readonly XmlSchemaDatatype schemaIntegerType;
+
+ DataSet dataset;
+ DataTable table;
+ XmlSchema schema;
+
+ Hashtable nameToComponentMap = new Hashtable ();
+
+ // such element that has an attribute msdata:IsDataSet="true"
+ XmlSchemaElement datasetElement;
+
+ // choice alternatives in the "dataset element"
+ ArrayList topLevelElements = new ArrayList ();
+
+ // import target elements
+ ArrayList targetElements = new ArrayList ();
+
+ // The DataTable currently processing
+ DataTable contextTable;
+
+ // The columns and orders which will be added to the context
+ // table (See design notes; Because of the ordinal problem)
+ // [DataColumn] -> int ordinal
+ Hashtable currentOrdinalColumns = new Hashtable ();
+ ArrayList currentNonOrdinalColumns = new ArrayList ();
+ ArrayList currentColumnNames = new ArrayList ();
+
+ // .ctor()
+
+ public XmlSchemaDataImporter (DataSet dataset, XmlReader reader)
+ {
+ this.dataset = dataset;
+ ProcessMain (reader);
+ }
+
+ public XmlSchemaDataImporter (DataTable table, XmlReader reader)
+ {
+ this.table = table;
+ ProcessMain (reader);
+ }
+
+ // properties
+
+ public DataSet DataSet {
+ get { return dataset; }
+ }
+
+ public DataTable DataTable {
+ get { return table; }
+ }
+
+ public XmlSchema XmlSchema {
+ get { return schema; }
+ }
+
+ // methods
+
+ private void ProcessMain (XmlReader reader)
+ {
+ schema = XmlSchema.Read (reader, null);
+ // FIXME: Just XmlSchema.Namespace should work (mcs bug)
+ if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == System.Xml.Schema.XmlSchema.Namespace)
+ reader.ReadEndElement ();
+ schema.Compile (null);
+ if (schema.Id != null && dataset != null)
+ dataset.DataSetName = schema.Id; // default
+
+ foreach (XmlSchemaObject obj in schema.Items)
+ if (obj is XmlSchemaElement)
+ targetElements.Add (obj);
+
+ // This collection will grow up while processing elements.
+ int globals = targetElements.Count;
+
+ for (int i = 0; i < globals; i++)
+ ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
+
+ // Rest are local elements.
+ for (int i = globals; i < targetElements.Count; i++)
+ ProcessDataTableElement ((XmlSchemaElement) targetElements [i]);
+
+ if (datasetElement != null) {
+ // Handle constraints in the DataSet element. First keys.
+ foreach (XmlSchemaObject obj in datasetElement.Constraints)
+ if (! (obj is XmlSchemaKeyref))
+ ProcessParentKey ((XmlSchemaIdentityConstraint) obj);
+ // Then keyrefs.
+ foreach (XmlSchemaObject obj in datasetElement.Constraints)
+ if (obj is XmlSchemaKeyref)
+ ProcessReferenceKey (datasetElement, (XmlSchemaKeyref) obj);
+ }
+ }
+
+ private void ProcessGlobalElement (XmlSchemaElement el)
+ {
+ // If it is already registered (by resolving reference
+ // in previously-imported elements), just ignore.
+ if (nameToComponentMap.Contains (el))
+ return;
+
+ // Check if element is DataSet element
+ if (el.UnhandledAttributes != null) {
+ foreach (XmlAttribute attr in el.UnhandledAttributes) {
+ if (attr.LocalName == "IsDataSet" &&
+ attr.NamespaceURI == XmlConstants.MsdataNamespace) {
+ switch (attr.Value) {
+ case "true": // case sensitive
+ ProcessDataSetElement (el);
+ return;
+ case "false":
+ break;
+ default:
+ throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
+ }
+ }
+ }
+ }
+
+ // Read the design notes for detail... it is very complicated.
+ if (schema.Elements.Count == 1 &&
+ el.SchemaType is XmlSchemaComplexType) { // i.e. defined inline (IT IS required!! but wtf?)
+ XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
+ XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
+ XmlSchemaElement cpElem = gb != null && gb.Items.Count > 0 ? gb.Items [0] as XmlSchemaElement : null;
+ // What a complex condition!!!!!!!!!
+ if (cpElem != null && cpElem.ElementType is XmlSchemaComplexType) {
+ ProcessDataSetElement (el);
+ return;
+ }
+ }
+
+
+ // If type is not complex, just skip this element
+ if (! (el.ElementType is XmlSchemaComplexType))
+ return;
+
+ // Register as a top-level element
+ topLevelElements.Add (el);
+ // Create DataTable for this element
+ ProcessDataTableElement (el);
+ }
+
+ private void ProcessDataSetElement (XmlSchemaElement el)
+ {
+ if (dataset != null)
+ dataset.DataSetName = el.Name;
+ this.datasetElement = el;
+
+ // Search for msdata:Locale attribute
+ if (el.UnhandledAttributes != null) {
+ foreach (XmlAttribute attr in el.UnhandledAttributes) {
+ if (attr.LocalName == "Locale" &&
+ attr.NamespaceURI == XmlConstants.MsdataNamespace) {
+ CultureInfo ci = new CultureInfo (attr.Value);
+ if (dataset != null)
+ dataset.Locale = ci;
+ else
+ table.Locale = ci;
+ }
+ }
+ }
+
+ // Process content type particle (and create DataTable)
+ XmlSchemaComplexType ct = el.ElementType as XmlSchemaComplexType;
+ XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
+ if (p != null)
+ HandleDataSetContentTypeParticle (p);
+ }
+
+ private void HandleDataSetContentTypeParticle (XmlSchemaParticle p)
+ {
+ XmlSchemaElement el = p as XmlSchemaElement;
+ if (el != null) {
+ if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
+ ProcessDataTableElement (el);
+ }
+ else
+ foreach (XmlSchemaParticle pc in ((XmlSchemaGroupBase) p).Items)
+ HandleDataSetContentTypeParticle (pc);
+ }
+
+ private void ProcessDataTableElement (XmlSchemaElement el)
+ {
+ // If it is already registered, just ignore.
+ if (nameToComponentMap.Contains (el))
+ return;
+
+ // On reading schema into DataTable (not into DataSet)
+ // This operation should be an error.
+ if (table != null)
+ throw new InvalidOperationException ("More than one table is defined in this schema");
+ else if (contextTable != null)
+ throw new InvalidOperationException ("Should not occur. Request for data table definition was found on defining another data table.");
+
+ string name = el.QualifiedName.Name;
+
+ currentOrdinalColumns.Clear ();
+ currentNonOrdinalColumns.Clear ();
+ currentColumnNames.Clear ();
+ contextTable = new DataTable ();
+ contextTable.TableName = name;
+
+ dataset.Tables.Add (contextTable);
+ nameToComponentMap.Add (name, el);
+
+ // Handle complex type (NOTE: It is or should be
+ // impossible the type is other than complex type).
+ XmlSchemaComplexType ct = (XmlSchemaComplexType) el.ElementType;
+ // Handle attributes
+ foreach (DictionaryEntry de in ct.AttributeUses)
+ ImportColumnAttribute ((XmlSchemaAttribute) de.Value);
+
+ // Handle content type particle
+ if (ct.ContentTypeParticle is XmlSchemaElement)
+ ImportColumnElement ((XmlSchemaElement) ct.ContentTypeParticle);
+ else if (ct.ContentTypeParticle is XmlSchemaGroupBase)
+ ImportColumnGroupBase ((XmlSchemaGroupBase) ct.ContentTypeParticle);
+ // else if null then do nothing.
+
+ // add columns to the table in specified order
+ // (by msdata:Ordinal attributes)
+ SortedList sd = new SortedList ();
+ foreach (DictionaryEntry de in currentOrdinalColumns)
+ sd.Add (de.Value, de.Key);
+ foreach (DictionaryEntry de in sd)
+ contextTable.Columns.Add ((DataColumn) de.Value);
+ foreach (DataColumn dc in currentNonOrdinalColumns)
+ contextTable.Columns.Add (dc);
+
+ contextTable = null;
+ }
+
+ private void ImportColumnGroupBase (XmlSchemaGroupBase gb)
+ {
+ foreach (XmlSchemaParticle p in gb.Items) {
+ XmlSchemaElement el = p as XmlSchemaElement;
+ if (el != null)
+ ImportColumnElement (el);
+ else
+ ImportColumnGroupBase ((XmlSchemaGroupBase) gb);
+ }
+ }
+
+ private void ImportColumnAttribute (XmlSchemaAttribute attr)
+ {
+ DataColumn col = new DataColumn ();
+ col.ColumnName = attr.QualifiedName.Name;
+ XmlSchemaDatatype dt = attr.AttributeType as XmlSchemaDatatype;
+ if (dt == null && attr.AttributeType != null)
+ dt = ((XmlSchemaSimpleType) attr.AttributeType).Datatype;
+ col.DataType = ConvertDatatype (dt);
+ col.ColumnMapping = MappingType.Attribute;
+ col.DefaultValue = attr.DefaultValue;
+
+ // Call this method after filling the name
+ ImportColumnCommon (attr, attr.QualifiedName, col);
+ }
+
+ private void ImportColumnElement (XmlSchemaElement el)
+ {
+ // FIXME: element nest check
+
+ DataColumn col = new DataColumn ();
+ col.DefaultValue = el.DefaultValue;
+ col.AllowDBNull = (el.MinOccurs == 0);
+
+ // FIXME: need to handle array item for maxOccurs > 1
+ if (el.ElementType is XmlSchemaComplexType)
+ // import new table and set this column as reference.
+ FillDataColumnComplexElement (el, col);
+ else
+ FillDataColumnSimpleElement (el, col);
+
+ // Call this method after filling the name
+ ImportColumnCommon (el, el.QualifiedName, col);
+ }
+
+ // common process for element and attribute
+ private void ImportColumnCommon (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
+ {
+ col.Namespace = name.Namespace;
+
+ int ordinal = -1;
+ if (obj.UnhandledAttributes != null) {
+ foreach (XmlAttribute attr in obj.UnhandledAttributes) {
+ if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
+ continue;
+ switch (attr.LocalName) {
+ case XmlConstants.Caption:
+ col.Caption = attr.Value;
+ break;
+ case XmlConstants.DataType:
+ col.DataType = Type.GetType (attr.Value);
+ break;
+ case XmlConstants.AutoIncrement:
+ col.AutoIncrement = bool.Parse (attr.Value);
+ break;
+ case XmlConstants.AutoIncrementSeed:
+ col.AutoIncrementSeed = int.Parse (attr.Value);
+ break;
+ case XmlConstants.Ordinal:
+ ordinal = int.Parse (attr.Value);
+ break;
+ }
+ }
+ }
+ if (ordinal < 0)
+ currentNonOrdinalColumns.Add (col);
+ else
+ currentOrdinalColumns.Add (col, ordinal);
+ currentColumnNames.Add (col.ColumnName);
+ }
+
+ [MonoTODO]
+ private void FillDataColumnComplexElement (XmlSchemaElement el, DataColumn col)
+ {
+ // Those components will be handled later.
+ targetElements.Add (el);
+
+ // create identical name
+ string name = el.QualifiedName.Name + "_Id";
+ string nameTmp = name;
+ for (int i = 0; currentColumnNames.Contains (nameTmp); i++)
+ nameTmp = name + i;
+
+ col.ColumnName = nameTmp;
+ col.ColumnMapping = MappingType.Hidden;
+ }
+
+ private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
+ {
+ col.ColumnName = el.QualifiedName.Name;
+ col.ColumnMapping = MappingType.Element;
+ XmlSchemaDatatype dt = el.ElementType as XmlSchemaDatatype;
+ if (dt == null && el.ElementType != null)
+ dt = ((XmlSchemaSimpleType) el.ElementType).Datatype;
+ col.DataType = ConvertDatatype (dt);
+ XmlSchemaSimpleType st = el.ElementType as XmlSchemaSimpleType;
+ // Handle restriction facets
+ // FIXME: how to handle list and union??
+ XmlSchemaSimpleTypeRestriction restriction = st == null ? null : st.Content as XmlSchemaSimpleTypeRestriction;
+ if (restriction != null) {
+ foreach (XmlSchemaFacet f in restriction.Facets) {
+ if (f is XmlSchemaMaxLengthFacet)
+ // There is no reason why MaxLength is limited to int, except for the fact that DataColumn.MaxLength property is int.
+ col.MaxLength = int.Parse (f.Value);
+ }
+ }
+ }
+
+ private Type ConvertDatatype (XmlSchemaDatatype dt)
+ {
+ if (dt == null)
+ return typeof (string);
+ else if (dt == schemaIntegerType)
+ return typeof (long); // LAMESPEC: MSDN documentation says it is based on
+ else
+ return dt.ValueType;
+ }
+
+ // This method cuts out the local name of the last step from XPath.
+ // It is nothing more than hack. However, MS looks to do similar.
+ private string GetSelectorTarget (string xpath)
+ {
+ string tableName = xpath;
+ int index = tableName.LastIndexOf ('/');
+ // '>' is enough. If XPath [0] = '/', it is invalid.
+ // Selector can specify only element axes.
+ if (index > 0)
+ tableName = tableName.Substring (index + 1);
+
+ // Round QName to NSName
+ index = tableName.LastIndexOf (':');
+ if (index > 0)
+ tableName = tableName.Substring (index + 1);
+
+ return tableName;
+ }
+
+ private void ProcessParentKey (XmlSchemaIdentityConstraint ic)
+ {
+ // Basic concept came from XmlSchemaMapper.cs
+
+ string tableName = GetSelectorTarget (ic.Selector.XPath);
+
+ DataTable table = dataset.Tables [tableName];
+ if (table == null)
+ throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+
+ DataColumn [] cols = new DataColumn [ic.Fields.Count];
+ int i = 0;
+ foreach (XmlSchemaXPath Field in ic.Fields) {
+ string colName = Field.XPath;
+ // FIXME: attribute XPath
+ int index = colName.LastIndexOf (':');
+ if (index > 0)
+ colName = colName.Substring (index + 1);
+
+ DataColumn col = table.Columns [colName];
+ if (col == null)
+ throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
+
+ cols [i] = table.Columns [colName];
+ i++;
+ }
+
+ bool isPK = false;
+ // find if there is an attribute with the constraint name
+ // if not use the XmlSchemaConstraint's name.
+ string constraintName = ic.Name;
+ if (ic.UnhandledAttributes != null) {
+ foreach (XmlAttribute attr in ic.UnhandledAttributes) {
+ if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
+ continue;
+ switch (attr.LocalName) {
+ case XmlConstants.ConstraintName:
+ constraintName = attr.Value;
+ break;
+ case XmlConstants.PrimaryKey:
+ isPK = bool.Parse(attr.Value);
+ break;
+ }
+ }
+ }
+ UniqueConstraint c = new UniqueConstraint (constraintName, cols, isPK);
+ table.Constraints.Add (c);
+ }
+
+ private void ProcessReferenceKey (XmlSchemaElement element, XmlSchemaKeyref keyref)
+ {
+ // Basic concept came from XmlSchemaMapper.cs
+
+ string tableName = GetSelectorTarget (keyref.Selector.XPath);
+
+ DataColumn [] cols;
+ DataTable table = dataset.Tables [tableName];
+ if (table == null)
+ throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+
+ cols = new DataColumn [keyref.Fields.Count];
+ int i = 0;
+ foreach (XmlSchemaXPath Field in keyref.Fields) {
+ // FIXME: attribute XPath
+ string colName = Field.XPath;
+ int index = colName.LastIndexOf (':');
+ if (index != -1)
+ colName = colName.Substring (index + 1);
+
+ cols [i] = table.Columns [colName];
+ i++;
+ }
+ string name = keyref.Refer.Name;
+ // get the unique constraint for the releation
+ UniqueConstraint uniq = FindConstraint (name, element);
+ // generate the FK.
+ ForeignKeyConstraint fkc = new ForeignKeyConstraint(keyref.Name, uniq.Columns, cols);
+ table.Constraints.Add (fkc);
+ // generate the relation.
+ DataRelation rel = new DataRelation (keyref.Name, uniq.Columns, cols, false);
+ if (keyref.UnhandledAttributes != null) {
+ foreach (XmlAttribute attr in keyref.UnhandledAttributes)
+ if (attr.LocalName == "IsNested" && attr.Value == "true" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
+ rel.Nested = true;
+ }
+ rel.SetParentKeyConstraint (uniq);
+ rel.SetChildKeyConstraint (fkc);
+
+ dataset.Relations.Add (rel);
+ }
+
+ // get the unique constraint for the relation.
+ // name - the name of the XmlSchemaUnique element
+ private UniqueConstraint FindConstraint (string name, XmlSchemaElement element)
+ {
+ // Copied from XmlSchemaMapper.cs
+
+ // find the element in the constraint collection.
+ foreach (XmlSchemaIdentityConstraint c in element.Constraints) {
+ if (c is XmlSchemaKeyref)
+ continue;
+
+ if (c.Name == name) {
+ string tableName = GetSelectorTarget (c.Selector.XPath);
+
+ // find the table in the dataset.
+ DataTable table = dataset.Tables [tableName];
+
+ string constraintName = c.Name;
+ // find if there is an attribute with the constraint name
+ // if not use the XmlSchemaUnique name.
+ if (c.UnhandledAttributes != null)
+ foreach (XmlAttribute attr in c.UnhandledAttributes)
+ if (attr.LocalName == "ConstraintName" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
+ constraintName = attr.Value;
+ return (UniqueConstraint) table.Constraints [constraintName];
+ }
+ }
+ throw new DataException ("Target identity constraint was not found: " + name);
+ }
+
+ }
+}