// (C)2004 Novell Inc.
//
//
+// ***** The design note became somewhat obsolete. Should be rewritten. *****
//
// * Design Notes
//
// 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
//
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_generating_dataset_relational_structure_from_xsd.asp
//
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
using System;
using System.Collections;
using System.Data;
namespace System.Data
{
+ internal class TableStructureCollection : CollectionBase
+ {
+ public void Add (TableStructure table)
+ {
+ List.Add (table);
+ }
+
+ public TableStructure this [int i] {
+ get { return List [i] as TableStructure; }
+ }
+
+ public TableStructure this [string name] {
+ get {
+ foreach (TableStructure ts in List)
+ if (ts.Table.TableName == name)
+ return ts;
+ return null;
+ }
+ }
+ }
+
+ internal class RelationStructureCollection : CollectionBase
+ {
+ public void Add (RelationStructure rel)
+ {
+ List.Add (rel);
+ }
+
+ public RelationStructure this [int i] {
+ get { return List [i] as RelationStructure; }
+ }
+
+ public RelationStructure this [string parent, string child] {
+ get {
+ foreach (RelationStructure rel in List)
+ if (rel.ParentTableName == parent && rel.ChildTableName == child)
+ return rel;
+ return null;
+ }
+ }
+ }
+
+ internal class TableStructure
+ {
+ public TableStructure (DataTable table)
+ {
+ this.Table = table;
+ }
+
+ // The columns and orders which will be added to the context
+ // table (See design notes; Because of the ordinal problem)
+ public DataTable Table;
+ public Hashtable OrdinalColumns = new Hashtable ();
+ public ArrayList NonOrdinalColumns = new ArrayList ();
+ public DataColumn PrimaryKey;
+
+ public bool ContainsColumn (string name)
+ {
+ foreach (DataColumn col in NonOrdinalColumns)
+ if (col.ColumnName == name)
+ return true;
+ foreach (DataColumn col in OrdinalColumns.Keys)
+ if (col.ColumnName == name)
+ return true;
+ return false;
+ }
+ }
+
+ internal class RelationStructure
+ {
+ public string ExplicitName;
+ public string ParentTableName;
+ public string ChildTableName;
+ public string ParentColumnName;
+ public string ChildColumnName;
+ public bool IsNested;
+ public bool CreateConstraint;
+ }
+
+ internal class ConstraintStructure
+ {
+ public readonly string TableName;
+ public readonly string [] Columns;
+ public readonly bool [] IsAttribute;
+ public readonly string ConstraintName;
+ public readonly bool IsPrimaryKey;
+ public readonly string ReferName;
+ public readonly bool IsNested;
+ public readonly bool IsConstraintOnly;
+
+ public ConstraintStructure (string tname, string [] cols, bool [] isAttr, string cname, bool isPK, string refName, bool isNested, bool isConstraintOnly)
+ {
+ TableName = tname;
+ Columns = cols;
+ IsAttribute = isAttr;
+ ConstraintName = XmlHelper.Decode (cname);
+ IsPrimaryKey = isPK;
+ ReferName = refName;
+ IsNested = isNested;
+ IsConstraintOnly = isConstraintOnly;
+ }
+ }
+
internal class XmlSchemaDataImporter
{
+ static readonly XmlSchemaDatatype schemaIntegerType;
+ static readonly XmlSchemaDatatype schemaDecimalType;
+ static readonly XmlSchemaComplexType schemaAnyType;
+
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);
+ a.SchemaTypeName = new XmlQualifiedName ("integer", XmlSchema.Namespace);
s.Items.Add (a);
+ XmlSchemaAttribute b = new XmlSchemaAttribute ();
+ b.Name = "bar";
+ b.SchemaTypeName = new XmlQualifiedName ("decimal", XmlSchema.Namespace);
+ s.Items.Add (b);
XmlSchemaElement e = new XmlSchemaElement ();
e.Name = "bar";
s.Items.Add (e);
s.Compile (null);
+#if NET_2_0
+ schemaIntegerType = ((XmlSchemaSimpleType) a.AttributeSchemaType).Datatype;
+ schemaDecimalType = ((XmlSchemaSimpleType) b.AttributeSchemaType).Datatype;
+ schemaAnyType = e.ElementSchemaType as XmlSchemaComplexType;
+#else
schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
+ schemaDecimalType = b.AttributeType as XmlSchemaDatatype;
schemaAnyType = e.ElementType as XmlSchemaComplexType;
+#endif
}
- static readonly XmlSchemaDatatype schemaIntegerType;
- static readonly XmlSchemaComplexType schemaAnyType;
+ #region Fields
DataSet dataset;
- DataTable table;
+ bool forDataSet;
XmlSchema schema;
- Hashtable nameToComponentMap = new Hashtable ();
- Hashtable relationParentColumns = new Hashtable ();
- Hashtable nestedRelationColumns = new Hashtable ();
+ ArrayList relations = new ArrayList ();
+ Hashtable reservedConstraints = new Hashtable ();
// such element that has an attribute msdata:IsDataSet="true"
XmlSchemaElement datasetElement;
// import target elements
ArrayList targetElements = new ArrayList ();
- // The DataTable currently processing
- DataTable contextTable;
+ TableStructure currentTable;
- // 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 ();
-
- // used to determine if it handles the only one element as DataSet
- int globalElementCount;
+ #endregion
// .ctor()
- public XmlSchemaDataImporter (DataSet dataset, XmlReader reader)
+ public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
{
this.dataset = dataset;
- ProcessMain (reader);
- }
-
- public XmlSchemaDataImporter (DataTable table, XmlReader reader)
- {
- this.table = table;
- ProcessMain (reader);
- }
-
- // types
- struct RelationMapping
- {
- public RelationMapping (XmlSchemaElement el, DataTable table, DataColumn col)
- {
- Element = el;
- Table = table;
- Column = col;
- }
-
- public XmlSchemaElement Element;
- public DataTable Table;
- public DataColumn Column;
- }
-
- // properties
-
- public DataSet DataSet {
- get { return dataset; }
- }
-
- public DataTable DataTable {
- get { return table; }
- }
-
- public XmlSchema XmlSchema {
- get { return schema; }
+ this.forDataSet = forDataSet;
+ dataset.DataSetName = "NewDataSet"; // Initialize always
+ schema = XmlSchema.Read (reader, null);
+ if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace)
+ reader.ReadEndElement ();
+ schema.Compile (null);
}
// methods
- private void ProcessMain (XmlReader reader)
+ public void Process ()
{
- 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.DataSetName = schema.Id; // default
+ dataset.DataSetName = schema.Id; // default. Overridable by "DataSet element"
dataset.Namespace = schema.TargetNamespace;
+ // Find dataset element
+ foreach (XmlSchemaObject obj in schema.Items) {
+ XmlSchemaElement el = obj as XmlSchemaElement;
+ if (el != null) {
+ if (datasetElement == null &&
+ IsDataSetElement (el))
+ datasetElement = el;
+#if NET_2_0
+ if (el.ElementSchemaType is XmlSchemaComplexType &&
+ el.ElementSchemaType != schemaAnyType)
+#else
+ if (el.ElementType is XmlSchemaComplexType &&
+ el.ElementType != schemaAnyType)
+#endif
+ targetElements.Add (obj);
+ }
+ }
+
+ // make reservation of identity constraints
+ if (datasetElement != null) {
+ // keys/uniques.
+ foreach (XmlSchemaObject obj in datasetElement.Constraints)
+ if (! (obj is XmlSchemaKeyref))
+ ReserveSelfIdentity ((XmlSchemaIdentityConstraint) obj);
+ // keyrefs.
+ foreach (XmlSchemaObject obj in datasetElement.Constraints)
+ if (obj is XmlSchemaKeyref)
+ ReserveRelationIdentity (datasetElement, (XmlSchemaKeyref) obj);
+ }
+
foreach (XmlSchemaObject obj in schema.Items) {
if (obj is XmlSchemaElement) {
XmlSchemaElement el = obj as XmlSchemaElement;
+#if NET_2_0
+ if (el.ElementSchemaType is XmlSchemaComplexType &&
+ el.ElementSchemaType != schemaAnyType)
+#else
if (el.ElementType is XmlSchemaComplexType &&
-el.ElementType != schemaAnyType)
+ el.ElementType != schemaAnyType)
+#endif
targetElements.Add (obj);
}
}
// This collection will grow up while processing elements.
- globalElementCount = targetElements.Count;
+ int globalElementCount = targetElements.Count;
for (int i = 0; i < globalElementCount; i++)
ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
// Handle constraints in the DataSet element. First keys.
foreach (XmlSchemaObject obj in datasetElement.Constraints)
if (! (obj is XmlSchemaKeyref))
- ProcessParentKey ((XmlSchemaIdentityConstraint) obj);
+ ProcessSelfIdentity (reservedConstraints [obj] as ConstraintStructure);
// Then keyrefs.
foreach (XmlSchemaObject obj in datasetElement.Constraints)
if (obj is XmlSchemaKeyref)
- ProcessReferenceKey (datasetElement, (XmlSchemaKeyref) obj);
+ ProcessRelationIdentity (datasetElement, reservedConstraints [obj] as ConstraintStructure);
}
+
+ foreach (RelationStructure rs in this.relations)
+ dataset.Relations.Add (GenerateRelationship (rs));
}
- private void ProcessGlobalElement (XmlSchemaElement el)
+ private bool IsDataSetElement (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;
+ return true;
case "false":
break;
default:
}
}
- // FIXME: It should be more simple. It is likely to occur when the type was not resulted in a DataTable
-
- // Read the design notes for detail... it is very complicated.
+ if (schema.Elements.Count != 1)
+ return false;
+ if (!(el.SchemaType is XmlSchemaComplexType))
+ return false;
+ XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
+ if (ct.AttributeUses.Count > 0)
+ return false;
+ XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
+ if (gb == null || gb.Items.Count == 0)
+ return false;
+ foreach (XmlSchemaParticle p in gb.Items) {
+ if (ContainsColumn (p))
+ return false;
+ }
+ return true;
+ }
- if (globalElementCount == 1 && el.SchemaType is XmlSchemaComplexType) {
- 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;
- XmlSchemaComplexType ct2 = cpElem != null ? cpElem.ElementType as XmlSchemaComplexType : null;
- // What a complex condition!!!!!!!!!
- if (ct2 != null && ct != schemaAnyType && ct.AttributeUses.Count == 0) {
- ProcessDataSetElement (el);
- return;
- }
+ private bool ContainsColumn (XmlSchemaParticle p)
+ {
+ XmlSchemaElement el = p as XmlSchemaElement;
+ if (el != null) {
+ XmlSchemaComplexType ct = null;
+#if NET_2_0
+ ct = el.ElementSchemaType as XmlSchemaComplexType;
+#else
+ ct = el.ElementType as XmlSchemaComplexType;
+#endif
+ if (ct == null || ct == schemaAnyType)
+ return true; // column element
+ if (ct.AttributeUses.Count > 0)
+ return false; // table element
+ if (ct.ContentType == XmlSchemaContentType.TextOnly)
+ return true; // column element
+ else
+ return false; // table element
+ }
+ XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
+ for (int i = 0; i < gb.Items.Count; i++) {
+ if (ContainsColumn ((XmlSchemaParticle) gb.Items [i]))
+ return true;
}
+ return false;
+ }
+ private void ProcessGlobalElement (XmlSchemaElement el)
+ {
+ // If it is already registered (by resolving reference
+ // in previously-imported elements), just ignore.
+ if (dataset.Tables.Contains (el.QualifiedName.Name))
+ return;
// If type is not complex, just skip this element
+#if NET_2_0
+ if (! (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType))
+#else
if (! (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType))
+#endif
+ return;
+
+ if (IsDataSetElement (el)) {
+ ProcessDataSetElement (el);
return;
+ }
+ else
+ dataset.Locale = CultureInfo.CurrentCulture;
// Register as a top-level element
topLevelElements.Add (el);
dataset.DataSetName = el.Name;
this.datasetElement = el;
- // Search for msdata:Locale attribute
+ // Search for locale attributes
+ bool useCurrent = false;
if (el.UnhandledAttributes != null) {
foreach (XmlAttribute attr in el.UnhandledAttributes) {
+#if NET_2_0
+ if (attr.LocalName == "UseCurrentLocale" &&
+ attr.NamespaceURI == XmlConstants.MsdataNamespace)
+ useCurrent = true;
+#endif
if (attr.LocalName == "Locale" &&
attr.NamespaceURI == XmlConstants.MsdataNamespace) {
CultureInfo ci = new CultureInfo (attr.Value);
}
}
}
+#if NET_2_0
+ if (!useCurrent && !dataset.LocaleSpecified) // then set current culture instance _explicitly_
+ dataset.Locale = CultureInfo.CurrentCulture;
+#endif
// Process content type particle (and create DataTable)
- XmlSchemaComplexType ct = el.ElementType as XmlSchemaComplexType;
+ XmlSchemaComplexType ct = null;
+#if NET_2_0
+ ct = el.ElementSchemaType as XmlSchemaComplexType;
+#else
+ ct = el.ElementType as XmlSchemaComplexType;
+#endif
XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
if (p != null)
HandleDataSetContentTypeParticle (p);
{
XmlSchemaElement el = p as XmlSchemaElement;
if (el != null) {
+#if NET_2_0
+ if (el.ElementSchemaType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
+#else
if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
+#endif
ProcessDataTableElement (el);
}
else if (p is XmlSchemaGroupBase) {
private void ProcessDataTableElement (XmlSchemaElement el)
{
+ string tableName = XmlHelper.Decode (el.QualifiedName.Name);
// If it is already registered, just ignore.
- if (nameToComponentMap.Contains (el))
+ if (dataset.Tables.Contains (tableName))
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;
+ DataTable table = new DataTable (tableName);
+ table.Namespace = el.QualifiedName.Namespace;
+ TableStructure oldTable = currentTable;
+ currentTable = new TableStructure (table);
- currentOrdinalColumns.Clear ();
- currentNonOrdinalColumns.Clear ();
- currentColumnNames.Clear ();
- contextTable = new DataTable ();
- contextTable.TableName = name;
-
- dataset.Tables.Add (contextTable);
- nameToComponentMap.Add (name, el);
+ dataset.Tables.Add (table);
// Find Locale
if (el.UnhandledAttributes != null) {
foreach (XmlAttribute attr in el.UnhandledAttributes) {
if (attr.LocalName == "Locale" &&
attr.NamespaceURI == XmlConstants.MsdataNamespace)
- contextTable.Locale = new CultureInfo (attr.Value);
+ table.Locale = new CultureInfo (attr.Value);
}
}
- // Handle complex type (NOTE: It is or should be
+ // Handle complex type (NOTE: It is (or should be)
// impossible the type is other than complex type).
- XmlSchemaComplexType ct = (XmlSchemaComplexType) el.ElementType;
+ XmlSchemaComplexType ct = null;
+#if NET_2_0
+ ct = (XmlSchemaComplexType) el.ElementSchemaType;
+#else
+ ct = (XmlSchemaComplexType) el.ElementType;
+#endif
// Handle attributes
foreach (DictionaryEntry de in ct.AttributeUses)
// Handle simple content
switch (ct.ContentType) {
case XmlSchemaContentType.TextOnly:
- case XmlSchemaContentType.Mixed:
+// case XmlSchemaContentType.Mixed:
// LAMESPEC: When reading from XML Schema, it maps to "_text", while on the data inference, it is mapped to "_Text" (case ignorant).
string simpleName = el.QualifiedName.Name + "_text";
DataColumn simple = new DataColumn (simpleName);
+ simple.Namespace = el.QualifiedName.Namespace;
simple.AllowDBNull = (el.MinOccurs == 0);
simple.ColumnMapping = MappingType.SimpleContent;
simple.DataType = ConvertDatatype (ct.Datatype);
- currentNonOrdinalColumns.Add (simple);
- currentColumnNames.Add (simpleName);
+ currentTable.NonOrdinalColumns.Add (simple);
break;
}
// add columns to the table in specified order
// (by msdata:Ordinal attributes)
SortedList sd = new SortedList ();
- foreach (DictionaryEntry de in currentOrdinalColumns)
+ foreach (DictionaryEntry de in currentTable.OrdinalColumns)
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);
-
- HandlePopulatedRelationship (el);
+ table.Columns.Add ((DataColumn) de.Value);
+ foreach (DataColumn dc in currentTable.NonOrdinalColumns)
+ table.Columns.Add (dc);
- contextTable = null;
+ currentTable = oldTable;
}
- private void HandlePopulatedRelationship (XmlSchemaElement el)
+ private DataRelation GenerateRelationship (RelationStructure rs)
{
- // If there is a parent column, create DataRelation and pk
- DataColumn col = relationParentColumns [el] as DataColumn;
- if (col != null) {
- if (col.Table.PrimaryKey.Length > 0)
- throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", contextTable.TableName));
- col.Table.PrimaryKey = new DataColumn [] {col};
+ DataTable ptab = dataset.Tables [rs.ParentTableName];
+ DataTable ctab = dataset.Tables [rs.ChildTableName];
- DataColumn child = new DataColumn ();
- child.ColumnName = col.ColumnName;
- child.Namespace = String.Empty; // don't copy
- child.ColumnMapping = MappingType.Hidden;
- child.DataType = col.DataType;
- contextTable.Columns.Add (child);
+ DataRelation rel ;
+ string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
- DataRelation rel = new DataRelation (col.Table.TableName + '_' + child.Table.TableName, col, child);
- if (nestedRelationColumns.ContainsValue (col))
- rel.Nested = true;
- dataset.Relations.Add (rel);
+ // Annotation Relations belonging to a DataSet can contain multiple colnames
+ // in parentkey and childkey.
+ if (datasetElement != null) {
+ String[] pcolnames = rs.ParentColumnName.Split (null);
+ String[] ccolnames = rs.ChildColumnName.Split (null);
+
+ DataColumn[] pcol = new DataColumn [pcolnames.Length];
+ for (int i=0; i<pcol.Length; ++i)
+ pcol [i] = ptab.Columns [XmlHelper.Decode (pcolnames [i])];
+
+ DataColumn[] ccol = new DataColumn [ccolnames.Length];
+ for (int i=0; i < ccol.Length; ++i) {
+ ccol [i] = ctab.Columns [XmlHelper.Decode (ccolnames [i])];
+ if (ccol [i] == null)
+ ccol [i] = CreateChildColumn (pcol [i], ctab);
+ }
+ rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
+ } else {
+ DataColumn pcol = ptab.Columns [XmlHelper.Decode (rs.ParentColumnName)];
+ DataColumn ccol = ctab.Columns [XmlHelper.Decode (rs.ChildColumnName)];
+ if (ccol == null)
+ ccol = CreateChildColumn (pcol, ctab);
+ rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
}
+ rel.Nested = rs.IsNested;
+ if (rs.CreateConstraint)
+ rel.ParentTable.PrimaryKey = rel.ParentColumns;
+ return rel;
}
- /*
- private void HandleAttributes (XmlSchemaObjectCollection atts)
+ private DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
{
- foreach (XmlSchemaObject attrObj in atts) {
- XmlSchemaAttribute attr = attrObj as XmlSchemaAttribute;
- if (attr != null)
- ImportColumnAttribute (attr);
- else
- HandleAttributes (((XmlSchemaAttributeGroup) attrObj).Attributes);
- }
+ DataColumn col = childTable.Columns.Add (parentColumn.ColumnName,
+ parentColumn.DataType);
+ col.Namespace = String.Empty;
+ col.ColumnMapping = MappingType.Hidden;
+ return col;
}
- */
private void ImportColumnGroupBase (XmlSchemaElement parent, XmlSchemaGroupBase gb)
{
XmlSchemaElement el = p as XmlSchemaElement;
if (el != null)
ImportColumnElement (parent, el);
- else
- ImportColumnGroupBase (parent, (XmlSchemaGroupBase) gb);
+ else if (p is XmlSchemaGroupBase)
+ ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
+ // otherwise p is xs:any
}
}
private XmlSchemaDatatype GetSchemaPrimitiveType (object type)
{
+ if (type is XmlSchemaComplexType)
+ return null; // It came here, so that maybe it is xs:anyType
XmlSchemaDatatype dt = type as XmlSchemaDatatype;
if (dt == null && type != null)
dt = ((XmlSchemaSimpleType) type).Datatype;
DataColumn col = new DataColumn ();
col.ColumnName = attr.QualifiedName.Name;
col.Namespace = attr.QualifiedName.Namespace;
- XmlSchemaDatatype dt = GetSchemaPrimitiveType (attr.AttributeType);
+ XmlSchemaDatatype dt = null;
+#if NET_2_0
+ dt = GetSchemaPrimitiveType (((XmlSchemaSimpleType) attr.AttributeSchemaType).Datatype);
+#else
+ dt = GetSchemaPrimitiveType (attr.AttributeType);
+#endif
// This complicated check comes from the fact that
// MS.NET fails to map System.Object to anyType (that
// will cause ReadTypedObject() fail on XmlValidatingReader).
if (attr.Use == XmlSchemaUse.Required)
col.AllowDBNull = false;
+#if NET_2_0
+ FillFacet (col, attr.AttributeSchemaType as XmlSchemaSimpleType);
+#else
FillFacet (col, attr.AttributeType as XmlSchemaSimpleType);
+#endif
// Call this method after filling the name
- ImportColumnCommon (attr, attr.QualifiedName, col);
+ ImportColumnMetaInfo (attr, attr.QualifiedName, col);
+ AddColumn (col);
}
private void ImportColumnElement (XmlSchemaElement parent, XmlSchemaElement el)
col.DefaultValue = GetElementDefaultValue (el);
col.AllowDBNull = (el.MinOccurs == 0);
- // FIXME: need to handle array item for maxOccurs > 1
- if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType) {
- // import new table and set this column as reference.
+#if NET_2_0
+ if (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType)
+#else
+ if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType)
+#endif
FillDataColumnComplexElement (parent, el, col);
- // If the element is not referenced one, the element will be handled later.
- if (el.RefName == XmlQualifiedName.Empty)
- targetElements.Add (el);
- }
+ else if (el.MaxOccurs != 1)
+ FillDataColumnRepeatedSimpleElement (parent, 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)
+ private void ImportColumnMetaInfo (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
{
- int ordinal = -1;
if (obj.UnhandledAttributes != null) {
foreach (XmlAttribute attr in obj.UnhandledAttributes) {
if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
case XmlConstants.AutoIncrementSeed:
col.AutoIncrementSeed = int.Parse (attr.Value);
break;
+ case XmlConstants.AutoIncrementStep:
+ col.AutoIncrementStep = int.Parse (attr.Value);
+ break;
+ case XmlConstants.ReadOnly:
+ col.ReadOnly = XmlConvert.ToBoolean (attr.Value);
+ break;
case XmlConstants.Ordinal:
- ordinal = int.Parse (attr.Value);
+ int 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 parent, XmlSchemaElement el, DataColumn col)
{
if (targetElements.Contains (el))
return; // do nothing
- FillUniqueKeyColumn (parent, el, col);
+ string elName = XmlHelper.Decode (el.QualifiedName.Name);
+ if (elName == dataset.DataSetName)
+ // Well, why it is ArgumentException :-?
+ throw new ArgumentException ("Nested element must not have the same name as DataSet's name.");
+
+ if (el.Annotation != null)
+ HandleAnnotations (el.Annotation, true);
+ // If xsd:keyref xsd:key for this table exists, then don't add
+ // relation here manually.
+ else if (!DataSetDefinesKey (elName)) {
+ AddParentKeyColumn (parent, el, col);
+
+ RelationStructure rel = new RelationStructure ();
+ rel.ParentTableName = XmlHelper.Decode (parent.QualifiedName.Name);
+ rel.ChildTableName = elName;
+ rel.ParentColumnName = col.ColumnName;
+ rel.ChildColumnName = col.ColumnName;
+ rel.CreateConstraint = true;
+ rel.IsNested = true;
+ relations.Add (rel);
+ }
- // For nested relations, DataRelation.Nested will be filled later
- if (el.SchemaType != el.ElementType)
- nestedRelationColumns [el] = col;
+ // If the element is not referenced one, the element will be handled later.
+ if (el.RefName == XmlQualifiedName.Empty)
+ ProcessDataTableElement (el);
- // FIXME: Handle "relationship" annotations
-// if (el.Annotation != null)
-// HandleAnnotations (el.Annotation, true);
+ }
- relationParentColumns.Add (el, col);
+ private bool DataSetDefinesKey (string name)
+ {
+ foreach (ConstraintStructure c in reservedConstraints.Values)
+ if (c.TableName == name && (c.IsPrimaryKey || c.IsNested))
+ return true;
+ return false;
}
- private void FillUniqueKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
+ private void AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
{
+ if (currentTable.PrimaryKey != null)
+ return;
+
// check name identity
- string name = parent.QualifiedName.Name + "_Id";
- if (currentColumnNames.Contains (name))
- throw new DataException (String.Format ("There is already a column that has the same name: {0}", name));
+ string name = XmlHelper.Decode (parent.QualifiedName.Name) + "_Id";
+ int count = 0;
+ while (currentTable.ContainsColumn (name))
+ name = String.Format ("{0}_{1}", name, count++);
+ // check existing primary key
+ if (currentTable.Table.PrimaryKey.Length > 0)
+ throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", currentTable.Table.TableName));
col.ColumnName = name;
col.ColumnMapping = MappingType.Hidden;
col.Namespace = parent.QualifiedName.Namespace;
col.DataType = typeof (int);
- col.Unique = true;
col.AutoIncrement = true;
col.AllowDBNull = false;
+
+ ImportColumnMetaInfo (el, el.QualifiedName, col);
+ AddColumn (col);
+ currentTable.PrimaryKey = col;
}
private void FillDataColumnRepeatedSimpleElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
if (targetElements.Contains (el))
return; // do nothing
- FillUniqueKeyColumn (parent, el, col);
- // FIXME: confirm.
- // Those components will be handled later.
- targetElements.Add (el);
+ AddParentKeyColumn (parent, el, col);
+ DataColumn pkey = currentTable.PrimaryKey;
+
+ string elName = XmlHelper.Decode (el.QualifiedName.Name);
+ string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
DataTable dt = new DataTable ();
- dt.TableName = parent.QualifiedName.Name + "_" + el.QualifiedName.Name;
+ dt.TableName = elName;
+ dt.Namespace = el.QualifiedName.Namespace;
+ // reference key column to parent
DataColumn cc = new DataColumn ();
- cc.ColumnName = el.QualifiedName.Name;
- cc.Namespace = dataset.Namespace;
+ cc.ColumnName = parentName + "_Id";
+ cc.Namespace = parent.QualifiedName.Namespace;
cc.ColumnMapping = MappingType.Hidden;
- cc.DataType = this.ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
+ cc.DataType = typeof (int);
+
+ // repeatable content simple element
+ DataColumn cc2 = new DataColumn ();
+ cc2.ColumnName = elName + "_Column";
+ cc2.Namespace = el.QualifiedName.Namespace;
+ cc2.ColumnMapping = MappingType.SimpleContent;
+ cc2.AllowDBNull = false;
+#if NET_2_0
+ cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
+#else
+ cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
+#endif
+
+ dt.Columns.Add (cc2);
+ dt.Columns.Add (cc);
+ dataset.Tables.Add (dt);
+
+ RelationStructure rel = new RelationStructure ();
+ rel.ParentTableName = parentName;
+ rel.ChildTableName = dt.TableName;
+ rel.ParentColumnName = pkey.ColumnName;
+ rel.ChildColumnName = cc.ColumnName;
+ rel.IsNested = true;
+ rel.CreateConstraint = true;
+ relations.Add (rel);
}
private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
{
- col.ColumnName = el.QualifiedName.Name;
+ col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
col.Namespace = el.QualifiedName.Namespace;
col.ColumnMapping = MappingType.Element;
- XmlSchemaDatatype dt = el.ElementType as XmlSchemaDatatype;
- if (dt == null && el.ElementType != null) {
- XmlSchemaSimpleType st = el.ElementType as XmlSchemaSimpleType;
- dt = st != null ? st.Datatype : null;
- }
- col.DataType = ConvertDatatype (dt);
+#if NET_2_0
+ col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
+ FillFacet (col, el.ElementSchemaType as XmlSchemaSimpleType);
+#else
+ col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
FillFacet (col, el.ElementType as XmlSchemaSimpleType);
+#endif
+
+ ImportColumnMetaInfo (el, el.QualifiedName, col);
+
+ AddColumn (col);
+ }
+
+ private void AddColumn (DataColumn col)
+ {
+ if (col.Ordinal < 0)
+ currentTable.NonOrdinalColumns.Add (col);
+ else
+ currentTable.OrdinalColumns.Add (col, col.Ordinal);
}
private void FillFacet (DataColumn col, XmlSchemaSimpleType st)
return;
// Handle restriction facets
- // FIXME: how to handle list and union??
XmlSchemaSimpleTypeRestriction restriction = st == null ? null : st.Content as XmlSchemaSimpleTypeRestriction;
if (restriction == null)
{
if (dt == null)
return typeof (string);
- else if (dt == schemaIntegerType)
+ else if (dt.ValueType == typeof (decimal)) {
// LAMESPEC: MSDN documentation says it is based
// on ValueType. However, in the System.Xml.Schema
// context, xs:integer is mapped to Decimal, while
// in DataSet context it is mapped to Int64.
- return typeof (long);
+ if (dt == schemaDecimalType)
+ return typeof (decimal);
+ else if (dt == schemaIntegerType)
+ return typeof (long);
+ else
+ return typeof (ulong);
+ }
else
return dt.ValueType;
}
if (index > 0)
tableName = tableName.Substring (index + 1);
- return tableName;
+ return XmlHelper.Decode (tableName);
}
- private void ProcessParentKey (XmlSchemaIdentityConstraint ic)
+ private void ReserveSelfIdentity (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];
+ string [] cols = new string [ic.Fields.Count];
+ bool [] isAttrSpec = new bool [cols.Length];
+
int i = 0;
foreach (XmlSchemaXPath Field in ic.Fields) {
string colName = Field.XPath;
- // FIXME: attribute XPath
+ bool isAttr = colName.Length > 0 && colName [0] == '@';
int index = colName.LastIndexOf (':');
if (index > 0)
colName = colName.Substring (index + 1);
+ else if (isAttr)
+ colName = colName.Substring (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];
+ colName = XmlHelper.Decode (colName);
+ cols [i] = colName;
+ isAttrSpec [i] = isAttr;
i++;
}
}
}
}
- UniqueConstraint c = new UniqueConstraint (constraintName, cols, isPK);
- table.Constraints.Add (c);
+ reservedConstraints.Add (ic,
+ new ConstraintStructure (tableName, cols,
+ isAttrSpec, constraintName, isPK, null, false, false));
}
- private void ProcessReferenceKey (XmlSchemaElement element, XmlSchemaKeyref keyref)
+ private void ProcessSelfIdentity (ConstraintStructure c)
{
// Basic concept came from XmlSchemaMapper.cs
- string tableName = GetSelectorTarget (keyref.Selector.XPath);
+ string tableName = c.TableName;
+
+ DataTable dt = dataset.Tables [tableName];
+ if (dt == null) {
+ if (forDataSet)
+ throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+ else
+ // nonexistent table name. .NET ignores it for DataTable.ReadXmlSchema().
+ return;
+ }
- DataColumn [] cols;
- 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 [c.Columns.Length];
+ for (int i = 0; i < cols.Length; i++) {
+ string colName = c.Columns [i];
+ bool isAttr = c.IsAttribute [i];
+ DataColumn col = dt.Columns [colName];
+ if (col == null)
+ throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
+ if (isAttr && col.ColumnMapping != MappingType.Attribute)
+ throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
+ if (!isAttr && col.ColumnMapping != MappingType.Element)
+ throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
+
+ cols [i] = dt.Columns [colName];
+ }
+
+ bool isPK = c.IsPrimaryKey;
+ string constraintName = c.ConstraintName;
+ dt.Constraints.Add (new UniqueConstraint (
+ constraintName, cols, isPK));
+ }
- cols = new DataColumn [keyref.Fields.Count];
+ private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
+ {
+ // Basic concept came from XmlSchemaMapper.cs
+
+ string tableName = GetSelectorTarget (keyref.Selector.XPath);
+
+ string [] cols = new string [keyref.Fields.Count];
+ bool [] isAttrSpec = new bool [cols.Length];
int i = 0;
foreach (XmlSchemaXPath Field in keyref.Fields) {
- // FIXME: attribute XPath
string colName = Field.XPath;
+ bool isAttr = colName.Length > 0 && colName [0] == '@';
int index = colName.LastIndexOf (':');
- if (index != -1)
+ if (index > 0)
colName = colName.Substring (index + 1);
+ else if (isAttr)
+ colName = colName.Substring (1);
- cols [i] = table.Columns [colName];
+ colName = XmlHelper.Decode (colName);
+ cols [i] = colName;
+ isAttrSpec [i] = isAttr;
i++;
}
- string name = keyref.Refer.Name;
+ string constraintName = keyref.Name;
+ bool isNested = false;
+ bool isConstraintOnly = false;
+ if (keyref.UnhandledAttributes != null) {
+ foreach (XmlAttribute attr in keyref.UnhandledAttributes) {
+ if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
+ continue;
+ switch (attr.LocalName) {
+ case XmlConstants.ConstraintName:
+ constraintName = attr.Value;
+ break;
+ case XmlConstants.IsNested:
+ if (attr.Value == "true")
+ isNested = true;
+ break;
+ case XmlConstants.ConstraintOnly:
+ if (attr.Value == "true")
+ isConstraintOnly = true;
+ break;
+ }
+ }
+ }
+
+ reservedConstraints.Add (keyref, new ConstraintStructure (
+ tableName, cols, isAttrSpec, constraintName,
+ false, keyref.Refer.Name, isNested, isConstraintOnly));
+ }
+
+ private void ProcessRelationIdentity (XmlSchemaElement element, ConstraintStructure c)
+ {
+ // Basic concept came from XmlSchemaMapper.cs
+
+ string tableName = c.TableName;
+
+ DataColumn [] cols;
+ DataTable dt = dataset.Tables [tableName];
+ if (dt == null)
+ throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+
+ cols = new DataColumn [c.Columns.Length];
+ for (int i = 0; i < cols.Length; i++) {
+ string colName = c.Columns [i];
+ bool isAttr = c.IsAttribute [i];
+ DataColumn col = dt.Columns [colName];
+ if (isAttr && col.ColumnMapping != MappingType.Attribute)
+ throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
+ if (!isAttr && col.ColumnMapping != MappingType.Element)
+ throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
+ cols [i] = col;
+ }
+ string name = c.ReferName;
// 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);
+ ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
+ dt.Constraints.Add (fkc);
+
+ if (!c.IsConstraintOnly) {
+ // generate the relation.
+ DataRelation rel = new DataRelation (c.ConstraintName, uniq.Columns, cols, true);
+ rel.Nested = c.IsNested;
+ rel.SetParentKeyConstraint (uniq);
+ rel.SetChildKeyConstraint (fkc);
- dataset.Relations.Add (rel);
+ dataset.Relations.Add (rel);
+ }
}
// get the unique constraint for the relation.
string tableName = GetSelectorTarget (c.Selector.XPath);
// find the table in the dataset.
- DataTable table = dataset.Tables [tableName];
+ DataTable dt = dataset.Tables [tableName];
string constraintName = c.Name;
// find if there is an attribute with the constraint name
foreach (XmlAttribute attr in c.UnhandledAttributes)
if (attr.LocalName == "ConstraintName" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
constraintName = attr.Value;
- return (UniqueConstraint) table.Constraints [constraintName];
+ return (UniqueConstraint) dt.Constraints [constraintName];
}
}
throw new DataException ("Target identity constraint was not found: " + name);
private void HandleRelationshipAnnotation (XmlElement el, bool nested)
{
+ string name = el.GetAttribute ("name");
string ptn = el.GetAttribute ("parent", XmlConstants.MsdataNamespace);
string ctn = el.GetAttribute ("child", XmlConstants.MsdataNamespace);
string pkn = el.GetAttribute ("parentkey", XmlConstants.MsdataNamespace);
string fkn = el.GetAttribute ("childkey", XmlConstants.MsdataNamespace);
- DataTable pt = dataset.Tables [ptn];
- DataTable ct = dataset.Tables [ctn];
- DataColumn pc = pt.Columns [pkn];
- DataColumn cc = ct.Columns [fkn];
-
- DataRelation rel = new DataRelation (el.GetAttribute ("name"), pc, cc, false);
- rel.Nested = nested;
- dataset.Relations.Add (rel);
+ RelationStructure rel = new RelationStructure ();
+ rel.ExplicitName = XmlHelper.Decode (name);
+ rel.ParentTableName = XmlHelper.Decode (ptn);
+ rel.ChildTableName = XmlHelper.Decode (ctn);
+ // ColumnNames will be decoded wherever they are used as they can
+ // contain 'space' separated list of column-names.
+ rel.ParentColumnName = pkn;
+ rel.ChildColumnName = fkn;
+ rel.IsNested = nested;
+ rel.CreateConstraint = false; // by default?
+ relations.Add (rel);
}
private object GetElementDefaultValue (XmlSchemaElement elem)