//------------------------------------------------------------------------------
// <copyright file="xmlsaver.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------

namespace System.Data {
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data.SqlTypes;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Runtime.Versioning;

    internal enum SchemaFormat {
        Public = 1,
        Remoting = 2,
        WebService = 3,
        RemotingSkipSchema = 4,
        WebServiceSkipSchema = 5
    }

    /// <devdoc>
    /// </devdoc>
    internal sealed class XmlTreeGen {
        ArrayList ConstraintNames;
        Hashtable namespaces;
        Hashtable autogenerated;
        Hashtable prefixes;

        DataSet _ds;
        ArrayList _tables = new ArrayList();
        ArrayList _relations = new ArrayList();

        XmlDocument _dc;
        XmlElement _sRoot;
        int prefixCount = 0;
        private SchemaFormat schFormat = SchemaFormat.Public;
        private string filePath = null;
        private string fileName = null;
        private string fileExt = null;
        XmlElement dsElement = null;
        XmlElement constraintSeparator = null;

        /// <summary>
        /// This converter allows new versions of the framework to write
        /// the assembly version of older versions of the framework.
        /// For example, having Dev10 using V4.0 target V2.0 of the framework.
        /// </summary>
        Converter<Type, string> targetConverter;
        
        internal XmlTreeGen(SchemaFormat format) {
            this.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 = (String) 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 = (String) 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);
                }
            }
        }

        internal void AddXdoProperties(Object instance, XmlElement root, XmlDocument xd) {
            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, xd);
            }
            return;
        }

        internal void AddXdoProperty(PropertyDescriptor pd, Object instance, XmlElement root, XmlDocument xd) {
            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 
                 type != typeof(bool) &&
                 type != typeof(Type) &&
                 type != typeof(object) &&
                 type != typeof(CultureInfo) &&
                 type != typeof(Int64) &&
                 type != typeof(Int32) ) {
                return;
            }

            if ((!pd.ShouldSerializeValue(instance) || !pd.Attributes.Contains(DesignerSerializationVisibilityAttribute.Visible))&&( bIsSqlType == false)) {
                return;
            }

            Object propInst = pd.GetValue(instance) ;

            if (propInst is InternalDataCollectionBase)
                return;

            if (propInst is PropertyCollection) {
                return;
            }
            // [....]: perf: Why not have this as a table?
            // there are several xdo properties that equal to some xml attributes, we should not explicitly ouput them.
            if (
                0 == String.Compare(pd.Name, "Namespace"    , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "PrimaryKey"   , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "ColumnName"   , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "DefaultValue" , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "TableName"    , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "DataSetName"  , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "AllowDBNull"  , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "Unique"       , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "NestedInDataSet" , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "Locale"       , StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "CaseSensitive", StringComparison.Ordinal) ||
                0 == String.Compare(pd.Name, "RemotingFormat"       , StringComparison.Ordinal)
            ) {
                return;
            }

            if (bisDataColumn){    //(instance is DataColumn) {
                if (0 == String.Compare(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
                        this.SetMSDataAttribute(root, col.DataType);
                    }
                    return;
                }
                if (0 == String.Compare(pd.Name, "Attribute", StringComparison.Ordinal)) {
                    return;
                }
            }

            string textValue = pd.Converter.ConvertToString(propInst) ;
            root.SetAttribute(pd.Name, Keywords.MSDNS, textValue);
            return;

        }

//
        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(Boolean)|| type == typeof(SqlBoolean))
                return "boolean";
            if (type == typeof(Single)|| 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(Int16)  || type == typeof(SqlInt16))
                return "short";
            if (type == typeof(Int32)  || type == typeof(SqlInt32))
                return "int";
            if (type == typeof(Int64)  || type == typeof(SqlInt64))
                return "long";
            if (type == typeof(UInt16))
                return "unsignedShort";
            if (type == typeof(UInt32))
                return "unsignedInt";
            if (type == typeof(UInt64))
                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 wont
            // 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((DataTable)(((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 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(XmlDocument xd, XmlElement rootSchema, string targetNamespace) {
/*
            if (_ds != null)
                rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName(_ds.DataSetName));
            else
                rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName("NewDataSet"));
*/


                if (!Common.ADP.IsEmpty(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 (!Common.ADP.IsEmpty(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((DataTable)r.ChildTable)) {
                    _tables.Add((DataTable)r.ChildTable);
                    CreateTablesHierarchy(r.ChildTable);
                }
            }
        }

        private void CreateRelations(DataTable dt) {
            foreach( DataRelation r in dt.ChildRelations ) {
                if (! _relations.Contains((DataRelation)r)) {
                    _relations.Add((DataRelation)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 (new DataTable[0]);
            DataTable[] 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).
        // These XSDs are not exposed out of this method, so ResourceExposure annotation is None.
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        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(xd, rootSchema, _ds.Namespace);
            }
            else {
                WriteSchemaRoot(xd, 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, xd );
                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, xd );
                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)) || Common.ADP.IsEmpty(top[i].Namespace) || (schFormat == SchemaFormat.Remoting)) {
                    bool fNestedInDataset = top[i].fNestedInDataset;

                    if (((_ds != null )&& (_ds.Namespace.Length != 0)) && Common.ADP.IsEmpty(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) || Common.ADP.IsEmpty(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(rootSchema, top[i].Namespace, el, Keywords.XSD_ELEMENT);
                    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-heirarchical relations without constraints

            DataRelation [] rels = new DataRelation[0];
            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) || Common.ADP.IsEmpty(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");
                    }
                    ((XmlNode)rootSchema).PrependChild((XmlNode)_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) || Common.ADP.IsEmpty(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");
                            }

                            ((XmlNode)tNode).PrependChild((XmlNode)_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) {
                ((XmlNode)rootSchema).PrependChild((XmlNode)comment);
            }

            if (!genSecondary) {
                xmlWriter.Flush();
            }
            return;// rootSchema;
        }

        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(xd, rootSchema, dt.Namespace);

            XmlElement dsCompositor = 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) {
            DataSet dataSet = (ds != null) ? 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) { // WebData 111631 :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 = null; 

            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;
            }

            this.filePath = Path.GetDirectoryName(fs.Name);
            this.fileName = Path.GetFileNameWithoutExtension(fs.Name);
            this.fileExt = Path.GetExtension(fs.Name);
            if (!Common.ADP.IsEmpty(this.filePath))
                this.filePath  = filePath + "\\";
        }

        internal void Save(DataSet ds, XmlWriter xw) {
            Save(ds, (DataTable)null, xw);
        }

        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);
        }

        internal void Save(DataSet ds, DataTable dt, XmlWriter xw) {
            Save(ds, dt, xw, false);
        }

        internal void Save(DataSet ds, DataTable dt, XmlWriter xw, bool writeHierarchy) {
            this.Save(ds, dt, xw, writeHierarchy, null);
        }

        internal void Save(DataSet ds, DataTable dt, XmlWriter xw, bool writeHierarchy, Converter<Type, string> multipleTargetConverter)
        {
            this.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) {
                    XmlElement e = (XmlElement) n;
                    if(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(_dc, schemaEl, NamespaceURI);
                if (!Common.ADP.IsEmpty(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) {
            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 (name != null && name.Length != 0) {
                        // 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
    // [....]: 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 (typeName == null || typeName.Length == 0) {
                    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
                    this.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 (Common.ADP.IsEmpty(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 (!Common.ADP.IsEmpty(tgNamespace)) {
                                break;
                            }
                        }
                    }
                }
                else { // if it does not have any parent table , then it should inherit NS from DataSet
                    tgNamespace = _ds.Namespace;
                }
            }
            return tgNamespace;            
        }

        internal XmlElement HandleColumn(DataColumn col, XmlDocument dc, XmlElement schema, bool fWriteOrdinal) {
            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, dc);
            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) {
                            string prefix = (string)prefixes[col.Namespace];
                            XmlElement tNode = GetSchema(col.Table.Namespace);
                    }
                    }
                }


            minOccurs = (col.AllowDBNull) ? 0 : 1;


            // [....] 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 ) {
            switch (rule) {
                case AcceptRejectRule.Cascade:  return "Cascade";
                case AcceptRejectRule.None:     return "None";
                default:                        return null;
            }
        }

        internal static string TranslateRule( Rule rule ) {
            switch (rule) {
                case Rule.Cascade:  return "Cascade";
                case Rule.None:     return "None";
                case Rule.SetNull:  return "SetNull";
                case Rule.SetDefault: return "SetDefault";
                default: return null;
            }
        }

        internal void AppendChildWithoutRef(XmlElement node, string Namespace, XmlElement el, string refString) {
            XmlElement schNode = GetSchema(Namespace);
            if (FindTypeNode(schNode, el.GetAttribute(Keywords.NAME)) == null)
                schNode.AppendChild(el);
        }

        internal 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;
        }

        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 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 = "";

            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 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.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;
        }

        internal XmlElement HandleTable(DataTable table, XmlDocument dc, XmlElement schema, bool genNested) {
            XmlElement root = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
            bool fWriteOrdinals = false;
            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 (Common.ADP.IsEmpty(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(CultureInfo.InvariantCulture));
            }
            if (table.ShouldSerializeLocale()) {
                root.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, table.Locale.ToString());
            }

            AddXdoProperties(table, root, dc);

            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 (_typeName == null || _typeName.Length == 0) {
                    _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 (Common.ADP.IsEmpty(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) );
// Bug 108292: Since we always write complex type as annonymous type, DO NOT WRITE ITS NAME
//  DO NOT REVERT THIS CHANGE
            }

            XmlElement compositor = null;

            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, dc);
                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 = null;
                DataColumn[] fields;

                if (constraints[i] is UniqueConstraint) {

                    UniqueConstraint unique = (UniqueConstraint)constraints[i];

                    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 (!Common.ADP.IsEmpty(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 (!Common.ADP.IsEmpty(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 (!Common.ADP.IsEmpty(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>
        /// <returns>type.AssemblyQualifiedName or targeted to a different version</returns>
        /// <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 != this.targetConverter)
                {
                    result = this.targetConverter(type);
                }

                if (!String.IsNullOrEmpty(result))
                {
                    // SetAttribute doesn't fail with invalid data, but the final XmlDocument.Save will fail later
                    // with the ArugmentException when calling the actual XmlWriter.SetAttribute
                    root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, result);
                }
            }
            catch (Exception ex)
            {
                if (Common.ADP.IsCatchableExceptionType(ex))
                {
                    ExceptionBuilder.ThrowMultipleTargetConverter(ex);
                }

                throw;
            }

            if (String.IsNullOrEmpty(result))
            {
                ExceptionBuilder.ThrowMultipleTargetConverter(null);
            }
        }
    }



 internal sealed class NewDiffgramGen {
        internal XmlDocument    _doc;
        internal DataSet        _ds;
        internal DataTable      _dt;
        internal XmlWriter     _xmlw;
        private bool fBefore = false;
        private bool fErrors = false;
        internal Hashtable rowsOrder = null;
        ArrayList _tables = new ArrayList();
        bool _writeHierarchy = false;


        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) {
                this._writeHierarchy = true;
                CreateTableHierarchy(dt);
            }

            DoAssignments(_tables);
        }

        private void CreateTableHierarchy(DataTable dt) {
//            if (!dt.SerializeHierarchy)
//                return;
            foreach( DataRelation r in dt.ChildRelations ) {
                if (! _tables.Contains((DataTable)r.ChildTable)) {
                    _tables.Add((DataTable)r.ChildTable);
                    CreateTableHierarchy(r.ChildTable);
                }
            }
        }


        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;
        }

        internal void Save(XmlWriter xmlw) {
            Save (xmlw, null);
    }

        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, this._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();
        }

        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 (error == null || error.Length == 0) {
                        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();
            }
        }

        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;

            // read value if the TextOnly column (if any)
            object val = (table.XmlText == null ? DBNull.Value : row[table.XmlText, DataRowVersion.Original]);

                    //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
        }

        private void GenerateColumn(DataRow row, DataColumn col, DataRowVersion version) {
            string value = null;

            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 || !col.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 (col.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 guy 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.XMLNS_XSD, Keywords.XSDNS);                                    
                                }
                                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 {
        XmlWriter _xmlw;

        DataSet _ds = null;
        DataTable _dt=null;

        ArrayList _dTables = new ArrayList();
        DataTable [] topLevelTables;

        bool    fFromTable = false; // also means no hierarchy
        bool    isDiffgram = false;
        Hashtable rowsOrder = null;
        bool _writeHierarchy = false;



        internal XmlDataTreeWriter(DataSet ds) {
            _ds = ds;
            topLevelTables = ds.TopLevelTables();
            foreach(DataTable table in ds.Tables) {
                _dTables.Add(table);
            }
        }

        internal XmlDataTreeWriter(DataSet ds, DataTable dt) { // need to modify this also
            _ds = ds;
            _dt = dt;

            _dTables.Add(dt);
            topLevelTables = ds.TopLevelTables();
        }

        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) {
                this._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 (new DataTable[0]);
            DataTable[] 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((DataTable)r.ChildTable)) {
                    _dTables.Add((DataTable)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 (error == null || error.Length == 0) {
                          continue;
                      }
              return true;
            }

          return false;
        }

        // the following line writes the data part
        // for the new diffgram format

        internal void SaveDiffgramData(XmlWriter xw, Hashtable rowsOrder) {
            _xmlw = DataTextWriter.CreateWriter(xw);
            isDiffgram = true;
            this.rowsOrder = rowsOrder;

            int countTopTable = topLevelTables.Length;

            string prefix = (_ds!= null)?(( _ds.Namespace.Length == 0 )? "" : _ds.Prefix):(( _dt.Namespace.Length == 0 )? "" : _dt.Prefix);

            if (_ds == null || _ds.DataSetName == null || _ds.DataSetName.Length == 0)
                _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();

        }


        internal void Save(XmlWriter xw, bool writeSchema) {
            _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 (_ds.DataSetName == null || _ds.DataSetName.Length == 0)
                        _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, this._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 ArrayList GetNestedChildRelations(DataRow row) {
            ArrayList list = new ArrayList();

            foreach( DataRelation r in row.Table.ChildRelations ) {
                if (r.Nested)
                    list.Add(r);
            }

            return list;
        }


        internal void  XmlDataRowWriter(DataRow row, String encodedTableName) {

            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 || !col.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 (col.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 guy 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.XMLNS_XSD, Keywords.XSDNS);
                                }
                                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 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 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();
        }
    }
}
