context: // Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
  // Copyright 2014       - Spartaco Giubbolini, Felix Obermaier
  //
  // This file is part of SharpMap.
  // SharpMap is free software; you can redistribute it and/or modify
  // it under the terms of the GNU Lesser General Public License as published by
  // the Free Software Foundation; either version 2 of the License, or
  // (at your option) any later version.
  // 
  // SharpMap is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU Lesser General Public License for more details.
  
  // You should have received a copy of the GNU Lesser General Public License
  // along with SharpMap; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Data;
  using System.Diagnostics;
  using System.Globalization;
  using System.IO;
  using System.Linq;
  using System.Runtime.Serialization;
  using GeoAPI;
  using GeoAPI.Geometries;
  using NetTopologySuite.IO;
  
  namespace SharpMap.Data
  {
      /// <summary>
      /// Represents an in-memory cache of spatial data. The FeatureDataSet is an extension of System.Data.DataSet
      /// </summary>
      /// <remarks>Serialization is achieved using the approach described in http://support.microsoft.com/kb/829740/en-us
      /// </remarks>
      [Serializable] 
      public class FeatureDataSet : DataSet, ISerializable
      {
          /// <summary>
          /// Initializes a new instance of the FeatureDataSet class.
          /// </summary>
          public FeatureDataSet()
          {}
  
          /// <summary>
          /// Initializes a new instance of the FeatureDataSet class.
          /// </summary>
          /// <param name="info">serialization info</param>
          /// <param name="context">streaming context</param>
          protected FeatureDataSet(SerializationInfo info, StreamingContext context)
          {
              DataSetName = info.GetString("name");
              Namespace = info.GetString("ns");
              Prefix = info.GetString("prefix");
              CaseSensitive = info.GetBoolean("case");
              Locale = new CultureInfo(info.GetInt32("locale"));
              EnforceConstraints = info.GetBoolean("enforceCons");
              
              var tables = (DataTable[]) info.GetValue("tables", typeof (DataTable[]));
              base.Tables.AddRange(tables);
  
              var constraints = (ArrayList)info.GetValue("constraints", typeof(ArrayList));
              SetForeignKeyConstraints(constraints);
  
              var relations = (ArrayList)info.GetValue("relations", typeof(ArrayList));
              SetRelations(relations);
  
              var extendedProperties = (PropertyCollection)info.GetValue("extendedProperties", typeof (PropertyCollection));
              if (extendedProperties.Count > 0) // otherwise the next foreach throws exception... weird.
                  foreach (DictionaryEntry keyPair in extendedProperties)
                      ExtendedProperties.Add(keyPair.Key, keyPair.Value);        }
  
          /// <summary>
          /// Gets the collection of tables contained in the FeatureDataSet
          /// </summary>
          public new FeatureTableCollection Tables
          {
              get { return new FeatureTableCollection(base.Tables); }
          }
  
          /// <summary>
          /// Copies the structure of the FeatureDataSet, including all FeatureDataTable schemas, relations, and constraints. Does not copy any data. 
          /// </summary>
          /// <returns></returns>
          public new FeatureDataSet Clone()
          {
              var cln = ((FeatureDataSet) (base.Clone()));
              return cln;
          }
  
          //private void InitClass()
          //{
          //    Prefix = "";
          //    Namespace = "sm";
          //    Locale = new CultureInfo("en-US");
          //    CaseSensitive = false;
          //    EnforceConstraints = true;
          //}
  
          void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
          {
              info.AddValue("name", DataSetName);
              info.AddValue("ns", Namespace);
              info.AddValue("prefix", Prefix);
              info.AddValue("case", CaseSensitive);
              info.AddValue("locale", Locale.LCID);
              info.AddValue("enforceCons", EnforceConstraints);
              info.AddValue("tables", base.Tables.OfType<DataTable>().ToArray());
              info.AddValue("constraints", GetForeignKeyConstraints());
              info.AddValue("relations", GetRelations());
              info.AddValue("extendedProperties", ExtendedProperties);
          }
  
          private ArrayList GetForeignKeyConstraints()
           {
              var constraintList = new ArrayList();
              var tables = base.Tables;
              for (int i = 0; i < tables.Count; i++)
              {
                  DataTable dt = tables[i];
                  for (int j = 0; j < dt.Constraints.Count; j++)
                  {
                      Constraint c = dt.Constraints[j];
                      var fk = c as ForeignKeyConstraint;
                      if (fk != null)
                      {
                          string constraintName = c.ConstraintName;
                          var parentInfo = new int[fk.RelatedColumns.Length + 1];
                          parentInfo[0] = tables.IndexOf(fk.RelatedTable);
                          for (int k = 1; k < parentInfo.Length; k++)
                          {
                              parentInfo[k] = fk.RelatedColumns[k - 1].Ordinal;
                          }
  
                          int[] childInfo = new int[fk.Columns.Length + 1];
                          childInfo[0] = i;//Since the constraint is on the current table, this is the child table.
                          for (int k = 1; k < childInfo.Length; k++)
                          {
                              childInfo[k] = fk.Columns[k - 1].Ordinal;
                          }
  
                          var list = new ArrayList
                          {
                              constraintName,
                              parentInfo,
                              childInfo,
                              new[] {(int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule}
                          };
                          var extendedProperties = new Hashtable();
                          if (fk.ExtendedProperties.Keys.Count > 0)
                          {
                              foreach (object propertyKey in fk.ExtendedProperties.Keys)
                              {
                                  extendedProperties.Add(propertyKey, fk.ExtendedProperties[propertyKey]);
                              }
                          }
                          list.Add(extendedProperties);
  
                          constraintList.Add(list);
                      }
                  }
              }
              return constraintList;
           }
  
          private ArrayList GetRelations()
          {
              var relationList = new ArrayList();
  
              var tables = base.Tables;
              foreach (DataRelation rel in Relations)
              {
                  string relationName = rel.RelationName;
                  var parentInfo = new int[rel.ParentColumns.Length + 1];
                  parentInfo[0] = tables.IndexOf(rel.ParentTable);
                  for (int j = 1; j < parentInfo.Length; j++)
                  {
                      parentInfo[j] = rel.ParentColumns[j - 1].Ordinal;
                  }
  
                  var childInfo = new int[rel.ChildColumns.Length + 1];
                  childInfo[0] = tables.IndexOf(rel.ChildTable);
                  for (int j = 1; j < childInfo.Length; j++)
                  {
                      childInfo[j] = rel.ChildColumns[j - 1].Ordinal;
                  }
  
                  var list = new ArrayList {relationName, parentInfo, childInfo, rel.Nested};
                  var extendedProperties = new Hashtable();
                  if (rel.ExtendedProperties.Keys.Count > 0)
                  {
                      foreach (object propertyKey in rel.ExtendedProperties.Keys)
                      {
                          extendedProperties.Add(propertyKey, rel.ExtendedProperties[propertyKey]);
                      }
                  }
                  list.Add(extendedProperties);
  
                  relationList.Add(list);
              }
              return relationList;
          }
  
          private void SetForeignKeyConstraints(ArrayList constraintList)
          {
              Debug.Assert(constraintList != null);
  
              var tables = base.Tables;
  
              foreach (ArrayList list in constraintList)
              {
                  Debug.Assert(list.Count == 5);
                  string constraintName = (string)list[0];
                  int[] parentInfo = (int[])list[1];
                  int[] childInfo = (int[])list[2];
                  int[] rules = (int[])list[3];
                  Hashtable extendedProperties = (Hashtable)list[4];
  
                  //ParentKey Columns.
                  Debug.Assert(parentInfo.Length >= 1);
                  DataColumn[] parentkeyColumns = new DataColumn[parentInfo.Length - 1];
                  for (int i = 0; i < parentkeyColumns.Length; i++)
                  {
                      Debug.Assert(tables.Count > parentInfo[0]);
                      Debug.Assert(tables[parentInfo[0]].Columns.Count > parentInfo[i + 1]);
                      parentkeyColumns[i] = tables[parentInfo[0]].Columns[parentInfo[i + 1]];
                  }
  
                  //ChildKey Columns.
                  Debug.Assert(childInfo.Length >= 1);
                  DataColumn[] childkeyColumns = new DataColumn[childInfo.Length - 1];
                  for (int i = 0; i < childkeyColumns.Length; i++)
                  {
                      Debug.Assert(tables.Count > childInfo[0]);
                      Debug.Assert(tables[childInfo[0]].Columns.Count > childInfo[i + 1]);
                      childkeyColumns[i] = tables[childInfo[0]].Columns[childInfo[i + 1]];
                  }
  
                  //Create the Constraint.
                  ForeignKeyConstraint fk = new ForeignKeyConstraint(constraintName, parentkeyColumns, childkeyColumns);
                  Debug.Assert(rules.Length == 3);
                  fk.AcceptRejectRule = (AcceptRejectRule)rules[0];
                  fk.UpdateRule = (Rule)rules[1];
                  fk.DeleteRule = (Rule)rules[2];
  
                  //Extended Properties.
                  Debug.Assert(extendedProperties != null);
                  if (extendedProperties.Keys.Count > 0)
                  {
                      foreach (object propertyKey in extendedProperties.Keys)
                      {
                          fk.ExtendedProperties.Add(propertyKey, extendedProperties[propertyKey]);
                      }
                  }
  
                  //Add the constraint to the child datatable.
                  Debug.Assert(tables.Count > childInfo[0]);
                  tables[childInfo[0]].Constraints.Add(fk);
              }
          }
  
          private void SetRelations(ArrayList relationList)
          {
              Debug.Assert(relationList != null);
  
              var tables = base.Tables;
              foreach (ArrayList list in relationList)
              {
                  Debug.Assert(list.Count == 5);
                  var relationName = (string)list[0];
                  var parentInfo = (int[])list[1];
                  var childInfo = (int[])list[2];
                  var isNested = (bool)list[3];
                  var extendedProperties = (Hashtable)list[4];
  
                  //ParentKey Columns.
                  Debug.Assert(parentInfo.Length >= 1);
                  var parentkeyColumns = new DataColumn[parentInfo.Length - 1];
                  for (int i = 0; i < parentkeyColumns.Length; i++)
                  {
                      Debug.Assert(tables.Count > parentInfo[0]);
                      Debug.Assert(tables[parentInfo[0]].Columns.Count > parentInfo[i + 1]);
                      parentkeyColumns[i] = tables[parentInfo[0]].Columns[parentInfo[i + 1]];
                  }
  
                  //ChildKey Columns.
                  Debug.Assert(childInfo.Length >= 1);
                  var childkeyColumns = new DataColumn[childInfo.Length - 1];
  
gt:                 for (int i = 0; i < childkeyColumns.Length; i++)