Index: src/Common/SharpMap/Data/Providers/DbaseReader.cs =================================================================== diff -u -r0153333b695581ca406c286a27467bc30d7aa5f8 -r5fc71a385897af92ccb092f2f969b5709afab85a --- src/Common/SharpMap/Data/Providers/DbaseReader.cs (.../DbaseReader.cs) (revision 0153333b695581ca406c286a27467bc30d7aa5f8) +++ src/Common/SharpMap/Data/Providers/DbaseReader.cs (.../DbaseReader.cs) (revision 5fc71a385897af92ccb092f2f969b5709afab85a) @@ -20,9 +20,12 @@ using System; using System.Data; +using System.Globalization; using System.IO; +using System.Text; using GeoAPI.Extensions.Feature; using NetTopologySuite.Extensions.Features; +using SharpMap.Utilities.Indexing; namespace SharpMap.Data.Providers { @@ -42,125 +45,86 @@ // TODO: speed-up this (memory-mapped file) - AttributeReader.StartReading((uint)Oid); + AttributeReader.StartReading((uint) Oid); for (int i = 0; i < AttributeReader.DbaseColumns.Length; i++) { DbaseReader.DbaseField column = AttributeReader.DbaseColumns[i]; attributes[column.ColumnName] = AttributeReader.ReadDbfValue(column); - } + } } return attributes; } - set { attributes = value; } + set + { + attributes = value; + } } public int Oid { get; set; } - protected bool Equals(ShapeFileFeature other) - { - return Geometry.EqualsExact(other.Geometry); - } - public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } return Equals((ShapeFileFeature) obj); } public override int GetHashCode() { int result = 17; - result = 37 * result + Geometry.GetHashCode(); + result = 37*result + Geometry.GetHashCode(); return result; } + + protected bool Equals(ShapeFileFeature other) + { + return Geometry.EqualsExact(other.Geometry); + } } - internal class DbaseReader : IDisposable - { - internal struct DbaseField - { - public string ColumnName; - public Type DataType; - public int Address; - public int Length; - public int Decimals; - - } - - private DateTime _lastUpdate; - private int _NumberOfRecords; - private Int16 _HeaderLength; - private Int16 _RecordLength; - private string _filename; - internal DbaseField[] DbaseColumns; - private FileStream fs; - private BinaryReader br; - private bool HeaderIsParsed; - - public DbaseReader(string filename) - { - - if (!File.Exists(filename)) - throw new FileNotFoundException(String.Format("Could not find file \"{0}\"", filename)); - _filename = filename; - HeaderIsParsed = false; - } + internal class DbaseReader : IDisposable + { + internal DbaseField[] DbaseColumns; - private bool _isOpen; + private int _NumberOfRecords; + private Int16 _HeaderLength; + private Int16 _RecordLength; + private readonly string _filename; + private FileStream fs; + private BinaryReader br; + private bool HeaderIsParsed; - public bool IsOpen - { - get { return _isOpen; } - set { _isOpen = value; } - } - - public void Open() - { - fs = new FileStream(_filename, FileMode.Open, FileAccess.Read); - br = new BinaryReader(fs); - _isOpen = true; - if (!HeaderIsParsed) //Don't read the header if it's already parsed - ParseDbfHeader(_filename); - } + private FeatureDataTable baseTable; - public void Close() - { - br.Close(); - fs.Close(); - _isOpen = false; - } + private Encoding _FileEncoding; - public void Dispose() - { - if(_isOpen) - this.Close(); - br = null; - fs = null; - } - // Binary Tree not working yet on Mono - // see bug: http://bugzilla.ximian.com/show_bug.cgi?id=78502 - #if !MONO + public DbaseReader(string filename) + { + if (!File.Exists(filename)) + { + throw new FileNotFoundException(String.Format("Could not find file \"{0}\"", filename)); + } + _filename = filename; + HeaderIsParsed = false; + } + + public bool IsOpen { get; set; } + + /* /// - /// Indexes a DBF column in a binary tree [NOT COMPLETE] - /// - /// datatype to be indexed - /// Column to index - /// - public SharpMap.Utilities.Indexing.BinaryTree CreateDbfIndex(int ColumnId) where T:IComparable - { - SharpMap.Utilities.Indexing.BinaryTree tree = new SharpMap.Utilities.Indexing.BinaryTree(); - for (uint i = 0; i < ((this._NumberOfRecords>10000)?10000:this._NumberOfRecords); i++) - tree.Add(new SharpMap.Utilities.Indexing.BinaryTree.ItemValue((T)GetValue(i, ColumnId), i)); - return tree; - } - #endif - /* - /// /// Creates an index on the columns for faster searching [EXPERIMENTAL - Requires Lucene dependencies] /// /// @@ -195,355 +159,279 @@ return this._filename + ".idx"; } */ - /// - /// Gets the date this file was last updated. - /// - public DateTime LastUpdate - { - get { return _lastUpdate; } - } - - private void ParseDbfHeader(string filename) - { - if (br.ReadByte() != 0x03) - throw new NotSupportedException("Unsupported DBF Type"); - - _lastUpdate = new DateTime((int)br.ReadByte() + 1900, (int)br.ReadByte(), (int)br.ReadByte()); //Read the last update date - _NumberOfRecords = br.ReadInt32(); // read number of records. - _HeaderLength = br.ReadInt16(); // read length of header structure. - _RecordLength = br.ReadInt16(); // read length of a record - fs.Seek(29, SeekOrigin.Begin); //Seek to encoding flag - _FileEncoding = GetDbaseLanguageDriver(br.ReadByte()); //Read and parse Language driver - fs.Seek(32, SeekOrigin.Begin); //Move past the reserved bytes - int NumberOfColumns = (_HeaderLength - 31) / 32; // calculate the number of DataColumns in the header - DbaseColumns = new DbaseField[NumberOfColumns]; - for (int i = 0; i < DbaseColumns.Length;i++) - { - DbaseColumns[i] = new DbaseField(); - DbaseColumns[i].ColumnName = System.Text.Encoding.UTF7.GetString((br.ReadBytes(11))).Replace("\0", "").Trim(); - char fieldtype = br.ReadChar(); - switch (fieldtype) - { - case 'L': - DbaseColumns[i].DataType = typeof(bool); - break; - case 'C': - DbaseColumns[i].DataType = typeof(string); - break; - case 'D': - DbaseColumns[i].DataType = typeof(DateTime); - break; - case 'N': - DbaseColumns[i].DataType = typeof(double); - break; - case 'F': - DbaseColumns[i].DataType = typeof(float); - break; - case 'B': - DbaseColumns[i].DataType = typeof(byte[]); - break; - default: - throw (new NotSupportedException("Invalid or unknown DBase field type '" + fieldtype + - "' in column '" + DbaseColumns[i].ColumnName + "'")); - } - DbaseColumns[i].Address = br.ReadInt32(); + /// + /// Gets the date this file was last updated. + /// + public DateTime LastUpdate { get; private set; } - int Length = (int)br.ReadByte(); - if (Length < 0) Length = Length + 256; - DbaseColumns[i].Length = Length; - DbaseColumns[i].Decimals = (int)br.ReadByte(); - //If the double-type doesn't have any decimals, make the type an integer - if (DbaseColumns[i].Decimals == 0 && DbaseColumns[i].DataType == typeof(double)) - if (DbaseColumns[i].Length <= 2) - DbaseColumns[i].DataType = typeof(Int16); - else if(DbaseColumns[i].Length<=4) - DbaseColumns[i].DataType = typeof(Int32); - else - DbaseColumns[i].DataType = typeof(Int64); - fs.Seek(fs.Position + 14, 0); - } - HeaderIsParsed = true; - CreateBaseTable(); - } + /// + /// Gets or sets the used for parsing strings from the DBase DBF file. + /// + /// + /// If the encoding type isn't set, the dbase driver will try to determine the correct . + /// + public Encoding Encoding { get; set; } - private System.Text.Encoding GetDbaseLanguageDriver(byte dbasecode) - { - switch (dbasecode) - { - case 0x01: return System.Text.Encoding.GetEncoding(437); //DOS USA code page 437 - case 0x02: return System.Text.Encoding.GetEncoding(850); // DOS Multilingual code page 850 - case 0x03: return System.Text.Encoding.GetEncoding(1252); // Windows ANSI code page 1252 - case 0x04: return System.Text.Encoding.GetEncoding(10000); // Standard Macintosh - case 0x08: return System.Text.Encoding.GetEncoding(865); // Danish OEM - case 0x09: return System.Text.Encoding.GetEncoding(437); // Dutch OEM - case 0x0A: return System.Text.Encoding.GetEncoding(850); // Dutch OEM Secondary codepage - case 0x0B: return System.Text.Encoding.GetEncoding(437); // Finnish OEM - case 0x0D: return System.Text.Encoding.GetEncoding(437); // French OEM - case 0x0E: return System.Text.Encoding.GetEncoding(850); // French OEM Secondary codepage - case 0x0F: return System.Text.Encoding.GetEncoding(437); // German OEM - case 0x10: return System.Text.Encoding.GetEncoding(850); // German OEM Secondary codepage - case 0x11: return System.Text.Encoding.GetEncoding(437); // Italian OEM - case 0x12: return System.Text.Encoding.GetEncoding(850); // Italian OEM Secondary codepage - case 0x13: return System.Text.Encoding.GetEncoding(932); // Japanese Shift-JIS - case 0x14: return System.Text.Encoding.GetEncoding(850); // Spanish OEM secondary codepage - case 0x15: return System.Text.Encoding.GetEncoding(437); // Swedish OEM - case 0x16: return System.Text.Encoding.GetEncoding(850); // Swedish OEM secondary codepage - case 0x17: return System.Text.Encoding.GetEncoding(865); // Norwegian OEM - case 0x18: return System.Text.Encoding.GetEncoding(437); // Spanish OEM - case 0x19: return System.Text.Encoding.GetEncoding(437); // English OEM (Britain) - case 0x1A: return System.Text.Encoding.GetEncoding(850); // English OEM (Britain) secondary codepage - case 0x1B: return System.Text.Encoding.GetEncoding(437); // English OEM (U.S.) - case 0x1C: return System.Text.Encoding.GetEncoding(863); // French OEM (Canada) - case 0x1D: return System.Text.Encoding.GetEncoding(850); // French OEM secondary codepage - case 0x1F: return System.Text.Encoding.GetEncoding(852); // Czech OEM - case 0x22: return System.Text.Encoding.GetEncoding(852); // Hungarian OEM - case 0x23: return System.Text.Encoding.GetEncoding(852); // Polish OEM - case 0x24: return System.Text.Encoding.GetEncoding(860); // Portuguese OEM - case 0x25: return System.Text.Encoding.GetEncoding(850); // Portuguese OEM secondary codepage - case 0x26: return System.Text.Encoding.GetEncoding(866); // Russian OEM - case 0x37: return System.Text.Encoding.GetEncoding(850); // English OEM (U.S.) secondary codepage - case 0x40: return System.Text.Encoding.GetEncoding(852); // Romanian OEM - case 0x4D: return System.Text.Encoding.GetEncoding(936); // Chinese GBK (PRC) - case 0x4E: return System.Text.Encoding.GetEncoding(949); // Korean (ANSI/OEM) - case 0x4F: return System.Text.Encoding.GetEncoding(950); // Chinese Big5 (Taiwan) - case 0x50: return System.Text.Encoding.GetEncoding(874); // Thai (ANSI/OEM) - case 0x57: return System.Text.Encoding.GetEncoding(1252); // ANSI - case 0x58: return System.Text.Encoding.GetEncoding(1252); // Western European ANSI - case 0x59: return System.Text.Encoding.GetEncoding(1252); // Spanish ANSI - case 0x64: return System.Text.Encoding.GetEncoding(852); // Eastern European MS�DOS - case 0x65: return System.Text.Encoding.GetEncoding(866); // Russian MS�DOS - case 0x66: return System.Text.Encoding.GetEncoding(865); // Nordic MS�DOS - case 0x67: return System.Text.Encoding.GetEncoding(861); // Icelandic MS�DOS - case 0x68: return System.Text.Encoding.GetEncoding(895); // Kamenicky (Czech) MS-DOS - case 0x69: return System.Text.Encoding.GetEncoding(620); // Mazovia (Polish) MS-DOS - case 0x6A: return System.Text.Encoding.GetEncoding(737); // Greek MS�DOS (437G) - case 0x6B: return System.Text.Encoding.GetEncoding(857); // Turkish MS�DOS - case 0x6C: return System.Text.Encoding.GetEncoding(863); // French�Canadian MS�DOS - case 0x78: return System.Text.Encoding.GetEncoding(950); // Taiwan Big 5 - case 0x79: return System.Text.Encoding.GetEncoding(949); // Hangul (Wansung) - case 0x7A: return System.Text.Encoding.GetEncoding(936); // PRC GBK - case 0x7B: return System.Text.Encoding.GetEncoding(932); // Japanese Shift-JIS - case 0x7C: return System.Text.Encoding.GetEncoding(874); // Thai Windows/MS�DOS - case 0x7D: return System.Text.Encoding.GetEncoding(1255); // Hebrew Windows - case 0x7E: return System.Text.Encoding.GetEncoding(1256); // Arabic Windows - case 0x86: return System.Text.Encoding.GetEncoding(737); // Greek OEM - case 0x87: return System.Text.Encoding.GetEncoding(852); // Slovenian OEM - case 0x88: return System.Text.Encoding.GetEncoding(857); // Turkish OEM - case 0x96: return System.Text.Encoding.GetEncoding(10007); // Russian Macintosh - case 0x97: return System.Text.Encoding.GetEncoding(10029); // Eastern European Macintosh - case 0x98: return System.Text.Encoding.GetEncoding(10006); // Greek Macintosh - case 0xC8: return System.Text.Encoding.GetEncoding(1250); // Eastern European Windows - case 0xC9: return System.Text.Encoding.GetEncoding(1251); // Russian Windows - case 0xCA: return System.Text.Encoding.GetEncoding(1254); // Turkish Windows - case 0xCB: return System.Text.Encoding.GetEncoding(1253); // Greek Windows - case 0xCC: return System.Text.Encoding.GetEncoding(1257); // Baltic Windows - default: - return System.Text.Encoding.UTF7; - } + public void Open() + { + fs = new FileStream(_filename, FileMode.Open, FileAccess.Read); + br = new BinaryReader(fs); + IsOpen = true; + if (!HeaderIsParsed) //Don't read the header if it's already parsed + { + ParseDbfHeader(_filename); + } + } - } + public void Close() + { + br.Close(); + fs.Close(); + IsOpen = false; + } - /// - /// Returns a DataTable that describes the column metadata of the DBase file. - /// - /// A DataTable that describes the column metadata. - public DataTable GetSchemaTable() - { - DataTable tab = new DataTable(); - // all of common, non "base-table" fields implemented - tab.Columns.Add("ColumnName", typeof(System.String)); - tab.Columns.Add("ColumnSize", typeof(Int32)); - tab.Columns.Add("ColumnOrdinal", typeof(Int32)); - tab.Columns.Add("NumericPrecision", typeof(Int16)); - tab.Columns.Add("NumericScale", typeof(Int16)); - tab.Columns.Add("DataType", typeof(System.Type)); - tab.Columns.Add("AllowDBNull", typeof(bool)); - tab.Columns.Add("IsReadOnly", typeof(bool)); - tab.Columns.Add("IsUnique", typeof(bool)); - tab.Columns.Add("IsRowVersion", typeof(bool)); - tab.Columns.Add("IsKey", typeof(bool)); - tab.Columns.Add("IsAutoIncrement", typeof(bool)); - tab.Columns.Add("IsLong", typeof(bool)); + // Binary Tree not working yet on Mono + // see bug: http://bugzilla.ximian.com/show_bug.cgi?id=78502 +#if !MONO + /// + /// Indexes a DBF column in a binary tree [NOT COMPLETE] + /// + /// datatype to be indexed + /// Column to index + /// + public BinaryTree CreateDbfIndex(int ColumnId) where T : IComparable + { + BinaryTree tree = new BinaryTree(); + for (uint i = 0; i < ((_NumberOfRecords > 10000) ? 10000 : _NumberOfRecords); i++) + { + tree.Add(new BinaryTree.ItemValue((T) GetValue(i, ColumnId), i)); + } + return tree; + } +#endif - foreach (DbaseField dbf in DbaseColumns) - tab.Columns.Add(dbf.ColumnName, dbf.DataType); + /// + /// Returns a DataTable that describes the column metadata of the DBase file. + /// + /// A DataTable that describes the column metadata. + public DataTable GetSchemaTable() + { + DataTable tab = new DataTable(); + // all of common, non "base-table" fields implemented + tab.Columns.Add("ColumnName", typeof(String)); + tab.Columns.Add("ColumnSize", typeof(Int32)); + tab.Columns.Add("ColumnOrdinal", typeof(Int32)); + tab.Columns.Add("NumericPrecision", typeof(Int16)); + tab.Columns.Add("NumericScale", typeof(Int16)); + tab.Columns.Add("DataType", typeof(Type)); + tab.Columns.Add("AllowDBNull", typeof(bool)); + tab.Columns.Add("IsReadOnly", typeof(bool)); + tab.Columns.Add("IsUnique", typeof(bool)); + tab.Columns.Add("IsRowVersion", typeof(bool)); + tab.Columns.Add("IsKey", typeof(bool)); + tab.Columns.Add("IsAutoIncrement", typeof(bool)); + tab.Columns.Add("IsLong", typeof(bool)); - for (int i = 0; i < DbaseColumns.Length; i++) - { - DataRow r = tab.NewRow(); - r["ColumnName"] = DbaseColumns[i].ColumnName; - r["ColumnSize"] = DbaseColumns[i].Length; - r["ColumnOrdinal"] = i; - r["NumericPrecision"] = DbaseColumns[i].Decimals; - r["NumericScale"] = 0; - r["DataType"] = DbaseColumns[i].DataType; - r["AllowDBNull"] = true; - r["IsReadOnly"] = true; - r["IsUnique"] = false; - r["IsRowVersion"] = false; - r["IsKey"] = false; - r["IsAutoIncrement"] = false; - r["IsLong"] = false; + foreach (DbaseField dbf in DbaseColumns) + { + tab.Columns.Add(dbf.ColumnName, dbf.DataType); + } - // specializations, if ID is unique - //if (_ColumnNames[i] == "ID") - // r["IsUnique"] = true; + for (int i = 0; i < DbaseColumns.Length; i++) + { + DataRow r = tab.NewRow(); + r["ColumnName"] = DbaseColumns[i].ColumnName; + r["ColumnSize"] = DbaseColumns[i].Length; + r["ColumnOrdinal"] = i; + r["NumericPrecision"] = DbaseColumns[i].Decimals; + r["NumericScale"] = 0; + r["DataType"] = DbaseColumns[i].DataType; + r["AllowDBNull"] = true; + r["IsReadOnly"] = true; + r["IsUnique"] = false; + r["IsRowVersion"] = false; + r["IsKey"] = false; + r["IsAutoIncrement"] = false; + r["IsLong"] = false; - tab.Rows.Add(r); - } - - return tab; - } + // specializations, if ID is unique + //if (_ColumnNames[i] == "ID") + // r["IsUnique"] = true; + tab.Rows.Add(r); + } - private SharpMap.Data.FeatureDataTable baseTable; + return tab; + } + public void StartReading(uint oid) + { + if (!IsOpen) + { + Open(); + } - private void CreateBaseTable() - { - baseTable = new SharpMap.Data.FeatureDataTable(); + fs.Seek(_HeaderLength + oid*_RecordLength, 0); - foreach (DbaseField dbf in DbaseColumns) - { - baseTable.Columns.Add(dbf.ColumnName, dbf.DataType); - } + if (br.ReadChar() == '*') //is record marked deleted? + { + // TODO: handle deleted features correctly + } + } - SetPrimaryKey(baseTable); - } + public void Dispose() + { + if (IsOpen) + { + Close(); + } + br = null; + fs = null; + } - private void SetPrimaryKey(FeatureDataTable table) - { - const string primaryKeyColumnName = "OBJECTID"; - if (baseTable.Columns.Contains(primaryKeyColumnName)) - { - baseTable.PrimaryKey = new[] { baseTable.Columns[primaryKeyColumnName] }; - } - else + internal FeatureDataTable NewTable + { + get { - var idColumn = new DataColumn("DSHELL_ADDED_" + primaryKeyColumnName, typeof(int)) - { - AutoIncrement = true - }; + return baseTable.Clone(); + } + } - baseTable.Columns.Add(idColumn); - baseTable.PrimaryKey = new[] { idColumn }; + internal object GetValue(uint oid, int colid) + { + if (!IsOpen) + { + throw (new ApplicationException("An attempt was made to read from a closed DBF file")); } - } + if (oid >= _NumberOfRecords) + { + throw (new ArgumentException("Invalid DataRow requested at index " + oid.ToString())); + } + if (colid >= DbaseColumns.Length || colid < 0) + { + throw ((new ArgumentException("Column index out of range"))); + } - internal SharpMap.Data.FeatureDataTable NewTable - { - get { return baseTable.Clone(); } - } + fs.Seek(_HeaderLength + oid*_RecordLength, 0); + for (int i = 0; i < colid; i++) + { + br.BaseStream.Seek(DbaseColumns[i].Length, SeekOrigin.Current); + } - internal object GetValue(uint oid, int colid) - { - if (!_isOpen) - throw (new ApplicationException("An attempt was made to read from a closed DBF file")); - if (oid >= _NumberOfRecords) - throw (new ArgumentException("Invalid DataRow requested at index " + oid.ToString())); - if (colid >= DbaseColumns.Length || colid < 0) - throw ((new ArgumentException("Column index out of range"))); + return ReadDbfValue(DbaseColumns[colid]); + } - fs.Seek(_HeaderLength + oid * _RecordLength, 0); - for (int i = 0; i < colid; i++) - br.BaseStream.Seek(DbaseColumns[i].Length,SeekOrigin.Current); + /// + /// Gets the feature at the specified Object ID + /// + /// + /// + internal IFeature GetFeature(int oid) + { + if (!IsOpen) + { + Open(); + } - return ReadDbfValue(DbaseColumns[colid]); - } - - private System.Text.Encoding _Encoding; - private System.Text.Encoding _FileEncoding; - - /// - /// Gets or sets the used for parsing strings from the DBase DBF file. - /// - /// - /// If the encoding type isn't set, the dbase driver will try to determine the correct . - /// - public System.Text.Encoding Encoding - { - get { return _Encoding; } - set { _Encoding = value; } - } - - /// - /// Gets the feature at the specified Object ID - /// - /// - /// - internal IFeature GetFeature(int oid) - { - if (!_isOpen) - { - Open(); - } - if (oid >= _NumberOfRecords) - throw (new ArgumentException("Invalid DataRow requested at index " + oid.ToString())); + { + throw (new ArgumentException("Invalid DataRow requested at index " + oid.ToString())); + } - var feature = new ShapeFileFeature { AttributeReader = this, Oid = oid }; + var feature = new ShapeFileFeature + { + AttributeReader = this, Oid = oid + }; return feature; - } + } - internal object ReadDbfValue(DbaseField dbf) - { - switch (dbf.DataType.ToString()) - { - case "System.String": - if(_Encoding==null) - return _FileEncoding.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); - else - return _Encoding.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); - case "System.Double": - string temp = System.Text.Encoding.UTF7.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); - double dbl = 0; - if(double.TryParse(temp, System.Globalization.NumberStyles.Float, SharpMap.Map.numberFormat_EnUS, out dbl)) - return dbl; - else - return DBNull.Value; - case "System.Int16": - string temp16 = System.Text.Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); - Int16 i16 = 0; - if (Int16.TryParse(temp16, System.Globalization.NumberStyles.Float, SharpMap.Map.numberFormat_EnUS, out i16)) - return i16; - else - return DBNull.Value; - case "System.Int32": - string temp32 = System.Text.Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); - Int32 i32 = 0; - if (Int32.TryParse(temp32, System.Globalization.NumberStyles.Float, SharpMap.Map.numberFormat_EnUS, out i32)) - return i32; - else - return DBNull.Value; - case "System.Int64": - string temp64 = System.Text.Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); - Int64 i64 = 0; - if (Int64.TryParse(temp64, System.Globalization.NumberStyles.Float, SharpMap.Map.numberFormat_EnUS, out i64)) - return i64; - else - return DBNull.Value; - case "System.Single": - string temp4 = System.Text.Encoding.UTF8.GetString((br.ReadBytes(dbf.Length))); - float f = 0; - if (float.TryParse(temp4, System.Globalization.NumberStyles.Float, SharpMap.Map.numberFormat_EnUS, out f)) - return f; - else - return DBNull.Value; - case "System.Boolean": - char tempChar = br.ReadChar(); - return ((tempChar == 'T') || (tempChar == 't') || (tempChar == 'Y') || (tempChar == 'y')); - case "System.DateTime": - DateTime date; - // Mono has not yet implemented DateTime.TryParseExact - #if !MONO - if (DateTime.TryParseExact(System.Text.Encoding.UTF7.GetString((br.ReadBytes(8))), - "yyyyMMdd", SharpMap.Map.numberFormat_EnUS, System.Globalization.DateTimeStyles.None, out date)) - return date; - else - return DBNull.Value; - #else + internal object ReadDbfValue(DbaseField dbf) + { + switch (dbf.DataType.ToString()) + { + case "System.String": + if (Encoding == null) + { + return _FileEncoding.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); + } + else + { + return Encoding.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); + } + case "System.Double": + string temp = Encoding.UTF7.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); + double dbl = 0; + if (double.TryParse(temp, NumberStyles.Float, Map.numberFormat_EnUS, out dbl)) + { + return dbl; + } + else + { + return DBNull.Value; + } + case "System.Int16": + string temp16 = Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); + Int16 i16 = 0; + if (Int16.TryParse(temp16, NumberStyles.Float, Map.numberFormat_EnUS, out i16)) + { + return i16; + } + else + { + return DBNull.Value; + } + case "System.Int32": + string temp32 = Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); + Int32 i32 = 0; + if (Int32.TryParse(temp32, NumberStyles.Float, Map.numberFormat_EnUS, out i32)) + { + return i32; + } + else + { + return DBNull.Value; + } + case "System.Int64": + string temp64 = Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); + Int64 i64 = 0; + if (Int64.TryParse(temp64, NumberStyles.Float, Map.numberFormat_EnUS, out i64)) + { + return i64; + } + else + { + return DBNull.Value; + } + case "System.Single": + string temp4 = Encoding.UTF8.GetString((br.ReadBytes(dbf.Length))); + float f = 0; + if (float.TryParse(temp4, NumberStyles.Float, Map.numberFormat_EnUS, out f)) + { + return f; + } + else + { + return DBNull.Value; + } + case "System.Boolean": + char tempChar = br.ReadChar(); + return ((tempChar == 'T') || (tempChar == 't') || (tempChar == 'Y') || (tempChar == 'y')); + case "System.DateTime": + DateTime date; + // Mono has not yet implemented DateTime.TryParseExact +#if !MONO + if (DateTime.TryParseExact(Encoding.UTF7.GetString((br.ReadBytes(8))), + "yyyyMMdd", Map.numberFormat_EnUS, DateTimeStyles.None, out date)) + { + return date; + } + else + { + return DBNull.Value; + } +#else try { return date = DateTime.ParseExact ( System.Text.Encoding.UTF7.GetString((br.ReadBytes(8))), @@ -554,24 +442,275 @@ return DBNull.Value; } #endif - default: - throw (new NotSupportedException("Cannot parse DBase field '" + dbf.ColumnName + "' of type '" + dbf.DataType.ToString() + "'")); - } - } + default: + throw (new NotSupportedException("Cannot parse DBase field '" + dbf.ColumnName + "' of type '" + dbf.DataType.ToString() + "'")); + } + } - public void StartReading(uint oid) - { - if (!IsOpen) + private void ParseDbfHeader(string filename) + { + if (br.ReadByte() != 0x03) { - Open(); + throw new NotSupportedException("Unsupported DBF Type"); } - fs.Seek(_HeaderLength + oid * _RecordLength, 0); + LastUpdate = new DateTime((int) br.ReadByte() + 1900, (int) br.ReadByte(), (int) br.ReadByte()); //Read the last update date + _NumberOfRecords = br.ReadInt32(); // read number of records. + _HeaderLength = br.ReadInt16(); // read length of header structure. + _RecordLength = br.ReadInt16(); // read length of a record + fs.Seek(29, SeekOrigin.Begin); //Seek to encoding flag + _FileEncoding = GetDbaseLanguageDriver(br.ReadByte()); //Read and parse Language driver + fs.Seek(32, SeekOrigin.Begin); //Move past the reserved bytes - if (br.ReadChar() == '*') //is record marked deleted? + int NumberOfColumns = (_HeaderLength - 31)/32; // calculate the number of DataColumns in the header + DbaseColumns = new DbaseField[NumberOfColumns]; + for (int i = 0; i < DbaseColumns.Length; i++) { - // TODO: handle deleted features correctly + DbaseColumns[i] = new DbaseField(); + DbaseColumns[i].ColumnName = Encoding.UTF7.GetString((br.ReadBytes(11))).Replace("\0", "").Trim(); + char fieldtype = br.ReadChar(); + switch (fieldtype) + { + case 'L': + DbaseColumns[i].DataType = typeof(bool); + break; + case 'C': + DbaseColumns[i].DataType = typeof(string); + break; + case 'D': + DbaseColumns[i].DataType = typeof(DateTime); + break; + case 'N': + DbaseColumns[i].DataType = typeof(double); + break; + case 'F': + DbaseColumns[i].DataType = typeof(float); + break; + case 'B': + DbaseColumns[i].DataType = typeof(byte[]); + break; + default: + throw (new NotSupportedException("Invalid or unknown DBase field type '" + fieldtype + + "' in column '" + DbaseColumns[i].ColumnName + "'")); + } + DbaseColumns[i].Address = br.ReadInt32(); + + int Length = (int) br.ReadByte(); + if (Length < 0) + { + Length = Length + 256; + } + DbaseColumns[i].Length = Length; + DbaseColumns[i].Decimals = (int) br.ReadByte(); + //If the double-type doesn't have any decimals, make the type an integer + if (DbaseColumns[i].Decimals == 0 && DbaseColumns[i].DataType == typeof(double)) + { + if (DbaseColumns[i].Length <= 2) + { + DbaseColumns[i].DataType = typeof(Int16); + } + else if (DbaseColumns[i].Length <= 4) + { + DbaseColumns[i].DataType = typeof(Int32); + } + else + { + DbaseColumns[i].DataType = typeof(Int64); + } + } + fs.Seek(fs.Position + 14, 0); } + HeaderIsParsed = true; + CreateBaseTable(); } - } -} + + private Encoding GetDbaseLanguageDriver(byte dbasecode) + { + switch (dbasecode) + { + case 0x01: + return Encoding.GetEncoding(437); //DOS USA code page 437 + case 0x02: + return Encoding.GetEncoding(850); // DOS Multilingual code page 850 + case 0x03: + return Encoding.GetEncoding(1252); // Windows ANSI code page 1252 + case 0x04: + return Encoding.GetEncoding(10000); // Standard Macintosh + case 0x08: + return Encoding.GetEncoding(865); // Danish OEM + case 0x09: + return Encoding.GetEncoding(437); // Dutch OEM + case 0x0A: + return Encoding.GetEncoding(850); // Dutch OEM Secondary codepage + case 0x0B: + return Encoding.GetEncoding(437); // Finnish OEM + case 0x0D: + return Encoding.GetEncoding(437); // French OEM + case 0x0E: + return Encoding.GetEncoding(850); // French OEM Secondary codepage + case 0x0F: + return Encoding.GetEncoding(437); // German OEM + case 0x10: + return Encoding.GetEncoding(850); // German OEM Secondary codepage + case 0x11: + return Encoding.GetEncoding(437); // Italian OEM + case 0x12: + return Encoding.GetEncoding(850); // Italian OEM Secondary codepage + case 0x13: + return Encoding.GetEncoding(932); // Japanese Shift-JIS + case 0x14: + return Encoding.GetEncoding(850); // Spanish OEM secondary codepage + case 0x15: + return Encoding.GetEncoding(437); // Swedish OEM + case 0x16: + return Encoding.GetEncoding(850); // Swedish OEM secondary codepage + case 0x17: + return Encoding.GetEncoding(865); // Norwegian OEM + case 0x18: + return Encoding.GetEncoding(437); // Spanish OEM + case 0x19: + return Encoding.GetEncoding(437); // English OEM (Britain) + case 0x1A: + return Encoding.GetEncoding(850); // English OEM (Britain) secondary codepage + case 0x1B: + return Encoding.GetEncoding(437); // English OEM (U.S.) + case 0x1C: + return Encoding.GetEncoding(863); // French OEM (Canada) + case 0x1D: + return Encoding.GetEncoding(850); // French OEM secondary codepage + case 0x1F: + return Encoding.GetEncoding(852); // Czech OEM + case 0x22: + return Encoding.GetEncoding(852); // Hungarian OEM + case 0x23: + return Encoding.GetEncoding(852); // Polish OEM + case 0x24: + return Encoding.GetEncoding(860); // Portuguese OEM + case 0x25: + return Encoding.GetEncoding(850); // Portuguese OEM secondary codepage + case 0x26: + return Encoding.GetEncoding(866); // Russian OEM + case 0x37: + return Encoding.GetEncoding(850); // English OEM (U.S.) secondary codepage + case 0x40: + return Encoding.GetEncoding(852); // Romanian OEM + case 0x4D: + return Encoding.GetEncoding(936); // Chinese GBK (PRC) + case 0x4E: + return Encoding.GetEncoding(949); // Korean (ANSI/OEM) + case 0x4F: + return Encoding.GetEncoding(950); // Chinese Big5 (Taiwan) + case 0x50: + return Encoding.GetEncoding(874); // Thai (ANSI/OEM) + case 0x57: + return Encoding.GetEncoding(1252); // ANSI + case 0x58: + return Encoding.GetEncoding(1252); // Western European ANSI + case 0x59: + return Encoding.GetEncoding(1252); // Spanish ANSI + case 0x64: + return Encoding.GetEncoding(852); // Eastern European MS�DOS + case 0x65: + return Encoding.GetEncoding(866); // Russian MS�DOS + case 0x66: + return Encoding.GetEncoding(865); // Nordic MS�DOS + case 0x67: + return Encoding.GetEncoding(861); // Icelandic MS�DOS + case 0x68: + return Encoding.GetEncoding(895); // Kamenicky (Czech) MS-DOS + case 0x69: + return Encoding.GetEncoding(620); // Mazovia (Polish) MS-DOS + case 0x6A: + return Encoding.GetEncoding(737); // Greek MS�DOS (437G) + case 0x6B: + return Encoding.GetEncoding(857); // Turkish MS�DOS + case 0x6C: + return Encoding.GetEncoding(863); // French�Canadian MS�DOS + case 0x78: + return Encoding.GetEncoding(950); // Taiwan Big 5 + case 0x79: + return Encoding.GetEncoding(949); // Hangul (Wansung) + case 0x7A: + return Encoding.GetEncoding(936); // PRC GBK + case 0x7B: + return Encoding.GetEncoding(932); // Japanese Shift-JIS + case 0x7C: + return Encoding.GetEncoding(874); // Thai Windows/MS�DOS + case 0x7D: + return Encoding.GetEncoding(1255); // Hebrew Windows + case 0x7E: + return Encoding.GetEncoding(1256); // Arabic Windows + case 0x86: + return Encoding.GetEncoding(737); // Greek OEM + case 0x87: + return Encoding.GetEncoding(852); // Slovenian OEM + case 0x88: + return Encoding.GetEncoding(857); // Turkish OEM + case 0x96: + return Encoding.GetEncoding(10007); // Russian Macintosh + case 0x97: + return Encoding.GetEncoding(10029); // Eastern European Macintosh + case 0x98: + return Encoding.GetEncoding(10006); // Greek Macintosh + case 0xC8: + return Encoding.GetEncoding(1250); // Eastern European Windows + case 0xC9: + return Encoding.GetEncoding(1251); // Russian Windows + case 0xCA: + return Encoding.GetEncoding(1254); // Turkish Windows + case 0xCB: + return Encoding.GetEncoding(1253); // Greek Windows + case 0xCC: + return Encoding.GetEncoding(1257); // Baltic Windows + default: + return Encoding.UTF7; + } + } + + private void CreateBaseTable() + { + baseTable = new FeatureDataTable(); + + foreach (DbaseField dbf in DbaseColumns) + { + baseTable.Columns.Add(dbf.ColumnName, dbf.DataType); + } + + SetPrimaryKey(baseTable); + } + + private void SetPrimaryKey(FeatureDataTable table) + { + const string primaryKeyColumnName = "OBJECTID"; + if (baseTable.Columns.Contains(primaryKeyColumnName)) + { + baseTable.PrimaryKey = new[] + { + baseTable.Columns[primaryKeyColumnName] + }; + } + else + { + var idColumn = new DataColumn("DSHELL_ADDED_" + primaryKeyColumnName, typeof(int)) + { + AutoIncrement = true + }; + + baseTable.Columns.Add(idColumn); + baseTable.PrimaryKey = new[] + { + idColumn + }; + } + } + + internal struct DbaseField + { + public string ColumnName; + public Type DataType; + public int Address; + public int Length; + public int Decimals; + } + } +} \ No newline at end of file