|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.ComponentModel;
using System.Data.Common;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace System.Data
{
internal enum SchemaFormat
{
Public = 1,
Remoting = 2,
WebService = 3,
RemotingSkipSchema = 4,
WebServiceSkipSchema = 5
}
/// <summary>
/// </summary>
internal sealed class XmlTreeGen
{
private ArrayList? _constraintNames;
private Hashtable? _namespaces;
private Hashtable _autogenerated = default!; // Late-initialized
private Hashtable? _prefixes;
private DataSet? _ds;
private readonly ArrayList _tables = new ArrayList();
private readonly ArrayList _relations = new ArrayList();
private XmlDocument? _dc;
private XmlElement? _sRoot;
private int _prefixCount;
private readonly SchemaFormat _schFormat = SchemaFormat.Public;
private string? _filePath;
private string? _fileName;
private string? _fileExt;
private XmlElement? _dsElement;
private XmlElement? _constraintSeparator;
/// <summary>
/// This converter allows new versions of the framework to write
/// the assembly version of older versions of the framework.
/// </summary>
private Converter<Type, string>? _targetConverter;
internal XmlTreeGen(SchemaFormat format)
{
_schFormat = format;
}
internal static void AddExtendedProperties(PropertyCollection? props, XmlElement node)
{
AddExtendedProperties(props, node, null);
}
internal static void AddExtendedProperties(PropertyCollection? props, XmlElement node, Type? type)
{
if (props != null)
{
foreach (DictionaryEntry entry in props)
{
string s, v;
if (entry.Key is INullable)
{
s = (string)SqlConvert.ChangeTypeForXML(entry.Key, typeof(string));
}
else
{
s = Convert.ToString(entry.Key, CultureInfo.InvariantCulture)!;
}
if (entry.Value is INullable)
{
v = (string)SqlConvert.ChangeTypeForXML(entry.Value, typeof(string));
}
else if (entry.Value is System.Numerics.BigInteger)
{
v = (string)BigIntegerStorage.ConvertFromBigInteger((System.Numerics.BigInteger)entry.Value, typeof(string), CultureInfo.InvariantCulture);
}
else
{
v = Convert.ToString(entry.Value, CultureInfo.InvariantCulture)!;
}
if (type == typeof(DataRelation))
{
s = Keywords.MSD_REL_PREFIX + s;
}
else if (type == typeof(ForeignKeyConstraint))
{
s = Keywords.MSD_FK_PREFIX + s;
}
node.SetAttribute(XmlConvert.EncodeLocalName(s), Keywords.MSPROPNS, v);
}
}
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
internal void AddXdoProperties(object? instance, XmlElement root)
{
if (instance == null)
{
return;
}
PropertyDescriptorCollection pds = TypeDescriptor.GetProperties(instance);
if (!((instance is DataSet) || (instance is DataTable) || (instance is DataColumn) || (instance is DataRelation)))
{
return;
}
for (int i = 0; i < pds.Count; i++)
{
AddXdoProperty(pds[i], instance, root);
}
return;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
internal void AddXdoProperty(PropertyDescriptor pd, object instance, XmlElement root)
{
Type type = pd.PropertyType;
bool bisDataColumn = false;
DataColumn? col = null; // it may cause problem to assign null here, I will need to change this.
bool bIsSqlType = false;
bool bImplementsInullable = false;
if (instance is DataColumn)
{
col = (DataColumn)instance;
bisDataColumn = true;
bIsSqlType = col.IsSqlType;
bImplementsInullable = col.ImplementsINullable;
}
if (bImplementsInullable == false &&
type != typeof(string) && // DO NOT REMOVE THIS CHECK
type != typeof(bool) &&
type != typeof(Type) &&
type != typeof(object) &&
type != typeof(CultureInfo) &&
type != typeof(long) &&
type != typeof(int))
{
return;
}
if ((!pd.ShouldSerializeValue(instance) || !ContainsDesignerSerializationVisibleAttribute(pd)) && (bIsSqlType == false))
{
return;
}
object? propInst = pd.GetValue(instance);
if (propInst is InternalDataCollectionBase)
return;
if (propInst is PropertyCollection)
{
return;
}
// SDUB: perf: Why not have this as a table?
// there are several xdo properties that equal to some xml attributes, we should not explicitly output them.
if (
string.Equals(pd.Name, "Namespace", StringComparison.Ordinal) ||
string.Equals(pd.Name, "PrimaryKey", StringComparison.Ordinal) ||
string.Equals(pd.Name, "ColumnName", StringComparison.Ordinal) ||
string.Equals(pd.Name, "DefaultValue", StringComparison.Ordinal) ||
string.Equals(pd.Name, "TableName", StringComparison.Ordinal) ||
string.Equals(pd.Name, "DataSetName", StringComparison.Ordinal) ||
string.Equals(pd.Name, "AllowDBNull", StringComparison.Ordinal) ||
string.Equals(pd.Name, "Unique", StringComparison.Ordinal) ||
string.Equals(pd.Name, "NestedInDataSet", StringComparison.Ordinal) ||
string.Equals(pd.Name, "Locale", StringComparison.Ordinal) ||
string.Equals(pd.Name, "CaseSensitive", StringComparison.Ordinal) ||
string.Equals(pd.Name, "RemotingFormat", StringComparison.Ordinal)
)
{
return;
}
if (bisDataColumn)
{
if (string.Equals(pd.Name, "DataType", StringComparison.Ordinal))
{
string dt = XmlDataTypeName(col!.DataType);
if (bIsSqlType || (col.DataType == typeof(System.Numerics.BigInteger)))
{
root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, col.DataType.FullName);
}
else if ((dt.Length == 0) || bImplementsInullable || ((dt == Keywords.XSD_ANYTYPE) && (col.XmlDataType != Keywords.XSD_ANYTYPE)) || (col.DataType == typeof(DateTimeOffset)))
{
// in Whidbey, XmlDataTypeName function changed to return "anyType" for typeof(Object)
// should still always hit this code path for all non-built in types
// to handle version qualified typeof(Object) and other CDT objects correctly
// we must write the output exactly the same way as we read it
SetMSDataAttribute(root, col.DataType);
}
return;
}
if (string.Equals(pd.Name, "Attribute", StringComparison.Ordinal))
{
return;
}
}
string? textValue = pd.Converter.ConvertToString(propInst);
root.SetAttribute(pd.Name, Keywords.MSDNS, textValue);
return;
}
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields, typeof(DesignerSerializationVisibilityAttribute))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The DynamicDependency ensures the correct members are preserved.")]
private static bool ContainsDesignerSerializationVisibleAttribute(PropertyDescriptor pd) => pd.Attributes.Contains(DesignerSerializationVisibilityAttribute.Visible);
internal static string XmlDataTypeName(Type type)
{
if (type == typeof(char))
return "_"; // has to have SimpleType in this column.
if (type == typeof(byte[]) || type == typeof(SqlBytes))
return "base64Binary"; // has to have SimpleType in this column.
if (type == typeof(DateTime) || type == typeof(SqlDateTime))
return "dateTime";
if (type == typeof(TimeSpan))
return "duration";
if (type == typeof(decimal) || type == typeof(SqlDecimal) || type == typeof(SqlMoney))
return "decimal";
if (type == typeof(int))
return "int";
if (type == typeof(bool) || type == typeof(SqlBoolean))
return "boolean";
if (type == typeof(float) || type == typeof(SqlSingle))
return "float";
if (type == typeof(double) || type == typeof(SqlDouble))
return "double";
if (type == typeof(sbyte) || type == typeof(SqlByte))
return "byte";
if (type == typeof(byte))
return "unsignedByte";
if (type == typeof(short) || type == typeof(SqlInt16))
return "short";
if (type == typeof(int) || type == typeof(SqlInt32))
return "int";
if (type == typeof(long) || type == typeof(SqlInt64))
return "long";
if (type == typeof(ushort))
return "unsignedShort";
if (type == typeof(uint))
return "unsignedInt";
if (type == typeof(ulong))
return "unsignedLong";
if (type == typeof(System.Numerics.BigInteger))
return Keywords.XSD_ANYTYPE; //"integer";
if (type == typeof(Uri))
return "anyURI";
if (type == typeof(SqlBinary))
return "hexBinary";
if (type == typeof(string) || type == typeof(SqlGuid) || type == typeof(SqlString) || type == typeof(SqlChars))
return "string";
if (type == typeof(object) || type == typeof(SqlXml) || type == typeof(DateTimeOffset))
return Keywords.XSD_ANYTYPE;
return string.Empty;
// by default, if we dont map anything, we will map to String
// but I can not make Sql Types that will map to string be unmapped, because in schema , I will miss the second part and won't
// be able to differenciate between string snd SqlString and others that map to String
}
private void GenerateConstraintNames(DataTable table, bool fromTable)
{
// if constraint created obased on relation and it is self related rel. then add constraint
StringBuilder? builder = null;
foreach (Constraint constr in table.Constraints)
{
if (fromTable)
{
if (constr is ForeignKeyConstraint)
{ // if parent table does not exist , no need to create FKConst
if (!_tables.Contains(((ForeignKeyConstraint)constr).RelatedTable))
{
continue;
}
}
}
int nameInt = 0;
string name = constr.ConstraintName;
while (_constraintNames!.Contains(name))
{
if (null == builder)
{
builder = new StringBuilder();
}
builder.Append(table.TableName).Append('_').Append(constr.ConstraintName);
if (0 < nameInt)
{
builder.Append('_').Append(nameInt);
}
nameInt++;
name = builder.ToString();
builder.Length = 0;
}
_constraintNames.Add(name);
constr.SchemaName = name;
}
}
private void GenerateConstraintNames(ArrayList tables)
{
for (int i = 0; i < tables.Count; i++)
{
GenerateConstraintNames((DataTable)tables[i]!, true);
}
}
private void GenerateConstraintNames(DataSet ds)
{
foreach (DataTable dt in ds.Tables)
{
GenerateConstraintNames(dt, false);
}
}
//Does the DS or ANY object in it have ExtendedProperties?
private static bool _PropsNotEmpty(PropertyCollection? props)
{
return props != null && props.Count != 0;
}
private static bool HaveExtendedProperties(DataSet ds)
{
if (_PropsNotEmpty(ds._extendedProperties))
{
return true;
}
for (int t = 0; t < ds.Tables.Count; t++)
{
DataTable table = ds.Tables[t];
if (_PropsNotEmpty(table._extendedProperties))
{
return true;
}
for (int c = 0; c < table.Columns.Count; c++)
{
if (_PropsNotEmpty(table.Columns[c]._extendedProperties))
{
return true;
}
}
}
// What is the best way to enumerate relations? from DataSet of from DataTable?
for (int r = 0; r < ds.Relations.Count; r++)
{
if (_PropsNotEmpty(ds.Relations[r]._extendedProperties))
{
return true;
}
}
// What about constraints?
return false;
}// HaveExtendedProperties
internal void WriteSchemaRoot(XmlElement rootSchema, string targetNamespace)
{
/*
if (_ds != null)
rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName(_ds.DataSetName));
else
rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName("NewDataSet"));
*/
if (!string.IsNullOrEmpty(targetNamespace))
{
rootSchema.SetAttribute(Keywords.TARGETNAMESPACE, targetNamespace);
rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, targetNamespace);
}
// Add the namespaces
// rootSchema.SetAttribute(Keywords.XMLNS, Keywords.XSD_ATOM.String));
rootSchema.SetAttribute(Keywords.XMLNS, targetNamespace);
rootSchema.SetAttribute(Keywords.XMLNS_XSD, Keywords.XSDNS);
rootSchema.SetAttribute(Keywords.XMLNS_MSDATA, Keywords.MSDNS);
if ((_ds != null) && (HaveExtendedProperties(_ds)))
{
rootSchema.SetAttribute(Keywords.XMLNS_MSPROP, Keywords.MSPROPNS);
}
if (!string.IsNullOrEmpty(targetNamespace))
{
rootSchema.SetAttribute(Keywords.XSD_ATTRIBUTEFORMDEFAULT, Keywords.QUALIFIED);
rootSchema.SetAttribute(Keywords.XSD_ELEMENTFORMDEFAULT, Keywords.QUALIFIED);
}
}
internal static void ValidateColumnMapping(Type columnType)
{
if (DataStorage.IsTypeCustomType(columnType))
{
throw ExceptionBuilder.InvalidDataColumnMapping(columnType);
}
}
internal void SetupAutoGenerated(DataSet ds)
{
foreach (DataTable dt in ds.Tables)
SetupAutoGenerated(dt);
}
internal void SetupAutoGenerated(ArrayList dt)
{
for (int i = 0; i < dt.Count; i++)
{
SetupAutoGenerated((DataTable)dt[i]!);
}
}
internal void SetupAutoGenerated(DataTable dt)
{
foreach (DataColumn col in dt.Columns)
{
if (AutoGenerated(col))
_autogenerated[col] = col;
}
foreach (Constraint cs in dt.Constraints)
{
ForeignKeyConstraint? fk = (cs as ForeignKeyConstraint);
if (null != fk)
{
if (AutoGenerated(fk))
_autogenerated[fk] = fk;
else
{
if (_autogenerated[fk.Columns[0]] != null)
_autogenerated[fk.Columns[0]] = null;
if (_autogenerated[fk.RelatedColumnsReference[0]] != null)
_autogenerated[fk.RelatedColumnsReference[0]] = null;
// special case of the ghosted constraints:
UniqueConstraint? _constraint = (UniqueConstraint?)fk.RelatedTable.Constraints.FindConstraint(new UniqueConstraint("TEMP", fk.RelatedColumnsReference));
if (_constraint == null)
continue;
if (_autogenerated[_constraint] != null)
_autogenerated[_constraint] = null;
if (_autogenerated[_constraint.Key.ColumnsReference[0]] != null)
_autogenerated[_constraint.Key.ColumnsReference[0]] = null;
}
}
else
{
UniqueConstraint unique = (UniqueConstraint)cs;
if (AutoGenerated(unique))
_autogenerated[unique] = unique;
else
{
if (_autogenerated[unique.Key.ColumnsReference[0]] != null)
_autogenerated[unique.Key.ColumnsReference[0]] = null;
}
}
}
}
private void CreateTablesHierarchy(DataTable dt)
{
// if (!dt.SerializeHierarchy)
// return;
foreach (DataRelation r in dt.ChildRelations)
{
if (!_tables.Contains(r.ChildTable))
{
_tables.Add(r.ChildTable);
CreateTablesHierarchy(r.ChildTable);
}
}
}
private void CreateRelations(DataTable dt)
{
foreach (DataRelation r in dt.ChildRelations)
{
if (!_relations.Contains(r))
{
_relations.Add(r);
// if (dt.SerializeHierarchy)
CreateRelations(r.ChildTable);
}
}
}
private DataTable[] CreateToplevelTables()
{
ArrayList topTables = new ArrayList();
for (int i = 0; i < _tables.Count; i++)
{
DataTable table = (DataTable)_tables[i]!;
if (table.ParentRelations.Count == 0)
topTables.Add(table);
else
{
bool fNestedButNotSelfNested = false;
for (int j = 0; j < table.ParentRelations.Count; j++)
{
if (table.ParentRelations[j].Nested)
{
if (table.ParentRelations[j].ParentTable == table)
{
fNestedButNotSelfNested = false;
break;
}
fNestedButNotSelfNested = true;
}
}
if (!fNestedButNotSelfNested)
topTables.Add(table);
}
}
if (topTables.Count == 0)
{
return Array.Empty<DataTable>();
}
var temp = new DataTable[topTables.Count];
topTables.CopyTo(temp, 0);
return temp;
}
// SxS: this method can generate XSD files if the input xmlWriter is XmlTextWriter or DataTextWriter and its underlying stream is FileStream
// These XSDs are located in the same folder as the underlying stream's file path (see SetPath method).
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void SchemaTree(XmlDocument xd, XmlWriter xmlWriter, DataSet? ds, DataTable? dt, bool writeHierarchy)
{
_constraintNames = new ArrayList();
_autogenerated = new Hashtable();
bool genSecondary = _filePath != null; //null non-file based streams.
_dsElement = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
DataTable[] top;
bool fFlat = false;
DataTable _dt = dt!;
if (ds != null)
{
_ds = ds;
foreach (DataTable table in ds.Tables)
{
_tables.Add(table);
}
}
else
{
if (dt!.DataSet != null)
{
// preserve datatable's dataset to use for xml
// if null it would write out document element instead of dt.DataSet.DataSetName
_ds = dt.DataSet;
}
_tables.Add(dt);
if (writeHierarchy)
{
CreateTablesHierarchy(dt);
}
}
_dc = xd;
_namespaces = new Hashtable();
_prefixes = new Hashtable();
XmlElement rootSchema = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SCHEMA, Keywords.XSDNS);
_sRoot = rootSchema;
// Need to writeid attribute on schema, as webservice relys on it for typeddataset deserialization
// to get class name
if (_ds != null)
{
rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName(_ds.DataSetName));
}
else
{
rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName("NewDataSet"));
}
if (_ds != null)
{
WriteSchemaRoot(rootSchema, _ds.Namespace);
}
else
{
WriteSchemaRoot(rootSchema, _dt.Namespace);
}
// register the root element and associated NS
if (_schFormat == SchemaFormat.Remoting)
{
if (_ds != null)
{
_namespaces[_ds.Namespace] = rootSchema;
}
else
{
_namespaces[_dt.Namespace] = rootSchema;
}
}
if (_schFormat != SchemaFormat.Remoting)
{
if (_ds != null)
{
_namespaces[_ds.Namespace] = rootSchema;
if (_ds.Namespace.Length == 0)
_prefixes[_ds.Namespace] = null;
else
{
// generate a prefix for the dataset schema itself.
rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, _ds.Namespace);
_prefixes[_ds.Namespace] = "mstns";
}
}
}
// Generate all the constraint names
if (ds != null)
GenerateConstraintNames(ds);
else
GenerateConstraintNames(_tables);
// Setup AutoGenerated table
if (_schFormat != SchemaFormat.Remoting)
{
if (ds != null)
{
SetupAutoGenerated(ds);
}
else
{
SetupAutoGenerated(_tables);
}
}
//
// Output all top level elements, which will recursively invoke to other tables.
//
top = ((ds != null) ? ds.TopLevelTables(true) : CreateToplevelTables());
if (top.Length == 0 || _schFormat == SchemaFormat.WebServiceSkipSchema || _schFormat == SchemaFormat.RemotingSkipSchema)
{
// return an empty schema for now.
// probably we need to throw an exception
FillDataSetElement(xd, ds, dt);
rootSchema.AppendChild(_dsElement);
AddXdoProperties(_ds, _dsElement);
AddExtendedProperties(ds!._extendedProperties, _dsElement);
xd.AppendChild(rootSchema);
xd.Save(xmlWriter);
xmlWriter.Flush();
return; // rootSchema content has already been pushed to xmlWriter
}
// if (schFormat != SchemaFormat.WebService && namespaces.Count > 1 && !genSecondary) {
// rootSchema.SetAttribute(Keywords.MSD_FRAGMENTCOUNT, Keywords.MSDNS, namespaces.Count.ToString());
// }
// Fill out dataset element
XmlElement dsCompositor = FillDataSetElement(xd, ds, dt);
_constraintSeparator = xd.CreateElement(Keywords.XSD_PREFIX, "SHOULDNOTBEHERE", Keywords.XSDNS);
_dsElement.AppendChild(_constraintSeparator);
// DataSet properties
if (_ds != null)
{
AddXdoProperties(_ds, _dsElement);
AddExtendedProperties(_ds._extendedProperties, _dsElement);
}
for (int i = 0; i < top.Length; i++)
{
XmlElement el = HandleTable(top[i], xd, rootSchema);
if (((_ds != null) && (_ds.Namespace == top[i].Namespace)) || string.IsNullOrEmpty(top[i].Namespace) || (_schFormat == SchemaFormat.Remoting))
{
bool fNestedInDataset = top[i]._fNestedInDataset;
if (((_ds != null) && (_ds.Namespace.Length != 0)) && string.IsNullOrEmpty(top[i].Namespace))
{
fNestedInDataset = true;
}
// what if dt has two nested relation , one self nested , the other with dtParent
if (top[i].SelfNested)
{ // regarding above check : is it selfnested!
fNestedInDataset = false;
}
if (top[i].NestedParentsCount > 1)
{ // if it has multiple parents, it should be global
fNestedInDataset = false;
}
if (fNestedInDataset)
{ //deal with maxOccurs properly
if (top[i].MinOccurs != 1)
{
el.SetAttribute(Keywords.MINOCCURS, top[i].MinOccurs.ToString(CultureInfo.InvariantCulture));
}
if (top[i].MaxOccurs == -1)
{
el.SetAttribute(Keywords.MAXOCCURS, Keywords.ZERO_OR_MORE);
}
else if (top[i].MaxOccurs != 1)
{
el.SetAttribute(Keywords.MAXOCCURS, top[i].MaxOccurs.ToString(CultureInfo.InvariantCulture));
}
}
if (!fNestedInDataset)
{
rootSchema.AppendChild(el);
XmlElement node = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
if ((_ds != null && _ds.Namespace == top[i].Namespace) || string.IsNullOrEmpty(top[i].Namespace) || (_schFormat == SchemaFormat.Remoting))
node.SetAttribute(Keywords.REF, top[i].EncodedTableName);
else
node.SetAttribute(Keywords.REF, ((string)_prefixes[top[i].Namespace]!) + ':' + top[i].EncodedTableName);
dsCompositor.AppendChild(node);
}
else
dsCompositor.AppendChild(el);
}
else
{
AppendChildWithoutRef(top[i].Namespace, el);
XmlElement node = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
node.SetAttribute(Keywords.REF, ((string)_prefixes[top[i].Namespace]!) + ':' + top[i].EncodedTableName);
dsCompositor.AppendChild(node);
}
}
_dsElement.RemoveChild(_constraintSeparator);
rootSchema.AppendChild(_dsElement);
// Output all non-hierarchical relations without constraints
DataRelation[] rels = Array.Empty<DataRelation>();
if (ds != null && _tables.Count > 0)
{ // we need to make sure we want to write relation just for tables in list
rels = new DataRelation[ds.Relations.Count];
for (int i = 0; i < ds.Relations.Count; i++)
{
rels[i] = ds.Relations[i];
}
}
else if (writeHierarchy && _tables.Count > 0)
{
CreateRelations((DataTable)_tables[0]!);
rels = new DataRelation[_relations.Count];
_relations.CopyTo(rels, 0);
}
XmlElement? nodeAnn = null;
XmlElement? nodeApp = null;
for (int i = 0; i < rels.Length; ++i)
{
DataRelation rel = rels[i];
if (!rel.Nested || fFlat)
{
if (rel.ChildKeyConstraint == null)
{
if (nodeAnn == null)
{
nodeAnn = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ANNOTATION, Keywords.XSDNS);
rootSchema.AppendChild(nodeAnn);
nodeApp = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_APPINFO, Keywords.XSDNS);
nodeAnn.AppendChild(nodeApp);
}
Debug.Assert(nodeApp != null, "Need to create <application..> node first.");
nodeApp.AppendChild(HandleRelation(rel, xd));
}
}
}
XmlComment? comment = null;
bool isMultipleNamespaceAndStreamingWriter = (_namespaces.Count > 1 && !genSecondary);
if (_schFormat != SchemaFormat.Remoting && _schFormat != SchemaFormat.RemotingSkipSchema)
{
// complete processing of rootSchema
foreach (string ns in _namespaces.Keys)
{
if (ns == ((_ds != null) ? _ds.Namespace : _dt.Namespace) || string.IsNullOrEmpty(ns))
{
continue;
}
XmlElement _import = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_IMPORT, Keywords.XSDNS);
_import.SetAttribute(Keywords.XSD_NAMESPACE, ns);
if (_schFormat != SchemaFormat.WebService && !isMultipleNamespaceAndStreamingWriter)
{
_import.SetAttribute(Keywords.XSD_SCHEMALOCATION, _fileName + "_" + _prefixes[ns] + ".xsd");
}
rootSchema.PrependChild(_import);
}
if (_schFormat != SchemaFormat.WebService && isMultipleNamespaceAndStreamingWriter)
{
rootSchema.SetAttribute(Keywords.MSD_FRAGMENTCOUNT, Keywords.MSDNS, _namespaces.Count.ToString(CultureInfo.InvariantCulture));
}
// Post rootSchema content to xmlWriter.
xd.AppendChild(rootSchema); // KB
if (_schFormat != SchemaFormat.WebService && isMultipleNamespaceAndStreamingWriter)
{
xd.WriteTo(xmlWriter);
}
else
{
xd.Save(xmlWriter);
}
xd.RemoveChild(rootSchema); //KB
foreach (string ns in _namespaces.Keys)
{
if (ns == ((_ds != null) ? _ds.Namespace : _dt.Namespace) || string.IsNullOrEmpty(ns))
{
continue;
}
XmlWriter? xw = null;
if (!genSecondary)
{
xw = xmlWriter;
}
else
{
xw = new XmlTextWriter(_filePath + _fileName + "_" + _prefixes[ns] + ".xsd", null);
}
try
{
if (genSecondary)
{
if (xw is XmlTextWriter)
{
((XmlTextWriter)xw).Formatting = Formatting.Indented;
}
xw.WriteStartDocument(true);
}
XmlElement tNode = (XmlElement)_namespaces[ns]!;
_dc.AppendChild(tNode);
foreach (string imp_ns in _namespaces.Keys)
{
if (ns == imp_ns)
{
continue; // don't write out yourself
}
string? prefix = (string?)_prefixes[imp_ns];
if (prefix == null)
{ // only for dataset.Namespace == empty
continue; // do nothing
}
tNode.SetAttribute("xmlns:" + prefix, imp_ns);
XmlElement _import2 = _dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_IMPORT, Keywords.XSDNS);
_import2.SetAttribute(Keywords.XSD_NAMESPACE, imp_ns);
if (_schFormat != SchemaFormat.WebService && !isMultipleNamespaceAndStreamingWriter)
{
if (imp_ns == ((_ds != null) ? _ds.Namespace : _dt.Namespace))
_import2.SetAttribute(Keywords.XSD_SCHEMALOCATION, _fileName + _fileExt); // for the dataset namespace don't append anything
else
_import2.SetAttribute(Keywords.XSD_SCHEMALOCATION, _fileName + "_" + prefix + ".xsd");
}
tNode.PrependChild(_import2);
}
if (_schFormat != SchemaFormat.WebService && isMultipleNamespaceAndStreamingWriter)
{
_dc.WriteTo(xw);
}
else
{
_dc.Save(xw);
}
_dc.RemoveChild(tNode);
if (genSecondary)
{
xw.WriteEndDocument();
}
}
finally
{
if (genSecondary)
{
xw.Close();
}
}
}
}
else
{
xd.AppendChild(rootSchema);
xd.Save(xmlWriter);
}
if (comment != null)
{
rootSchema.PrependChild(comment);
}
if (!genSecondary)
{
xmlWriter.Flush();
}
return; // rootSchema;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal XmlElement SchemaTree(XmlDocument xd, DataTable dt)
{
_dsElement = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
_constraintNames = new ArrayList();
_ds = dt.DataSet;
_dc = xd;
_namespaces = new Hashtable();
_prefixes = new Hashtable();
if (_schFormat != SchemaFormat.Remoting)
{
_autogenerated = new Hashtable();
}
XmlElement rootSchema = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SCHEMA, Keywords.XSDNS);
_sRoot = rootSchema;
WriteSchemaRoot(rootSchema, dt.Namespace);
_ = FillDataSetElement(xd, null, dt);
_constraintSeparator = xd.CreateElement(Keywords.XSD_PREFIX, "SHOULDNOTBEHERE", Keywords.XSDNS);
_dsElement.AppendChild(_constraintSeparator);
if (_schFormat != SchemaFormat.Remoting)
{
if (_ds != null)
{
_namespaces[_ds.Namespace] = rootSchema;
if (_ds.Namespace.Length == 0)
{
_prefixes[_ds.Namespace] = null;
}
else
{
// generate a prefix for the dataset schema itself.
rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, _ds.Namespace);
_prefixes[_ds.Namespace] = "mstns";
}
}
else
{
_namespaces[dt.Namespace] = rootSchema;
if (dt.Namespace.Length == 0)
{
_prefixes[dt.Namespace] = null;
}
else
{
// generate a prefix for the dataset schema itself.
rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, dt.Namespace);
_prefixes[dt.Namespace] = "mstns";
}
}
}
// Generate all the constraint names
GenerateConstraintNames(dt, true);
//
// Output all top level elements, which will recursively invoke to other tables.
//
XmlElement el = HandleTable(dt, xd, rootSchema, false);
rootSchema.AppendChild(el);
_dsElement.RemoveChild(_constraintSeparator);
rootSchema.AppendChild(_dsElement);
return rootSchema;
}
internal XmlElement FillDataSetElement(XmlDocument xd, DataSet? ds, DataTable? dt)
{
Debug.Assert(ds == null || dt == null);
Debug.Assert(_dsElement != null);
DataSet? dataSet = ds ?? dt!.DataSet;
if (dataSet != null)
{
_dsElement.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(dataSet.DataSetName));
_dsElement.SetAttribute(Keywords.MSD_ISDATASET, Keywords.MSDNS, Keywords.TRUE);
if (ds == null)
_dsElement.SetAttribute(Keywords.MSD_MAINDATATABLE, Keywords.MSDNS, XmlConvert.EncodeLocalName(((dt!.Namespace.Length == 0) ? dt.TableName : (dt.Namespace + ":" + dt.TableName))));
// Add CaseSensitive and locale properties
if (dataSet.CaseSensitive)
{
_dsElement.SetAttribute(Keywords.MSD_CASESENSITIVE, Keywords.MSDNS, Keywords.TRUE);
}
if (dataSet.ShouldSerializeLocale() || !dataSet.Locale.Equals(CultureInfo.CurrentCulture))
{
_dsElement.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, dataSet.Locale.ToString());
}
else
{
_dsElement.SetAttribute(Keywords.MSD_USECURRENTLOCALE, Keywords.MSDNS, Keywords.TRUE);
}
}
else
{ // No DataSet
if (dt != null)
{
_dsElement.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName("NewDataSet"));
_dsElement.SetAttribute(Keywords.MSD_ISDATASET, Keywords.MSDNS, Keywords.TRUE);
_dsElement.SetAttribute(Keywords.MSD_MAINDATATABLE, Keywords.MSDNS, XmlConvert.EncodeLocalName(((dt.Namespace.Length == 0) ? dt.TableName : (dt.Namespace + ":" + dt.TableName))));
if (dt.CaseSensitive)
{
// it is a bug to go and write casesensitive attrib as 'true', by default
_dsElement.SetAttribute(Keywords.MSD_CASESENSITIVE, Keywords.MSDNS, Keywords.TRUE);
}
if (dt.ShouldSerializeLocale() || !dt.Locale.Equals(CultureInfo.CurrentCulture))
{
_dsElement.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, dt.Locale.ToString());
}
else
{
_dsElement.SetAttribute(Keywords.MSD_USECURRENTLOCALE, Keywords.MSDNS, Keywords.TRUE);
}
}
}
XmlElement type = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_COMPLEXTYPE, Keywords.XSDNS);
_dsElement.AppendChild(type);
XmlElement compositor = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_CHOICE, Keywords.XSDNS);
compositor.SetAttribute(Keywords.MINOCCURS, Keywords.ZERO_DIGIT);
compositor.SetAttribute(Keywords.MAXOCCURS, Keywords.ZERO_OR_MORE);
type.AppendChild(compositor);
return compositor;
}
internal void SetPath(XmlWriter xw)
{
FileStream? fs;
DataTextWriter? sw = xw as DataTextWriter;
fs = (sw != null) ? sw.BaseStream as FileStream : null;
if (fs == null)
{
XmlTextWriter? textw = xw as XmlTextWriter;
if (textw == null)
return;
fs = textw.BaseStream as FileStream;
if (fs == null)
return;
}
_filePath = Path.GetDirectoryName(fs.Name);
_fileName = Path.GetFileNameWithoutExtension(fs.Name);
_fileExt = Path.GetExtension(fs.Name);
if (!string.IsNullOrEmpty(_filePath))
_filePath += "\\";
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(DataSet ds, XmlWriter xw)
{
Save(ds, null, xw);
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(DataTable dt, XmlWriter xw)
{
XmlDocument doc = new XmlDocument();
if (_schFormat == SchemaFormat.Public)
{
SetPath(xw);
}
XmlElement rootSchema = SchemaTree(doc, dt);
doc.AppendChild(rootSchema);
doc.Save(xw);
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(DataSet ds, DataTable? dt, XmlWriter xw)
{
Save(ds, dt, xw, false);
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(DataSet? ds, DataTable? dt, XmlWriter xw, bool writeHierarchy)
{
Save(ds, dt, xw, writeHierarchy, null);
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(DataSet? ds, DataTable? dt, XmlWriter xw, bool writeHierarchy, Converter<Type, string>? multipleTargetConverter)
{
_targetConverter = multipleTargetConverter;
XmlDocument doc = new XmlDocument();
if (_schFormat == SchemaFormat.Public)
{
SetPath(xw);
}
if (_schFormat == SchemaFormat.WebServiceSkipSchema && xw.WriteState == WriteState.Element)
{
xw.WriteAttributeString(Keywords.MSD, Keywords.MSD_SCHEMASERIALIZATIONMODE, Keywords.MSDNS, Keywords.MSD_EXCLUDESCHEMA);
}
SchemaTree(doc, xw, ds, dt, writeHierarchy);
}
internal XmlElement HandleRelation(DataRelation rel, XmlDocument dc)
{
XmlElement root = dc.CreateElement(Keywords.MSD, Keywords.MSD_RELATION, Keywords.MSDNS);
// convert relation name to valid xml name
root.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(rel.RelationName));
root.SetAttribute(Keywords.MSD_PARENT, Keywords.MSDNS, rel.ParentKey.Table.EncodedTableName);
root.SetAttribute(Keywords.MSD_CHILD, Keywords.MSDNS, rel.ChildKey.Table.EncodedTableName);
if ((_ds == null) || (_ds.Tables.InternalIndexOf(rel.ParentKey.Table.TableName) == -3))
root.SetAttribute(Keywords.MSD_PARENTTABLENS, Keywords.MSDNS, rel.ParentKey.Table.Namespace);
if ((_ds == null) || (_ds.Tables.InternalIndexOf(rel.ChildKey.Table.TableName) == -3))
root.SetAttribute(Keywords.MSD_CHILDTABLENS, Keywords.MSDNS, rel.ChildKey.Table.Namespace);
DataColumn[] key = rel.ParentKey.ColumnsReference;
string text = key[0].EncodedColumnName;
StringBuilder? builder = null;
if (1 < key.Length)
{
builder = new StringBuilder();
builder.Append(text);
for (int i = 1; i < key.Length; i++)
{
builder.Append(Keywords.MSD_KEYFIELDSEP).Append(key[i].EncodedColumnName);
}
text = builder.ToString();
}
root.SetAttribute(Keywords.MSD_PARENTKEY, Keywords.MSDNS, text);
key = rel.ChildKey.ColumnsReference;
text = key[0].EncodedColumnName;
if (1 < key.Length)
{
if (null != builder)
{
builder.Length = 0;
}
else
{
builder = new StringBuilder();
}
builder.Append(text);
for (int i = 1; i < key.Length; i++)
{
builder.Append(Keywords.MSD_KEYFIELDSEP).Append(key[i].EncodedColumnName);
}
text = builder.ToString();
}
root.SetAttribute(Keywords.MSD_CHILDKEY, Keywords.MSDNS, text);
AddExtendedProperties(rel._extendedProperties, root);
return root;
}
private static XmlElement? FindSimpleType(XmlElement schema, string name)
{
for (XmlNode? n = schema.FirstChild; n != null; n = n.NextSibling)
{
if (n is XmlElement e && e.GetAttribute(Keywords.NAME) == name)
{
return e;
}
}
return null;
}// FindSimpleType
internal XmlElement GetSchema(string NamespaceURI)
{
XmlElement? schemaEl = (XmlElement?)_namespaces![NamespaceURI];
if (schemaEl == null)
{
schemaEl = _dc!.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SCHEMA, Keywords.XSDNS);
WriteSchemaRoot(schemaEl, NamespaceURI);
if (!string.IsNullOrEmpty(NamespaceURI))
{
string prefix = Keywords.APP + Convert.ToString(++_prefixCount, CultureInfo.InvariantCulture);
_sRoot!.SetAttribute("xmlns:" + prefix, NamespaceURI);
schemaEl.SetAttribute("xmlns:" + prefix, NamespaceURI);
_prefixes![NamespaceURI] = prefix;
}
_namespaces[NamespaceURI] = schemaEl;
}
return schemaEl;
}
internal void HandleColumnType(DataColumn col, XmlDocument dc, XmlElement root, XmlElement schema)
{
Debug.Assert(_prefixes != null);
string keyword = Keywords.TYPE;
if (col.ColumnMapping == MappingType.SimpleContent)
keyword = Keywords.BASE;
if (col.SimpleType != null)
{
// generate simpleType node
SimpleType? stNode = col.SimpleType;
while (stNode != null)
{
// for remoting, set the msdata:targetNamespace for the simpleType.
XmlNode type;
string? name = stNode.Name;
if (!string.IsNullOrEmpty(name))
{
// For remoting, always need to work with root schema's namespace
string nSpace = (_schFormat != SchemaFormat.Remoting) ? stNode.Namespace :
(col.Table!.DataSet != null ? col.Table.DataSet.Namespace : col.Table.Namespace);
// for remoting we need to use columns NS, for other cases it is wrong to get Columns NS, we need to take type's namespace
XmlElement schNode = GetSchema(nSpace);
//SchNode To Ensure BaseSimpleType Prefix is Generated
if (stNode.BaseSimpleType != null && stNode.BaseSimpleType.Namespace != null && stNode.BaseSimpleType.Namespace.Length > 0)
GetSchema(stNode.BaseSimpleType.Namespace); //it will ensure a prefix has been created for this namespace
type = stNode.ToNode(dc, _prefixes, (_schFormat == SchemaFormat.Remoting));
if (stNode == col.SimpleType)
{
string? prefix = (string?)_prefixes[nSpace];
// set the columns's type
if (prefix != null && prefix.Length > 0)
{
if (_schFormat != SchemaFormat.Remoting)
root.SetAttribute(keyword, (prefix + ":" + name)); // look at below,this loop assumes we would be here just oen time: Its Wrong
else // As all types (in remoting) belong to the same namespace, just write type name
root.SetAttribute(keyword, name);
}
else
root.SetAttribute(keyword, name);
// set the root to the actual type, do not overwrite it in the iteration.
}
XmlElement? elmSimpeType = FindSimpleType(schNode, name);
if (elmSimpeType == null)
{
// if we don't have the defenition for this simpleType yet. Add it
schNode.AppendChild(type);
}
else
{
#if DEBUG
// enzol: TO DO: replace the constructor with IsEqual(XmlElement)
// Debug.Assert(col.SimpleType.IsEqual(new SimpleType(elmSimpeType)), $"simpleTypes with the same name have to be the same: {name}");
#endif
}
}
else
{
//SchNode To Ensure BaseSimpleType Prefix is Generated
if (stNode.BaseSimpleType != null && stNode.BaseSimpleType.Namespace != null && stNode.BaseSimpleType.Namespace.Length > 0)
GetSchema(stNode.BaseSimpleType.Namespace); //it will ensure a prefix has been created for this namespace
type = stNode.ToNode(dc, _prefixes, _schFormat == SchemaFormat.Remoting);
root.AppendChild(type);
}
stNode = stNode.BaseSimpleType;
}
}
else if (col.XmlDataType != null && col.XmlDataType.Length != 0 && XSDSchema.IsXsdType(col.XmlDataType))
{
root.SetAttribute(keyword, XSDSchema.QualifiedName(col.XmlDataType));
}
else
{
string typeName = XmlDataTypeName(col.DataType); // do not update the hashtable, as it will not write msdata:DataType
if (string.IsNullOrEmpty(typeName))
{
if (col.DataType == typeof(Guid) || col.DataType == typeof(Type))
{
typeName = "string";
}
else
{
if (col.ColumnMapping == MappingType.Attribute)
{
XmlTreeGen.ValidateColumnMapping(col.DataType);
}
typeName = Keywords.XSD_ANYTYPE;
}
}
root.SetAttribute(keyword, XSDSchema.QualifiedName(typeName));
}
}
internal void AddColumnProperties(DataColumn col, XmlElement root)
{
if (col.DataType != typeof(string))
{
string dt = XmlDataTypeName(col.DataType);
if ((col.IsSqlType && ((dt.Length == 0) || col.ImplementsINullable)) || (typeof(SqlXml) == col.DataType) || col.DataType == typeof(DateTimeOffset) || col.DataType == typeof(System.Numerics.BigInteger))
{ // no need to check if it is Sql typee if it already implements INullable,
root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, col.DataType.FullName);
}
else if ((dt.Length == 0) || col.ImplementsINullable || ((dt == Keywords.XSD_ANYTYPE) && (col.XmlDataType != Keywords.XSD_ANYTYPE)))
{
// in Whidbey, XmlDataTypeName function changed to return "anyType" for typeof(Object)
// should still always hit this code path for all non-built in types
// to handle version qualified typeof(Object) and other CDT objects correctly
// we must write the output exactly the same way as we read it
SetMSDataAttribute(root, col.DataType);
}
}
if (col.ReadOnly)
root.SetAttribute("ReadOnly", Keywords.MSDNS, Keywords.TRUE);
if (col.Expression.Length != 0)
root.SetAttribute("Expression", Keywords.MSDNS, col.Expression);
if (col.AutoIncrement)
{
root.SetAttribute("AutoIncrement", Keywords.MSDNS, Keywords.TRUE);
}
if (col.AutoIncrementSeed != 0)
root.SetAttribute("AutoIncrementSeed", Keywords.MSDNS, col.AutoIncrementSeed.ToString(CultureInfo.InvariantCulture));
if (col.AutoIncrementStep != 1)
root.SetAttribute("AutoIncrementStep", Keywords.MSDNS, col.AutoIncrementStep.ToString(CultureInfo.InvariantCulture));
if (col.Caption != col.ColumnName)
root.SetAttribute("Caption", Keywords.MSDNS, col.Caption);
if (col.Prefix.Length != 0)
root.SetAttribute("Prefix", Keywords.MSDNS, col.Prefix);
if (col.DataType == typeof(DateTime) && col.DateTimeMode != DataSetDateTime.UnspecifiedLocal)
{
root.SetAttribute("DateTimeMode", Keywords.MSDNS, col.DateTimeMode.ToString());
}
}
private string FindTargetNamespace(DataTable table)
{
string tgNamespace = table.TypeName.IsEmpty ? table.Namespace : table.TypeName.Namespace;
if (string.IsNullOrEmpty(tgNamespace))
{
DataRelation[] nestedParentRelations = table.NestedParentRelations;
if (nestedParentRelations.Length != 0)
{
for (int i = 0; i < nestedParentRelations.Length; i++)
{
DataTable parentTable = nestedParentRelations[i].ParentTable;
if (table != parentTable)
{// table can be self nested so it may go to infinite loop!
tgNamespace = FindTargetNamespace(parentTable);
if (!string.IsNullOrEmpty(tgNamespace))
{
break;
}
}
}
}
else
{ // if it does not have any parent table , then it should inherit NS from DataSet
tgNamespace = _ds!.Namespace;
}
}
return tgNamespace;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal XmlElement HandleColumn(DataColumn col, XmlDocument dc, XmlElement schema, bool fWriteOrdinal)
{
Debug.Assert(_prefixes != null);
Debug.Assert(_dc != null);
XmlElement root;
int minOccurs;
Debug.Assert(col.ColumnMapping != MappingType.SimpleContent, "Illegal state");
string refString = (col.ColumnMapping != MappingType.Element) ? Keywords.XSD_ATTRIBUTE : Keywords.XSD_ELEMENT;
root = dc.CreateElement(Keywords.XSD_PREFIX, refString, Keywords.XSDNS);
// First add any attributes.
root.SetAttribute(Keywords.NAME, col.EncodedColumnName);
if (col.Namespace.Length == 0)
{
DataTable _table = col.Table!;
// We need to travese the hirerarchy to find the targetnamepace
string tgNamespace = FindTargetNamespace(_table);
if (col.Namespace != tgNamespace)
{
root.SetAttribute(Keywords.FORM, Keywords.UNQUALIFIED);
}
}
if (col.GetType() != typeof(DataColumn))
AddXdoProperties(col, root);
else
AddColumnProperties(col, root);
AddExtendedProperties(col._extendedProperties, root);
HandleColumnType(col, dc, root, schema);
if (col.ColumnMapping == MappingType.Hidden)
{ // CDT / UDT can not be mapped to Hidden column
if (!col.AllowDBNull)
root.SetAttribute(Keywords.MSD_ALLOWDBNULL, Keywords.MSDNS, Keywords.FALSE);
if (!col.DefaultValueIsNull)
if (col.DataType == typeof(bool))
root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, (bool)(col.DefaultValue) ? Keywords.TRUE : Keywords.FALSE);
else
{
XmlTreeGen.ValidateColumnMapping(col.DataType);
root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, col.ConvertObjectToXml(col.DefaultValue));
}
}
if ((!col.DefaultValueIsNull) && (col.ColumnMapping != MappingType.Hidden))
{
XmlTreeGen.ValidateColumnMapping(col.DataType);
if (col.ColumnMapping == MappingType.Attribute && !col.AllowDBNull)
{
if (col.DataType == typeof(bool))
{
root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, (bool)(col.DefaultValue) ? Keywords.TRUE : Keywords.FALSE);
}
else
{ // CDT / UDT columns cn not be mapped to Attribute also
root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, col.ConvertObjectToXml(col.DefaultValue));
}
}
else
{ // Element Column : need to handle CDT
if (col.DataType == typeof(bool))
{
root.SetAttribute(Keywords.DEFAULT, (bool)(col.DefaultValue) ? Keywords.TRUE : Keywords.FALSE);
}
else
{
if (!col.IsCustomType)
{ // built in type
root.SetAttribute(Keywords.DEFAULT, col.ConvertObjectToXml(col.DefaultValue));
}
else
{ // UDT column
}
}
}
}
if (_schFormat == SchemaFormat.Remoting)
root.SetAttribute(Keywords.TARGETNAMESPACE, Keywords.MSDNS, col.Namespace);
else
{
if ((col.Namespace != (col.Table!.TypeName.IsEmpty ? col.Table.Namespace : col.Table.TypeName.Namespace)) && (col.Namespace.Length != 0))
{
XmlElement schNode = GetSchema(col.Namespace);
if (FindTypeNode(schNode, col.EncodedColumnName) == null)
schNode.AppendChild(root);
root = _dc.CreateElement(Keywords.XSD_PREFIX, refString, Keywords.XSDNS);
root.SetAttribute(Keywords.REF, _prefixes[col.Namespace] + ":" + col.EncodedColumnName);
if (col.Table.Namespace != _ds!.Namespace)
{
_ = GetSchema(col.Table.Namespace);
}
}
}
minOccurs = (col.AllowDBNull) ? 0 : 1;
// March 2001 change
if (col.ColumnMapping == MappingType.Attribute && minOccurs != 0)
root.SetAttribute(Keywords.USE, Keywords.REQUIRED);
if (col.ColumnMapping == MappingType.Hidden)
{
root.SetAttribute(Keywords.USE, Keywords.PROHIBITED);
}
else
if (col.ColumnMapping != MappingType.Attribute && minOccurs != 1)
root.SetAttribute(Keywords.MINOCCURS, minOccurs.ToString(CultureInfo.InvariantCulture));
if ((col.ColumnMapping == MappingType.Element) && fWriteOrdinal)
root.SetAttribute(Keywords.MSD_ORDINAL, Keywords.MSDNS, col.Ordinal.ToString(CultureInfo.InvariantCulture));
return root;
}
internal static string TranslateAcceptRejectRule(AcceptRejectRule rule) =>
rule switch
{
AcceptRejectRule.Cascade => "Cascade",
AcceptRejectRule.None => "None",
_ => null!,
};
internal static string TranslateRule(Rule rule) =>
rule switch
{
Rule.Cascade => "Cascade",
Rule.None => "None",
Rule.SetNull => "SetNull",
Rule.SetDefault => "SetDefault",
_ => null!,
};
internal void AppendChildWithoutRef(string Namespace, XmlElement el)
{
XmlElement schNode = GetSchema(Namespace);
if (FindTypeNode(schNode, el.GetAttribute(Keywords.NAME)) == null)
schNode.AppendChild(el);
}
internal static XmlElement? FindTypeNode(XmlElement node, string strType)
{
if (node == null)
return null;
for (XmlNode? n = node.FirstChild; n != null; n = n.NextSibling)
{
if (!(n is XmlElement))
continue;
XmlElement child = (XmlElement)n;
if (XSDSchema.FEqualIdentity(child, Keywords.XSD_ELEMENT, Keywords.XSDNS) ||
XSDSchema.FEqualIdentity(child, Keywords.XSD_ATTRIBUTE, Keywords.XSDNS) ||
XSDSchema.FEqualIdentity(child, Keywords.XSD_COMPLEXTYPE, Keywords.XSDNS) ||
XSDSchema.FEqualIdentity(child, Keywords.XSD_SIMPLETYPE, Keywords.XSDNS))
{
if (child.GetAttribute(Keywords.NAME) == strType)
return child;
}
}
return null;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal XmlElement HandleTable(DataTable table, XmlDocument dc, XmlElement schema)
{
return HandleTable(table, dc, schema, true);
}
// we write out column Ordinals only if the table contains columns
// that map both to Attributes and Elements
private static bool HasMixedColumns(DataTable table)
{
bool hasAttributes = false;
bool hasElements = false;
foreach (DataColumn col in table.Columns)
{
if (!hasElements && col.ColumnMapping == MappingType.Element)
hasElements = true;
if (!hasAttributes && (col.ColumnMapping == MappingType.Attribute || col.ColumnMapping == MappingType.Hidden))
hasAttributes = !AutoGenerated(col);
if (hasAttributes && hasElements)
return true;
}
return false;
}
internal static bool AutoGenerated(DataColumn col)
{
// for now we use just this simple logic for the columns.
if (col.ColumnMapping != MappingType.Hidden)
return false;
if (col.DataType != typeof(int))
return false;
string generatedname = col.Table!.TableName + "_Id";
if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname + "_0"))
return true;
generatedname = string.Empty;
foreach (DataRelation rel in col.Table.ParentRelations)
{
if (!rel.Nested)
continue;
if (rel.ChildColumnsReference.Length != 1)
continue;
if (rel.ChildColumnsReference[0] != col)
continue;
if (rel.ParentColumnsReference.Length != 1)
continue;
//ok if we are here it means that we have a 1column-1column relation
generatedname = rel.ParentColumnsReference[0].Table!.TableName + "_Id";
}
if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname + "_0"))
return true;
return false;
}
internal static bool AutoGenerated(DataRelation rel)
{
string rName = rel.ParentTable.TableName + "_" + rel.ChildTable.TableName;
if (!rel.RelationName.StartsWith(rName, StringComparison.Ordinal))
return false;
if (rel.ExtendedProperties.Count > 0)
return false;
return true;
}
internal static bool AutoGenerated(UniqueConstraint unique)
{
// for now we use just this simple logic for the columns.
if (!unique.ConstraintName.StartsWith("Constraint", StringComparison.Ordinal))
return false;
if (unique.Key.ColumnsReference.Length != 1)
return false;
if (unique.ExtendedProperties.Count > 0)
return false;
return AutoGenerated(unique.Key.ColumnsReference[0]);
}
private static bool AutoGenerated(ForeignKeyConstraint fk)
{
return AutoGenerated(fk, true);
}
internal static bool AutoGenerated(ForeignKeyConstraint fk, bool checkRelation)
{
// for now we use just this simple logic for the columns.
DataRelation? rel = fk.FindParentRelation();
if (checkRelation)
{
if (rel == null)
return false; // otherwise roundtrip will create column
if (!AutoGenerated(rel))
return false;
if (rel.RelationName != fk.ConstraintName)
return false;
}
if (fk.ExtendedProperties.Count > 0)
return false;
if (fk.AcceptRejectRule != AcceptRejectRule.None)
return false;
if (fk.DeleteRule != Rule.Cascade)
return false;
if (fk.RelatedColumnsReference.Length != 1)
return false;
return AutoGenerated(fk.RelatedColumnsReference[0]);
}
private bool IsAutoGenerated(object o)
{
if (_schFormat != SchemaFormat.Remoting)
return _autogenerated[o] != null;
return false;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal XmlElement HandleTable(DataTable table, XmlDocument dc, XmlElement schema, bool genNested)
{
Debug.Assert(_prefixes != null);
Debug.Assert(_dc != null);
Debug.Assert(_dsElement != null);
XmlElement root = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
bool fWriteOrdinals;
bool fUnqualified = false;
if (((table.DataSet == null) || (_ds != null && table.Namespace != _ds.Namespace)) && (_schFormat == SchemaFormat.Remoting))
root.SetAttribute(Keywords.TARGETNAMESPACE, Keywords.MSDNS, table.Namespace);
// First add any attributes.
root.SetAttribute(Keywords.NAME, table.EncodedTableName);
if (table.Namespace.Length == 0)
{
DataTable _table = table;
string tgNamespace = _table.Namespace;
while (string.IsNullOrEmpty(tgNamespace))
{
DataRelation[] nestedParentRelations = _table.NestedParentRelations;
if (nestedParentRelations.Length == 0)
{
tgNamespace = (_ds != null) ? _ds.Namespace : "";
break;
}
int nestedparentPosition = -1; // it is find non-self-nested-parent
for (int i = 0; i < nestedParentRelations.Length; i++)
{
if (nestedParentRelations[i].ParentTable != _table)
{
nestedparentPosition = i;
break;
}
}
if (nestedparentPosition == -1)
{
break;
}
else
{
_table = nestedParentRelations[nestedparentPosition].ParentTable;
}
tgNamespace = _table.Namespace;
}
if (table.Namespace != tgNamespace)
{
root.SetAttribute(Keywords.FORM, Keywords.UNQUALIFIED);
fUnqualified = true;
}
}
if (table.ShouldSerializeCaseSensitive())
{
root.SetAttribute(Keywords.MSD_CASESENSITIVE, Keywords.MSDNS, table.CaseSensitive.ToString());
}
if (table.ShouldSerializeLocale())
{
root.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, table.Locale.ToString());
}
AddXdoProperties(table, root);
DataColumnCollection columns = table.Columns;
int cCount = columns.Count;
int realCount = 0;
if (cCount == 1 || cCount == 2)
for (int i = 0; i < cCount; i++)
{
DataColumn col = columns[i];
if (col.ColumnMapping == MappingType.Hidden)
{
DataRelationCollection childRelations = table.ChildRelations;
for (int j = 0; j < childRelations.Count; j++)
{
if (childRelations[j].Nested && childRelations[j].ParentKey.ColumnsReference.Length == 1 && childRelations[j].ParentKey.ColumnsReference[0] == col)
realCount++;
}
}
if (col.ColumnMapping == MappingType.Element)
realCount++;
}
if ((table._repeatableElement) && (realCount == 1))
{
// I only have 1 column and that gives me
// the type for this element
DataColumn col = table.Columns[0];
string _typeName = XmlDataTypeName(col.DataType);
if (string.IsNullOrEmpty(_typeName))
{
_typeName = Keywords.XSD_ANYTYPE;
}
root.SetAttribute(Keywords.TYPE, XSDSchema.QualifiedName(_typeName));
return root;
}
// Now add the type information nested inside the element or global.
XmlElement type = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_COMPLEXTYPE, Keywords.XSDNS);
if (!table.TypeName.IsEmpty && _schFormat != SchemaFormat.Remoting)
{
XmlElement typeSchema = GetSchema(table.TypeName.Namespace);
if (string.IsNullOrEmpty(table.TypeName.Namespace))
{
if (_ds == null)
typeSchema = GetSchema(table.Namespace);
else
typeSchema = fUnqualified ? GetSchema(_ds.Namespace) : GetSchema(table.Namespace);
}
if (FindTypeNode(typeSchema, table.TypeName.Name) == null)
typeSchema.AppendChild(type);
type.SetAttribute(Keywords.NAME, table.TypeName.Name);
}
else
{
root.AppendChild(type);
}
if (!table.TypeName.IsEmpty)
{
if (_schFormat != SchemaFormat.Remoting)
root.SetAttribute(Keywords.TYPE, NewDiffgramGen.QualifiedName((string)_prefixes[table.TypeName.Namespace]!, table.TypeName.Name));
}
XmlElement? compositor;
DataColumn? colTxt = table.XmlText;
if (colTxt != null)
{
XmlElement sc = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SIMPLECONTENT, Keywords.XSDNS);
if (colTxt.GetType() != typeof(DataColumn))
AddXdoProperties(colTxt, sc);
else
AddColumnProperties(colTxt, sc);
AddExtendedProperties(colTxt._extendedProperties, sc);
if (colTxt.AllowDBNull)
root.SetAttribute(Keywords.XSD_NILLABLE, string.Empty, Keywords.TRUE);
if (!colTxt.DefaultValueIsNull)
{
XmlTreeGen.ValidateColumnMapping(colTxt.DataType);
sc.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, colTxt.ConvertObjectToXml(colTxt.DefaultValue));
}
sc.SetAttribute(Keywords.MSD_COLUMNNAME, Keywords.MSDNS, colTxt.ColumnName);
sc.SetAttribute(Keywords.MSD_ORDINAL, Keywords.MSDNS, colTxt.Ordinal.ToString(CultureInfo.InvariantCulture));
type.AppendChild(sc);
XmlElement ext = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_EXTENSION, Keywords.XSDNS);
sc.AppendChild(ext);
HandleColumnType(colTxt, dc, ext, schema);
type = ext;
}
compositor = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SEQUENCE, Keywords.XSDNS);
type.AppendChild(compositor);
fWriteOrdinals = HasMixedColumns(table);
for (int i = 0; i < cCount; i++)
{
DataColumn col = columns[i];
if (col.ColumnMapping == MappingType.SimpleContent)
continue;
if (col.ColumnMapping == MappingType.Attribute || col.ColumnMapping == MappingType.Element || col.ColumnMapping == MappingType.Hidden)
{
if (IsAutoGenerated(col)) // skip automanifactured columns
continue;
bool isAttribute = col.ColumnMapping != MappingType.Element;
XmlElement el = HandleColumn(col, dc, schema, fWriteOrdinals);
XmlElement node = isAttribute ? type : compositor;
//bool flag = isAttribute ? col.Namespace == "" : col.Namespace == table.Namespace;
node.AppendChild(el);
}
}
if ((table.XmlText == null) && (genNested))
{
DataRelationCollection childRelations = table.ChildRelations;
for (int j = 0; j < childRelations.Count; j++)
{
XmlElement NestedTable;
if (!childRelations[j].Nested)
continue;
DataTable childTable = childRelations[j].ChildTable;
if (childTable == table)
{ // self join
NestedTable = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
NestedTable.SetAttribute(Keywords.REF, table.EncodedTableName);
}
else if (childTable.NestedParentsCount > 1)
{ // skip relations with multiple parents
NestedTable = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
NestedTable.SetAttribute(Keywords.REF, childTable.EncodedTableName);
}
else
NestedTable = HandleTable(childTable, dc, schema);
if (childTable.Namespace == table.Namespace)
{
NestedTable.SetAttribute(Keywords.MINOCCURS, "0");
NestedTable.SetAttribute(Keywords.MAXOCCURS, Keywords.ZERO_OR_MORE);
}
if ((childTable.Namespace == table.Namespace) || (childTable.Namespace.Length == 0) || _schFormat == SchemaFormat.Remoting)
{
compositor.AppendChild(NestedTable);
}
else
{
if (childTable.NestedParentsCount <= 1)
GetSchema(childTable.Namespace).AppendChild(NestedTable);
NestedTable = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
NestedTable.SetAttribute(Keywords.REF, ((string)_prefixes[childTable.Namespace]!) + ':' + childTable.EncodedTableName);
compositor.AppendChild(NestedTable);
}
if (childRelations[j].ChildKeyConstraint != null)
continue; // we write the relation using the constraint
XmlElement nodeAnn = _dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ANNOTATION, Keywords.XSDNS);
NestedTable.PrependChild(nodeAnn);
XmlElement nodeApp = _dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_APPINFO, Keywords.XSDNS);
nodeAnn.AppendChild(nodeApp);
nodeApp.AppendChild(HandleRelation(childRelations[j], dc));
}
}
if (compositor != null)
if (!compositor.HasChildNodes)
type.RemoveChild(compositor);
// Output all constraints.
ConstraintCollection constraints = table.Constraints;
XmlElement selector, field;
string xpathprefix = (_ds != null) ? (_ds.Namespace.Length != 0 ? Keywords.MSTNS_PREFIX : string.Empty) : string.Empty;
if (_schFormat != SchemaFormat.Remoting)
{
GetSchema(table.Namespace); // to ensure prefix handling
xpathprefix = table.Namespace.Length != 0 ? (string)_prefixes[table.Namespace]! + ':' : string.Empty;
}
for (int i = 0; i < constraints.Count; i++)
{
XmlElement? constraint;
DataColumn[] fields;
if (constraints[i] is UniqueConstraint unique)
{
if (IsAutoGenerated(unique))
continue;
// special case of the ghosted constraints:
fields = unique.Key.ColumnsReference;
constraint = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_UNIQUE, Keywords.XSDNS);
if ((_ds == null) || (_ds.Tables.InternalIndexOf(table.TableName) == -3))
constraint.SetAttribute(Keywords.MSD_TABLENS, Keywords.MSDNS, table.Namespace);
// convert constraint name to valid xml name
constraint.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(unique.SchemaName));
if (unique.ConstraintName != unique.SchemaName)
constraint.SetAttribute(Keywords.MSD_CONSTRAINTNAME, Keywords.MSDNS, unique.ConstraintName);
AddExtendedProperties(unique._extendedProperties, constraint);
selector = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SELECTOR, Keywords.XSDNS);
selector.SetAttribute(Keywords.XSD_XPATH, ".//" + xpathprefix + table.EncodedTableName);
constraint.AppendChild(selector);
if (unique.IsPrimaryKey)
{
constraint.SetAttribute(Keywords.MSD_PRIMARYKEY, Keywords.MSDNS, Keywords.TRUE);
}
if (0 < fields.Length)
{
StringBuilder encodedName = new StringBuilder();
for (int k = 0; k < fields.Length; k++)
{
encodedName.Length = 0;
if (_schFormat != SchemaFormat.Remoting)
{
GetSchema(fields[k].Namespace);
if (!string.IsNullOrEmpty(fields[k].Namespace))
{
encodedName.Append(_prefixes[fields[k].Namespace]).Append(':');
}
encodedName.Append(fields[k].EncodedColumnName);
}
else
{
encodedName.Append(xpathprefix).Append(fields[k].EncodedColumnName);
}
if ((fields[k].ColumnMapping == MappingType.Attribute) || (fields[k].ColumnMapping == MappingType.Hidden))
{
encodedName.Insert(0, '@');
}
field = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_FIELD, Keywords.XSDNS);
field.SetAttribute(Keywords.XSD_XPATH, encodedName.ToString());
constraint.AppendChild(field);
}
}
_dsElement.InsertBefore(constraint, _constraintSeparator);
}
else if (constraints[i] is ForeignKeyConstraint && genNested)
{
ForeignKeyConstraint foreign = (ForeignKeyConstraint)constraints[i];
if (_tables.Count > 0)
{
if (!_tables.Contains(foreign.RelatedTable) || !_tables.Contains(foreign.Table))
continue;
}
if (IsAutoGenerated(foreign))
continue;
DataRelation? rel = foreign.FindParentRelation();
// special case of the ghosted constraints:
fields = foreign.RelatedColumnsReference;
UniqueConstraint? _constraint = (UniqueConstraint?)foreign.RelatedTable.Constraints.FindConstraint(new UniqueConstraint("TEMP", fields));
if (_constraint == null)
{
constraint = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_KEY, Keywords.XSDNS);
constraint.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(foreign.SchemaName));
if ((_ds == null) || (_ds.Tables.InternalIndexOf(table.TableName) == -3))
constraint.SetAttribute(Keywords.MSD_TABLENS, Keywords.MSDNS, table.Namespace);
selector = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SELECTOR, Keywords.XSDNS);
selector.SetAttribute(Keywords.XSD_XPATH, ".//" + xpathprefix + foreign.RelatedTable.EncodedTableName);
constraint.AppendChild(selector);
if (0 < fields.Length)
{
StringBuilder encodedName = new StringBuilder();
for (int k = 0; k < fields.Length; k++)
{
encodedName.Length = 0;
if (_schFormat != SchemaFormat.Remoting)
{
GetSchema(fields[k].Namespace);
if (!string.IsNullOrEmpty(fields[k].Namespace))
{
encodedName.Append(_prefixes[fields[k].Namespace]).Append(':');
}
encodedName.Append(fields[k].EncodedColumnName);
}
else
{
encodedName.Append(xpathprefix).Append(fields[k].EncodedColumnName);
}
if ((fields[k].ColumnMapping == MappingType.Attribute) || (fields[k].ColumnMapping == MappingType.Hidden))
{
encodedName.Insert(0, '@');
}
field = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_FIELD, Keywords.XSDNS);
field.SetAttribute(Keywords.XSD_XPATH, encodedName.ToString());
constraint.AppendChild(field);
}
}
_dsElement.InsertBefore(constraint, _constraintSeparator);
}
constraint = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_KEYREF, Keywords.XSDNS);
// convert constraint name to valid xml name
constraint.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(foreign.SchemaName));
if ((_ds == null) || (_ds.Tables.InternalIndexOf(foreign.RelatedTable.TableName) == -3)) // if there is a conflicting name/namespace only
constraint.SetAttribute(Keywords.MSD_TABLENS, Keywords.MSDNS, foreign.Table!.Namespace);
if (_constraint == null)
constraint.SetAttribute(Keywords.REFER, XmlConvert.EncodeLocalName(foreign.SchemaName));
else
constraint.SetAttribute(Keywords.REFER, XmlConvert.EncodeLocalName(_constraint.SchemaName));
AddExtendedProperties(foreign._extendedProperties, constraint, typeof(ForeignKeyConstraint));
if (foreign.ConstraintName != foreign.SchemaName)
constraint.SetAttribute(Keywords.MSD_CONSTRAINTNAME, Keywords.MSDNS, foreign.ConstraintName);
if (null == rel)
{
constraint.SetAttribute(Keywords.MSD_CONSTRAINTONLY, Keywords.MSDNS, Keywords.TRUE);
}
else
{
if (rel.Nested)
constraint.SetAttribute(Keywords.MSD_ISNESTED, Keywords.MSDNS, Keywords.TRUE);
AddExtendedProperties(rel._extendedProperties, constraint, typeof(DataRelation));
if (foreign.ConstraintName != rel.RelationName)
{
constraint.SetAttribute(Keywords.MSD_RELATIONNAME, Keywords.MSDNS, XmlConvert.EncodeLocalName(rel.RelationName));
}
}
selector = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SELECTOR, Keywords.XSDNS);
selector.SetAttribute(Keywords.XSD_XPATH, ".//" + xpathprefix + table.EncodedTableName);
constraint.AppendChild(selector);
if (foreign.AcceptRejectRule != ForeignKeyConstraint.AcceptRejectRule_Default)
constraint.SetAttribute(Keywords.MSD_ACCEPTREJECTRULE, Keywords.MSDNS,
TranslateAcceptRejectRule(foreign.AcceptRejectRule));
if (foreign.UpdateRule != ForeignKeyConstraint.Rule_Default)
constraint.SetAttribute(Keywords.MSD_UPDATERULE, Keywords.MSDNS, TranslateRule(foreign.UpdateRule));
if (foreign.DeleteRule != ForeignKeyConstraint.Rule_Default)
constraint.SetAttribute(Keywords.MSD_DELETERULE, Keywords.MSDNS, TranslateRule(foreign.DeleteRule));
fields = foreign.Columns;
if (0 < fields.Length)
{
StringBuilder encodedName = new StringBuilder();
for (int k = 0; k < fields.Length; k++)
{
encodedName.Length = 0;
if (_schFormat != SchemaFormat.Remoting)
{
GetSchema(fields[k].Namespace);
if (!string.IsNullOrEmpty(fields[k].Namespace))
{
encodedName.Append(_prefixes[fields[k].Namespace]).Append(':');
}
encodedName.Append(fields[k].EncodedColumnName);
}
else
{
encodedName.Append(xpathprefix).Append(fields[k].EncodedColumnName);
}
if ((fields[k].ColumnMapping == MappingType.Attribute) || (fields[k].ColumnMapping == MappingType.Hidden))
{
encodedName.Insert(0, '@');
}
field = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_FIELD, Keywords.XSDNS);
field.SetAttribute(Keywords.XSD_XPATH, encodedName.ToString());
constraint.AppendChild(field);
}
}
_dsElement.InsertAfter(constraint, _constraintSeparator);
}
}
AddExtendedProperties(table._extendedProperties, root);
return root;
}
/// <summary>
/// resolve the name from the type for schema output and set the msdata:Data attribute
/// </summary>
/// <param name="root"></param>
/// <param name="type">non-special type to resolve</param>
/// <exception cref="DataException">if multipleTargetConverter throws or returns an empty result</exception>
private void SetMSDataAttribute(XmlElement root, Type type)
{
string result = DataStorage.GetQualifiedName(type);
try
{
if (null != _targetConverter)
{
result = _targetConverter(type);
}
if (!string.IsNullOrEmpty(result))
{
// SetAttribute doesn't fail with invalid data, but the final XmlDocument.Save will fail later
// with the ArgumentException when calling the actual XmlWriter.SetAttribute
root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, result);
}
}
catch (Exception ex) when (ADP.IsCatchableExceptionType(ex))
{
ExceptionBuilder.ThrowMultipleTargetConverter(ex);
}
if (string.IsNullOrEmpty(result))
{
ExceptionBuilder.ThrowMultipleTargetConverter(null);
}
}
}
internal sealed class NewDiffgramGen
{
internal XmlDocument _doc;
internal DataSet? _ds;
internal DataTable? _dt;
internal XmlWriter _xmlw = default!; // Late-initialized
private bool _fBefore;
private bool _fErrors;
internal Hashtable _rowsOrder;
private readonly ArrayList _tables = new ArrayList();
private readonly bool _writeHierarchy;
internal NewDiffgramGen(DataSet ds)
{
_ds = ds;
_dt = null;
_doc = new XmlDocument();
for (int i = 0; i < ds.Tables.Count; i++)
{
_tables.Add(ds.Tables[i]);
}
DoAssignments(_tables);
}
internal NewDiffgramGen(DataTable dt, bool writeHierarchy)
{
_ds = null;
_dt = dt;
_doc = new XmlDocument();
_tables.Add(dt);
if (writeHierarchy)
{
_writeHierarchy = true;
CreateTableHierarchy(dt);
}
DoAssignments(_tables);
}
private void CreateTableHierarchy(DataTable dt)
{
// if (!dt.SerializeHierarchy)
// return;
foreach (DataRelation r in dt.ChildRelations)
{
if (!_tables.Contains(r.ChildTable))
{
_tables.Add(r.ChildTable);
CreateTableHierarchy(r.ChildTable);
}
}
}
[MemberNotNull(nameof(_rowsOrder))]
private void DoAssignments(ArrayList tables)
{
int rows = 0;
for (int i = 0; i < tables.Count; i++)
{
rows += ((DataTable)tables[i]!).Rows.Count;
}
_rowsOrder = new Hashtable(rows);
for (int i = 0; i < tables.Count; i++)
{
DataTable dt = (DataTable)tables[i]!;
DataRowCollection rc = dt.Rows;
rows = rc.Count;
for (int j = 0; j < rows; j++)
_rowsOrder[rc[j]] = j;
}
}
private bool EmptyData()
{
for (int i = 0; i < _tables.Count; i++)
{
if (((DataTable)_tables[i]!).Rows.Count > 0)
return false;
}
return true;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(XmlWriter xmlw)
{
Save(xmlw, null);
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(XmlWriter xmlw, DataTable? table)
{
_xmlw = DataTextWriter.CreateWriter(xmlw);
_xmlw.WriteStartElement(Keywords.DFF, Keywords.DIFFGRAM, Keywords.DFFNS);
_xmlw.WriteAttributeString(Keywords.XMLNS, Keywords.MSD, null, Keywords.MSDNS);
// _xmlw.WriteAttributeString(Keywords.XMLNS, Keywords.UPDG, null, Keywords.UPDGNS);
if (!EmptyData())
{
// write the datapart
if (table != null)
new XmlDataTreeWriter(table, _writeHierarchy).SaveDiffgramData(_xmlw, _rowsOrder);
else
new XmlDataTreeWriter(_ds!).SaveDiffgramData(_xmlw, _rowsOrder);
// Walk the xd using relational apis and create nodes in nodeRoot appropriately.
if (table == null)
{
for (int i = 0; i < _ds!.Tables.Count; ++i)
{
GenerateTable(_ds.Tables[i]);
}
}
else
{
for (int i = 0; i < _tables.Count; i++)
{
GenerateTable((DataTable)_tables[i]!);
}
}
if (_fBefore)
_xmlw.WriteEndElement(); //SQL_BEFORE
if (table == null)
{
for (int i = 0; i < _ds!.Tables.Count; ++i)
{
GenerateTableErrors(_ds.Tables[i]);
}
}
else
{
for (int i = 0; i < _tables.Count; i++)
{
GenerateTableErrors((DataTable)_tables[i]!);
}
}
if (_fErrors)
_xmlw.WriteEndElement(); //ERRORS
}
_xmlw.WriteEndElement();
_xmlw.Flush();
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
private void GenerateTable(DataTable table)
{
int rowCount = table.Rows.Count;
if (rowCount <= 0)
return;
for (int rowNum = 0; rowNum < rowCount; ++rowNum)
GenerateRow(table.Rows[rowNum]);
}
private void GenerateTableErrors(DataTable table)
{
int rowCount = table.Rows.Count;
int colCount = table.Columns.Count;
if (rowCount <= 0)
return;
for (int rowNum = 0; rowNum < rowCount; ++rowNum)
{
bool tableName = false;
DataRow row = table.Rows[rowNum];
string prefix = (table.Namespace.Length != 0) ? table.Prefix : string.Empty;
if ((row.HasErrors) && (row.RowError.Length > 0))
{
if (!_fErrors)
{
_xmlw.WriteStartElement(Keywords.DFF, Keywords.MSD_ERRORS, Keywords.DFFNS);
_fErrors = true;
}
_xmlw.WriteStartElement(prefix, row.Table.EncodedTableName, row.Table.Namespace);
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, row.Table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture));
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.MSD_ERROR, Keywords.DFFNS, row.RowError);
tableName = true;
}
if (colCount <= 0)
continue;
for (int colNum = 0; colNum < colCount; ++colNum)
{
DataColumn column = table.Columns[colNum];
string error = row.GetColumnError(column);
string columnPrefix = (column.Namespace.Length != 0) ? column.Prefix : string.Empty;
if (string.IsNullOrEmpty(error))
{
continue;
}
if (!tableName)
{
if (!_fErrors)
{
_xmlw.WriteStartElement(Keywords.DFF, Keywords.MSD_ERRORS, Keywords.DFFNS);
_fErrors = true;
}
_xmlw.WriteStartElement(prefix, row.Table.EncodedTableName, row.Table.Namespace);
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, row.Table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture));
tableName = true;
}
_xmlw.WriteStartElement(columnPrefix, column.EncodedColumnName, column.Namespace);
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.MSD_ERROR, Keywords.DFFNS, error);
_xmlw.WriteEndElement();
}
if (tableName)
_xmlw.WriteEndElement();
}
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
private void GenerateRow(DataRow row)
{
DataRowState state = row.RowState;
if ((state == DataRowState.Unchanged) || (state == DataRowState.Added))
{
return;
}
if (!_fBefore)
{
_xmlw.WriteStartElement(Keywords.DFF, Keywords.SQL_BEFORE, Keywords.DFFNS);
_fBefore = true;
}
DataTable table = row.Table;
int colCount = table.Columns.Count;
string rowIDString = table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture);
string? parentId = null;
if ((state == DataRowState.Deleted) && (row.Table.NestedParentRelations.Length != 0))
{
DataRow? parentRow = row.GetNestedParentRow(DataRowVersion.Original);
if (parentRow != null)
{
parentId = parentRow.Table.TableName + parentRow.rowID.ToString(CultureInfo.InvariantCulture);
}
}
string tablePrefix = (table.Namespace.Length != 0) ? table.Prefix : string.Empty;
//old row
_xmlw.WriteStartElement(tablePrefix, row.Table.EncodedTableName, row.Table.Namespace);
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, rowIDString);
if ((state == DataRowState.Deleted) && XmlDataTreeWriter.RowHasErrors(row))
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASERRORS, Keywords.DFFNS, Keywords.TRUE);
if (parentId != null)
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFPID, Keywords.DFFNS, parentId);
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.ROWORDER, Keywords.MSDNS, _rowsOrder[row]!.ToString());
for (int colNum = 0; colNum < colCount; ++colNum)
{
if ((row.Table.Columns[colNum].ColumnMapping == MappingType.Attribute) ||
(row.Table.Columns[colNum].ColumnMapping == MappingType.Hidden))
GenerateColumn(row, row.Table.Columns[colNum], DataRowVersion.Original);
}
for (int colNum = 0; colNum < colCount; ++colNum)
{
if ((row.Table.Columns[colNum].ColumnMapping == MappingType.Element) ||
(row.Table.Columns[colNum].ColumnMapping == MappingType.SimpleContent))
GenerateColumn(row, row.Table.Columns[colNum], DataRowVersion.Original);
}
_xmlw.WriteEndElement(); //old row
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
private void GenerateColumn(DataRow row, DataColumn col, DataRowVersion version)
{
string? value;
value = col.GetColumnValueAsString(row, version); // this is useless for CTD
if (value == null)
{
if (col.ColumnMapping == MappingType.SimpleContent)
_xmlw.WriteAttributeString(Keywords.XSI, Keywords.XSI_NIL, Keywords.XSINS, Keywords.TRUE);
return;
}
string colPrefix = (col.Namespace.Length != 0) ? col.Prefix : string.Empty;
switch (col.ColumnMapping)
{
case MappingType.Attribute:
_xmlw.WriteAttributeString(colPrefix, col.EncodedColumnName, col.Namespace, value);
break;
case MappingType.Hidden:
_xmlw.WriteAttributeString(Keywords.MSD, "hidden" + col.EncodedColumnName, Keywords.MSDNS, value);
break;
case MappingType.SimpleContent:
_xmlw.WriteString(value);
break;
case MappingType.Element:
bool startElementSkipped = true;
object columnValue = row[col, version];
// if the object is built in type or if it implements IXMLSerializable, write the start Element, otherwise
//(if CDT and does not implement IXmlSerializable) skip it
if (!col.IsCustomType || !DataColumn.IsValueCustomTypeInstance(columnValue) || (typeof(IXmlSerializable).IsAssignableFrom(columnValue.GetType())))
{
_xmlw.WriteStartElement(colPrefix, col.EncodedColumnName, col.Namespace);
startElementSkipped = false;
}
Type valuesType = columnValue.GetType();
if (!col.IsCustomType)
{ // if column's type is built in type CLR or SQLType
if (valuesType == typeof(char) || valuesType == typeof(string))
{
if (XmlDataTreeWriter.PreserveSpace(value))
{
_xmlw.WriteAttributeString(Keywords.XML, Keywords.SPACE, Keywords.XML_XMLNS, Keywords.PRESERVE);
}
}
_xmlw.WriteString(value);
}
else
{ // Columns type is CDT
if ((columnValue != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(columnValue)))
{
if (DataColumn.IsValueCustomTypeInstance(columnValue)/* && valuesType != typeof(Type)*/)
{// value is also CDT
// if SkippedElement, ie does not implement IXMLSerializable: so No Polymorphysm Support.
if (!startElementSkipped && columnValue.GetType() != col.DataType)
{
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, DataStorage.GetQualifiedName(valuesType));
}
if (!startElementSkipped)
{
col.ConvertObjectToXml(columnValue, _xmlw, null); // XmlRootAttribute MUST be passed null
}
else
{
// this column's type does not implement IXmlSerializable, so we need to handle serialization via XmlSerializer
if (columnValue.GetType() != col.DataType)
{ // throw if polymorphism; not supported
throw ExceptionBuilder.PolymorphismNotSupported(valuesType.AssemblyQualifiedName!);
}
// therefore we are skipping the start element, but by passing XmlRootAttribute with the same name as
// we open the start element (column's name), XmlSerializer will open and close it for us
XmlRootAttribute xmlAttrib = new XmlRootAttribute(col.EncodedColumnName);
xmlAttrib.Namespace = col.Namespace;
col.ConvertObjectToXml(columnValue, _xmlw, xmlAttrib);
}
}
else
{ // value is built in CLR type (eg: string, int etc.)
// these basic clr types do not have direct xsd type mappings
if (valuesType == typeof(Type) || valuesType == typeof(Guid) || valuesType == typeof(char) ||
DataStorage.IsSqlType(valuesType))
{ // if unmapped type or SQL type write msdata:Datatype=typeofinstance
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, valuesType.FullName);
}
else if (columnValue is Type)
{
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, Keywords.TYPEINSTANCE);
}
else
{
string xsdTypeName = Keywords.XSD_PREFIXCOLON + XmlTreeGen.XmlDataTypeName(valuesType);
_xmlw.WriteAttributeString(Keywords.XSI, Keywords.TYPE, Keywords.XSINS, xsdTypeName);
_xmlw.WriteAttributeString(Keywords.XSD_PREFIX, Keywords.XMLNS, Keywords.XSDNS, xsdTypeName);
}
if (!DataStorage.IsSqlType(valuesType))
{
_xmlw.WriteString(col.ConvertObjectToXml(columnValue));
}
else
{
col.ConvertObjectToXml(columnValue, _xmlw, null);
}
}
}
}
if (!startElementSkipped)
{
_xmlw.WriteEndElement();
}
break;
}
}
internal static string QualifiedName(string prefix, string name)
{
if (prefix != null)
return prefix + ":" + name;
return name;
}
}
// DataTreeWriter
internal sealed class XmlDataTreeWriter
{
private XmlWriter? _xmlw;
private readonly DataSet? _ds;
private readonly DataTable? _dt;
private readonly ArrayList _dTables = new ArrayList();
private readonly DataTable[] _topLevelTables;
private readonly bool _fFromTable; // also means no hierarchy
private bool _isDiffgram;
private Hashtable? _rowsOrder;
private readonly bool _writeHierarchy;
internal XmlDataTreeWriter(DataSet ds)
{
_ds = ds;
_topLevelTables = ds.TopLevelTables();
foreach (DataTable table in ds.Tables)
{
_dTables.Add(table);
}
}
internal XmlDataTreeWriter(DataTable dt, bool writeHierarchy)
{
_dt = dt;
_fFromTable = true;
if (dt.DataSet == null)
{
_dTables.Add(dt);
_topLevelTables = new DataTable[] { dt };
}
else
{
_ds = dt.DataSet;
_dTables.Add(dt);
if (writeHierarchy)
{
_writeHierarchy = true;
CreateTablesHierarchy(dt);
_topLevelTables = CreateToplevelTables();
}
else // if no hierarchy , top level table should be dt
_topLevelTables = new DataTable[] { dt };
}
}
private DataTable[] CreateToplevelTables()
{
ArrayList topTables = new ArrayList();
for (int i = 0; i < _dTables.Count; i++)
{
DataTable table = (DataTable)_dTables[i]!;
if (table.ParentRelations.Count == 0)
topTables.Add(table);
else
{
bool fNestedButNotSelfNested = false;
for (int j = 0; j < table.ParentRelations.Count; j++)
{
if (table.ParentRelations[j].Nested)
{
if (table.ParentRelations[j].ParentTable == table)
{
fNestedButNotSelfNested = false;
break;
}
fNestedButNotSelfNested = true;
}
}
if (!fNestedButNotSelfNested)
topTables.Add(table);
}
}
if (topTables.Count == 0)
{
return Array.Empty<DataTable>();
}
var temp = new DataTable[topTables.Count];
topTables.CopyTo(temp, 0);
return temp;
}
private void CreateTablesHierarchy(DataTable dt)
{
// if (!dt.SerializeHierarchy)
// return;
foreach (DataRelation r in dt.ChildRelations)
{
if (!_dTables.Contains(r.ChildTable))
{
_dTables.Add(r.ChildTable);
CreateTablesHierarchy(r.ChildTable);
}
}
}
internal static bool RowHasErrors(DataRow row)
{
int colCount = row.Table.Columns.Count;
if ((row.HasErrors) && (row.RowError.Length > 0))
return true;
for (int colNum = 0; colNum < colCount; ++colNum)
{
DataColumn column = row.Table.Columns[colNum];
string error = row.GetColumnError(column);
if (string.IsNullOrEmpty(error))
{
continue;
}
return true;
}
return false;
}
// the following line writes the data part
// for the new diffgram format
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void SaveDiffgramData(XmlWriter xw, Hashtable rowsOrder)
{
Debug.Assert(_ds != null || _dt != null);
_xmlw = DataTextWriter.CreateWriter(xw);
_isDiffgram = true;
_rowsOrder = rowsOrder;
string prefix = (_ds != null) ? ((_ds.Namespace.Length == 0) ? "" : _ds.Prefix) : ((_dt!.Namespace.Length == 0) ? "" : _dt.Prefix);
if (_ds == null || string.IsNullOrEmpty(_ds.DataSetName))
_xmlw.WriteStartElement(prefix, Keywords.DOCUMENTELEMENT, (_dt!.Namespace == null) ? "" : _dt.Namespace);
else
_xmlw.WriteStartElement(prefix, XmlConvert.EncodeLocalName(_ds.DataSetName), _ds.Namespace);
// new XmlTreeGen(true).Save(_ds,_xmlw, false /* we don't care since we specified it's serialized */);
for (int i = 0; i < _dTables.Count; i++)
{
DataTable tempTable = ((DataTable)_dTables[i]!);
foreach (DataRow row in tempTable.Rows)
{
if (row.RowState == DataRowState.Deleted)
continue;
int nestedParentRowCount = row.GetNestedParentCount();
if (nestedParentRowCount == 0)
{
DataTable tempDT = ((DataTable)_dTables[i]!);
XmlDataRowWriter(row, tempDT.EncodedTableName);
}
else if (nestedParentRowCount > 1)
{
throw ExceptionBuilder.MultipleParentRows(tempTable.Namespace.Length == 0 ? tempTable.TableName : tempTable.Namespace + tempTable.TableName);
// At all times a nested row can only have 0 or 1 parents, never more than 1
}
}
}
_xmlw.WriteEndElement();
_xmlw.Flush();
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void Save(XmlWriter xw, bool writeSchema)
{
Debug.Assert(_ds != null || _dt != null);
_xmlw = DataTextWriter.CreateWriter(xw);
int countTopTable = _topLevelTables.Length;
bool fWriteDSElement = true;
string prefix = (_ds != null) ? ((_ds.Namespace.Length == 0) ? "" : _ds.Prefix) : ((_dt!.Namespace.Length == 0) ? "" : _dt.Prefix);
if (!writeSchema && _ds != null && _ds._fTopLevelTable && countTopTable == 1)
{
if (_ds.TopLevelTables()[0].Rows.Count == 1)
fWriteDSElement = false;
}
if (fWriteDSElement)
{
if (_ds == null)
{
_xmlw.WriteStartElement(prefix, Keywords.DOCUMENTELEMENT, _dt!.Namespace);
}
else
{
if (string.IsNullOrEmpty(_ds.DataSetName))
_xmlw.WriteStartElement(prefix, Keywords.DOCUMENTELEMENT, _ds.Namespace);
else
_xmlw.WriteStartElement(prefix, XmlConvert.EncodeLocalName(_ds.DataSetName), _ds.Namespace);
}
for (int i = 0; i < _dTables.Count; i++)
{
if (((DataTable)_dTables[i]!)._xmlText != null)
{
_xmlw.WriteAttributeString(Keywords.XMLNS, Keywords.XSI, Keywords.XSD_XMLNS_NS, Keywords.XSINS);
break;
}
}
if (writeSchema)
{
if (!_fFromTable)
{
new XmlTreeGen(SchemaFormat.Public).Save(_ds!, _xmlw);
}
else
{
new XmlTreeGen(SchemaFormat.Public).Save(null, _dt!, _xmlw, _writeHierarchy);
}
}
}
for (int i = 0; i < _dTables.Count; i++)
{
foreach (DataRow row in ((DataTable)_dTables[i]!).Rows)
{
if (row.RowState == DataRowState.Deleted)
continue;
int parentRowCount = row.GetNestedParentCount();
if (parentRowCount == 0)
{
XmlDataRowWriter(row, ((DataTable)_dTables[i]!).EncodedTableName);
}
else if (parentRowCount > 1)
{
DataTable dt = (DataTable)_dTables[i]!;
throw ExceptionBuilder.MultipleParentRows(dt.Namespace.Length == 0 ? dt.TableName : (dt.Namespace + dt.TableName));
// At all times a nested row can only have 0 or 1 parents, never more than 1
}
}
}
if (fWriteDSElement)
_xmlw.WriteEndElement();
_xmlw.Flush();
}
private static ArrayList GetNestedChildRelations(DataRow row)
{
ArrayList list = new ArrayList();
foreach (DataRelation r in row.Table.ChildRelations)
{
if (r.Nested)
list.Add(r);
}
return list;
}
[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
internal void XmlDataRowWriter(DataRow row, string encodedTableName)
{
Debug.Assert(_xmlw != null);
object value;
string prefix = (row.Table.Namespace.Length == 0) ? "" : row.Table.Prefix;
_xmlw.WriteStartElement(prefix, encodedTableName, row.Table.Namespace);
if (_isDiffgram)
{
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, row.Table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture));
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.ROWORDER, Keywords.MSDNS, _rowsOrder![row]!.ToString());
if (row.RowState == DataRowState.Added)
{
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASCHANGES, Keywords.DFFNS, Keywords.INSERTED);
}
if (row.RowState == DataRowState.Modified)
{
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASCHANGES, Keywords.DFFNS, Keywords.MODIFIED);
}
if (RowHasErrors(row))
{
_xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASERRORS, Keywords.DFFNS, Keywords.TRUE);
}
}
//write the attribute columns first, if any
foreach (DataColumn col in row.Table.Columns)
{
if (col._columnMapping == MappingType.Attribute)
{
value = row[col];
string colPrefix = (col.Namespace.Length == 0) ? "" : col.Prefix;
if ((value != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(value)))
{
XmlTreeGen.ValidateColumnMapping(col.DataType);
_xmlw.WriteAttributeString(colPrefix, col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml(value));
}
}
if (!_isDiffgram)
continue;
if (col._columnMapping == MappingType.Hidden)
{
value = row[col];
if ((value != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(value)))
{
XmlTreeGen.ValidateColumnMapping(col.DataType);
_xmlw.WriteAttributeString(Keywords.MSD, "hidden" + col.EncodedColumnName, Keywords.MSDNS, col.ConvertObjectToXml(value));
}
}
} //end foreach
foreach (DataColumn col in row.Table.Columns)
{
if (col._columnMapping != MappingType.Hidden)
{
value = row[col];
string colPrefix = (col.Namespace.Length == 0) ? "" : col.Prefix;
bool startElementSkipped = true;
if (((value == DBNull.Value) || (col.ImplementsINullable && DataStorage.IsObjectSqlNull(value))) && (col.ColumnMapping == MappingType.SimpleContent))
_xmlw.WriteAttributeString(Keywords.XSI, Keywords.XSI_NIL, Keywords.XSINS, Keywords.TRUE);
// basically this is a continue; if it is null we write xsi:nil='true'
// below, the check is if it is not null
if (((value != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(value))) && (col._columnMapping != MappingType.Attribute))
{
if (col._columnMapping != MappingType.SimpleContent)
{
// again, if we need to use XmlSerializer, do not write start Element (see above for more info)
if (!col.IsCustomType || !DataColumn.IsValueCustomTypeInstance(value) || (typeof(IXmlSerializable).IsAssignableFrom(value.GetType())))
{
_xmlw.WriteStartElement(colPrefix, col.EncodedColumnName, col.Namespace);
startElementSkipped = false;
}
}
Type valuesType = value.GetType();
if (!col.IsCustomType)
{ // if column's type is built in type: CLR and SQLTypes : ie storage supported types
if (valuesType == typeof(char) || valuesType == typeof(string))
{
if (PreserveSpace(value))
{
_xmlw.WriteAttributeString(Keywords.XML, Keywords.SPACE, Keywords.XML_XMLNS, Keywords.PRESERVE);
}
}
_xmlw.WriteString(col.ConvertObjectToXml(value));
}
else
{ // Columns type is CDT
if (DataColumn.IsValueCustomTypeInstance(value) /*&& !(value is Type) && valuesType != typeof(Type)*/)
{// value is also CDT
// if SkippedElement, ie does not implement IXMLSerializable: so No Polymorphism Support.
if (!startElementSkipped && valuesType != col.DataType)
{ // for polymorphism.
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, DataStorage.GetQualifiedName(valuesType));
}
if (!startElementSkipped)
{ // make sure XmlRootAttribute is passed null as this type implement IXmlSerializable
col.ConvertObjectToXml(value, _xmlw, null); // pass XmlRootAttribute as null, it also means: No XmlSerializer
}
else
{ // startElement is skipped: this column's type does not implement IXmlSerializable, need to go via XmlSerializer
if (value.GetType() != col.DataType)
{ // throw if polymorphism; not supported
throw ExceptionBuilder.PolymorphismNotSupported(valuesType.AssemblyQualifiedName!);
}
// therefore we are skipping the start element, but by passing XmlRootAttribute with the same name as
// we open the start element (column's name), XmlSerializer will open and close it for us
XmlRootAttribute xmlAttrib = new XmlRootAttribute(col.EncodedColumnName);
xmlAttrib.Namespace = col.Namespace;
col.ConvertObjectToXml(value, _xmlw, xmlAttrib);
}
}
else
{ // this is case that column type is object and value is CLR or SQLTypes
if (valuesType == typeof(Type) || valuesType == typeof(Guid) || valuesType == typeof(char) ||
DataStorage.IsSqlType(valuesType))
{ // if unmapped type or SQL type write msdata:Datatype=typeofinstance
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, valuesType.FullName);
}
else if (value is Type)
{
_xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, Keywords.TYPEINSTANCE);
}
else
{
string xsdTypeName = Keywords.XSD_PREFIXCOLON + XmlTreeGen.XmlDataTypeName(valuesType);
_xmlw.WriteAttributeString(Keywords.XSI, Keywords.TYPE, Keywords.XSINS, xsdTypeName);
_xmlw.WriteAttributeString(Keywords.XSD_PREFIX, Keywords.XMLNS, Keywords.XSDNS, xsdTypeName);
}
if (!DataStorage.IsSqlType(valuesType))
{
_xmlw.WriteString(col.ConvertObjectToXml(value));
}
else
{
col.ConvertObjectToXml(value, _xmlw, null);
}
}
}
if (col._columnMapping != MappingType.SimpleContent && !startElementSkipped)
_xmlw.WriteEndElement();
}
}
} //end foreach
if (_ds != null)
foreach (DataRelation dr in GetNestedChildRelations(row))
{
foreach (DataRow r in row.GetChildRows(dr))
{
XmlDataRowWriter(r, dr.ChildTable.EncodedTableName);
}
}
_xmlw.WriteEndElement();
}
internal static bool PreserveSpace(object value)
{
Debug.Assert(value != null, "Value can not be null");
string tempValue = value.ToString()!;
if (tempValue.Length == 0)
{
return false;
}
for (int i = 0; i < tempValue.Length; i++)
{
if (!char.IsWhiteSpace(tempValue, i))
{
return false;
}
}
return true;
}
}
internal sealed class DataTextWriter : XmlWriter
{
private readonly XmlWriter _xmltextWriter;
internal static XmlWriter CreateWriter(XmlWriter xw)
{
return new DataTextWriter(xw);
}
private DataTextWriter(XmlWriter w)
{
_xmltextWriter = w;
}
internal Stream? BaseStream
{
get
{
XmlTextWriter? textWriter = _xmltextWriter as XmlTextWriter;
if (null != textWriter)
{
return textWriter.BaseStream;
}
return null;
}
}
public override void WriteStartDocument()
{
_xmltextWriter.WriteStartDocument();
}
public override void WriteStartDocument(bool standalone)
{
_xmltextWriter.WriteStartDocument(standalone);
}
public override void WriteEndDocument()
{
_xmltextWriter.WriteEndDocument();
}
public override void WriteDocType(string name, string? pubid, string? sysid, string? subset)
{
_xmltextWriter.WriteDocType(name, pubid, sysid, subset);
}
public override void WriteStartElement(string? prefix, string localName, string? ns)
{
_xmltextWriter.WriteStartElement(prefix, localName, ns);
}
public override void WriteEndElement()
{
_xmltextWriter.WriteEndElement();
}
public override void WriteFullEndElement()
{
_xmltextWriter.WriteFullEndElement();
}
public override void WriteStartAttribute(string? prefix, string localName, string? ns)
{
_xmltextWriter.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteEndAttribute()
{
_xmltextWriter.WriteEndAttribute();
}
public override void WriteCData(string? text)
{
_xmltextWriter.WriteCData(text);
}
public override void WriteComment(string? text)
{
_xmltextWriter.WriteComment(text);
}
public override void WriteProcessingInstruction(string name, string? text)
{
_xmltextWriter.WriteProcessingInstruction(name, text);
}
public override void WriteEntityRef(string name)
{
_xmltextWriter.WriteEntityRef(name);
}
public override void WriteCharEntity(char ch)
{
_xmltextWriter.WriteCharEntity(ch);
}
public override void WriteWhitespace(string? ws)
{
_xmltextWriter.WriteWhitespace(ws);
}
public override void WriteString(string? text)
{
_xmltextWriter.WriteString(text);
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
_xmltextWriter.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteChars(char[] buffer, int index, int count)
{
_xmltextWriter.WriteChars(buffer, index, count);
}
public override void WriteRaw(char[] buffer, int index, int count)
{
_xmltextWriter.WriteRaw(buffer, index, count);
}
public override void WriteRaw(string data)
{
_xmltextWriter.WriteRaw(data);
}
public override void WriteBase64(byte[] buffer, int index, int count)
{
_xmltextWriter.WriteBase64(buffer, index, count);
}
public override void WriteBinHex(byte[] buffer, int index, int count)
{
_xmltextWriter.WriteBinHex(buffer, index, count);
}
public override WriteState WriteState
{
get
{
return _xmltextWriter.WriteState;
}
}
public override void Close()
{
_xmltextWriter.Close();
}
public override void Flush()
{
_xmltextWriter.Flush();
}
public override void WriteName(string name)
{
_xmltextWriter.WriteName(name);
}
public override void WriteQualifiedName(string localName, string? ns)
{
_xmltextWriter.WriteQualifiedName(localName, ns);
}
public override string? LookupPrefix(string ns)
{
return _xmltextWriter.LookupPrefix(ns);
}
public override XmlSpace XmlSpace
{
get
{
return _xmltextWriter.XmlSpace;
}
}
public override string? XmlLang
{
get
{
return _xmltextWriter.XmlLang;
}
}
public override void WriteNmToken(string name)
{
_xmltextWriter.WriteNmToken(name);
}
}
internal sealed class DataTextReader : XmlReader
{
private readonly XmlReader _xmlreader;
internal static XmlReader CreateReader(XmlReader xr)
{
Debug.Assert(!(xr is DataTextReader), "XmlReader is DataTextReader");
return new DataTextReader(xr);
}
private DataTextReader(XmlReader input)
{
_xmlreader = input;
}
public override XmlReaderSettings? Settings
{
get
{
return _xmlreader.Settings;
}
}
public override XmlNodeType NodeType
{
get
{
return _xmlreader.NodeType;
}
}
public override string Name
{
get
{
return _xmlreader.Name;
}
}
public override string LocalName
{
get
{
return _xmlreader.LocalName;
}
}
public override string NamespaceURI
{
get
{
return _xmlreader.NamespaceURI;
}
}
public override string Prefix
{
get { return _xmlreader.Prefix; }
}
public override bool HasValue
{
get { return _xmlreader.HasValue; }
}
public override string Value
{
get { return _xmlreader.Value; }
}
public override int Depth
{
get { return _xmlreader.Depth; }
}
public override string BaseURI
{
get { return _xmlreader.BaseURI; }
}
public override bool IsEmptyElement
{
get { return _xmlreader.IsEmptyElement; }
}
public override bool IsDefault
{
get { return _xmlreader.IsDefault; }
}
public override char QuoteChar
{
get { return _xmlreader.QuoteChar; }
}
public override XmlSpace XmlSpace
{
get { return _xmlreader.XmlSpace; }
}
public override string XmlLang
{
get { return _xmlreader.XmlLang; }
}
public override int AttributeCount { get { return _xmlreader.AttributeCount; } }
public override string? GetAttribute(string name)
{
return _xmlreader.GetAttribute(name);
}
public override string? GetAttribute(string localName, string? namespaceURI)
{
return _xmlreader.GetAttribute(localName, namespaceURI);
}
public override string GetAttribute(int i)
{
return _xmlreader.GetAttribute(i);
}
public override bool MoveToAttribute(string name)
{
return _xmlreader.MoveToAttribute(name);
}
public override bool MoveToAttribute(string localName, string? namespaceURI)
{
return _xmlreader.MoveToAttribute(localName, namespaceURI);
}
public override void MoveToAttribute(int i)
{
_xmlreader.MoveToAttribute(i);
}
public override bool MoveToFirstAttribute()
{
return _xmlreader.MoveToFirstAttribute();
}
public override bool MoveToNextAttribute()
{
return _xmlreader.MoveToNextAttribute();
}
public override bool MoveToElement()
{
return _xmlreader.MoveToElement();
}
public override bool ReadAttributeValue()
{
return _xmlreader.ReadAttributeValue();
}
public override bool Read()
{
return _xmlreader.Read();
}
public override bool EOF
{
get { return _xmlreader.EOF; }
}
public override void Close()
{
_xmlreader.Close();
}
public override ReadState ReadState
{
get { return _xmlreader.ReadState; }
}
public override void Skip()
{
_xmlreader.Skip();
}
public override XmlNameTable NameTable
{
get { return _xmlreader.NameTable; }
}
public override string? LookupNamespace(string prefix)
{
return _xmlreader.LookupNamespace(prefix);
}
public override bool CanResolveEntity
{
get { return _xmlreader.CanResolveEntity; }
}
public override void ResolveEntity()
{
_xmlreader.ResolveEntity();
}
public override bool CanReadBinaryContent
{
get { return _xmlreader.CanReadBinaryContent; }
}
public override int ReadContentAsBase64(byte[] buffer, int index, int count)
{
return _xmlreader.ReadContentAsBase64(buffer, index, count);
}
public override int ReadElementContentAsBase64(byte[] buffer, int index, int count)
{
return _xmlreader.ReadElementContentAsBase64(buffer, index, count);
}
public override int ReadContentAsBinHex(byte[] buffer, int index, int count)
{
return _xmlreader.ReadContentAsBinHex(buffer, index, count);
}
public override int ReadElementContentAsBinHex(byte[] buffer, int index, int count)
{
return _xmlreader.ReadElementContentAsBinHex(buffer, index, count);
}
public override bool CanReadValueChunk
{
get { return _xmlreader.CanReadValueChunk; }
}
public override string ReadString()
{
return _xmlreader.ReadString();
}
}
}
|