Index: src/Common/NetTopologySuite/IO/GeoTools/Dbase/DbaseFileHeader.cs =================================================================== diff -u -r8f6ae890fed8e8eae3a32f9c0498a10f82e0ddf9 -r5fc71a385897af92ccb092f2f969b5709afab85a --- src/Common/NetTopologySuite/IO/GeoTools/Dbase/DbaseFileHeader.cs (.../DbaseFileHeader.cs) (revision 8f6ae890fed8e8eae3a32f9c0498a10f82e0ddf9) +++ src/Common/NetTopologySuite/IO/GeoTools/Dbase/DbaseFileHeader.cs (.../DbaseFileHeader.cs) (revision 5fc71a385897af92ccb092f2f969b5709afab85a) @@ -4,373 +4,387 @@ namespace GisSharpBlog.NetTopologySuite.IO { - /// - /// Class for holding the information assicated with a dbase header. - /// - public class DbaseFileHeader - { - // Constant for the size of a record - private int FileDescriptorSize = 32; - - // type of the file, must be 03h - private int _fileType=0x03; - - // Date the file was last updated. - private DateTime _updateDate; - - // Number of records in the datafile - private int _numRecords = 0; - - // Length of the header structure - private int _headerLength; - - // Length of the records - private int _recordLength; - - // Number of fields in the record. - private int _numFields; - - // collection of header records. - private DbaseFieldDescriptor[] _fieldDescriptions; + /// + /// Class for holding the information assicated with a dbase header. + /// + public class DbaseFileHeader + { + // Constant for the size of a record + private readonly int FileDescriptorSize = 32; - /// - /// Initializes a new instance of the DbaseFileHeader class. - /// - public DbaseFileHeader() { } + // type of the file, must be 03h + private int _fileType = 0x03; - /// - /// Return the date this file was last updated. - /// - /// - public DateTime LastUpdateDate - { - get - { - return _updateDate; - } - } + // Date the file was last updated. - /// - /// Return the number of fields in the records. - /// - /// - public int NumFields - { - get { return _numFields; } - set { _numFields = value; } - } - - /// - /// Return the number of records in the file. - /// - /// - public int NumRecords - { - get { return _numRecords; } - set { _numRecords = value; } - } - - /// - /// Return the length of the records in bytes. - /// - /// - public int RecordLength - { - get - { - return _recordLength; - } - } + // Number of records in the datafile - /// - /// Return the length of the header. - /// - /// - public int HeaderLength - { - get - { - return _headerLength; - } - } + // Length of the header structure - /// - /// Add a column to this DbaseFileHeader. - /// - /// The name of the field to add. - /// The type is one of (C N L or D) character, number, logical(true/false), or date. - /// The Field length is the total length in bytes reserved for this column. - /// The decimal count only applies to numbers(N), and floating point values (F), and refers to the number of characters to reserve after the decimal point. - public void AddColumn (string fieldName, char fieldType, int fieldLength, int decimalCount) - { - if (fieldLength <=0) fieldLength = 1; - if (_fieldDescriptions == null) _fieldDescriptions = new DbaseFieldDescriptor[0]; - int tempLength = 1; // the length is used for the offset, and there is a * for deleted as the first byte - DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[_fieldDescriptions.Length+1]; - for (int i=0; i<_fieldDescriptions.Length; i++) - { - _fieldDescriptions[i].DataAddress = tempLength; - tempLength = tempLength + _fieldDescriptions[i].Length; - tempFieldDescriptors[i] = _fieldDescriptions[i]; - } - tempFieldDescriptors[_fieldDescriptions.Length] = new DbaseFieldDescriptor(); - tempFieldDescriptors[_fieldDescriptions.Length].Length = fieldLength; - tempFieldDescriptors[_fieldDescriptions.Length].DecimalCount = decimalCount; - tempFieldDescriptors[_fieldDescriptions.Length].DataAddress = tempLength; - - // set the field name - string tempFieldName = fieldName; - if (tempFieldName == null) tempFieldName = "NoName"; - if (tempFieldName.Length > 11) - { - tempFieldName = tempFieldName.Substring(0,11); - Trace.Write("FieldName "+fieldName+" is longer than 11 characters, truncating to "+tempFieldName); - } - tempFieldDescriptors[_fieldDescriptions.Length].Name = tempFieldName; - - // the field type - if ((fieldType == 'C') || (fieldType == 'c')) - { - tempFieldDescriptors[_fieldDescriptions.Length].DbaseType = 'C'; - if (fieldLength > 254) Trace.WriteLine("Field Length for "+fieldName+" set to "+fieldLength+" Which is longer than 254, not consistent with dbase III"); - } - else if ((fieldType == 'S') || (fieldType == 's')) - { - tempFieldDescriptors[_fieldDescriptions.Length].DbaseType = 'C'; - Trace.WriteLine("Field type for "+fieldName+" set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character."); - if (fieldLength >254) Trace.WriteLine("Field Length for "+fieldName+" set to "+fieldLength+" Which is longer than 254, not consistent with dbase III"); - tempFieldDescriptors[_fieldDescriptions.Length].Length = 8; - } - else if ((fieldType == 'D') || (fieldType == 'd')) - { - tempFieldDescriptors[_fieldDescriptions.Length].DbaseType = 'D'; - if (fieldLength != 8) Trace.WriteLine("Field Length for "+fieldName+" set to "+fieldLength+" Setting to 8 digets YYYYMMDD"); - tempFieldDescriptors[_fieldDescriptions.Length].Length = 8; - } - else if ((fieldType == 'F') || (fieldType == 'f')) - { - tempFieldDescriptors[_fieldDescriptions.Length].DbaseType = 'F'; - if (fieldLength > 20) Trace.WriteLine("Field Length for "+fieldName+" set to "+fieldLength+" Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII."); - } - else if ((fieldType == 'N') || (fieldType == 'n')) - { - tempFieldDescriptors[_fieldDescriptions.Length].DbaseType = 'N'; - if (fieldLength > 18) Trace.WriteLine("Field Length for "+fieldName+" set to "+fieldLength+" Preserving length, but should be set to Max of 18 for dbase III specification."); - if (decimalCount < 0) - { - Trace.WriteLine("Field Decimal Position for "+fieldName+" set to "+decimalCount+" Setting to 0 no decimal data will be saved."); - tempFieldDescriptors[_fieldDescriptions.Length].DecimalCount = 0; - } - if(decimalCount>fieldLength-1) - { - Trace.WriteLine("Field Decimal Position for "+fieldName+" set to "+decimalCount+" Setting to "+(fieldLength-1)+" no non decimal data will be saved."); - tempFieldDescriptors[_fieldDescriptions.Length].DecimalCount = fieldLength-1; - } - } - else if ((fieldType == 'L') || (fieldType == 'l')) - { - tempFieldDescriptors[_fieldDescriptions.Length].DbaseType = 'L'; - if (fieldLength != 1) Trace.WriteLine("Field Length for "+fieldName+" set to "+fieldLength+" Setting to length of 1 for logical fields."); - tempFieldDescriptors[_fieldDescriptions.Length].Length = 1; - } - else - { - throw new NotSupportedException("Unsupported field type "+fieldType + " For column "+fieldName); - } - // the length of a record - tempLength = tempLength + tempFieldDescriptors[_fieldDescriptions.Length].Length; - - // set the new fields. - _fieldDescriptions = tempFieldDescriptors; - _headerLength = 33+32*_fieldDescriptions.Length; - _numFields = _fieldDescriptions.Length; - _recordLength=tempLength; - } - - /// - /// Remove a column from this DbaseFileHeader. - /// - /// - /// return index of the removed column, -1 if no found. - public int RemoveColumn(string fieldName) - { - int retCol = -1; - int tempLength = 1; - DbaseFieldDescriptor[] tempFieldDescriptors = - new DbaseFieldDescriptor[_fieldDescriptions.Length - 1]; - for (int i = 0, j = 0; i < _fieldDescriptions.Length; i++) - { - if (fieldName.ToLower()!=(_fieldDescriptions[i].Name.Trim().ToLower())) - { - // if this is the last field and we still haven't found the - // named field - if (i == j && i == _fieldDescriptions.Length - 1) - return retCol; - tempFieldDescriptors[j] = _fieldDescriptions[i]; - tempFieldDescriptors[j].DataAddress = tempLength; - tempLength += tempFieldDescriptors[j].Length; - // only increment j on non-matching fields - j++; - } - else retCol = i; - } + // Length of the records - // set the new fields. - _fieldDescriptions = tempFieldDescriptors; - _headerLength = 33+32*_fieldDescriptions.Length; - _numFields = _fieldDescriptions.Length; - _recordLength = tempLength; + // Number of fields in the record. - return retCol; - } - - /// - /// Read the header data from the DBF file. - /// - /// BinaryReader containing the header. - public void ReadHeader(BinaryReader reader) - { - // type of reader. - _fileType = reader.ReadByte(); - if (_fileType != 0x03) - throw new NotSupportedException("Unsupported DBF reader Type "+_fileType); - - // parse the update date information. - int year = (int)reader.ReadByte(); - int month = (int)reader.ReadByte(); - int day = (int)reader.ReadByte(); - _updateDate = new DateTime(year + 1900, month, day); - - // read the number of records. - _numRecords = reader.ReadInt32(); - - // read the length of the header structure. - _headerLength = reader.ReadInt16(); - - // read the length of a record - _recordLength = reader.ReadInt16(); - - // skip the reserved bytes in the header. - //in.skipBytes(20); - reader.ReadBytes(20); - // calculate the number of Fields in the header - _numFields = (_headerLength - FileDescriptorSize -1)/FileDescriptorSize; - - // read all of the header records - _fieldDescriptions = new DbaseFieldDescriptor[_numFields]; - for (int i=0; i<_numFields; i++) - { - _fieldDescriptions[i] = new DbaseFieldDescriptor(); - - // read the field name - char[] buffer = new char[11]; - buffer = reader.ReadChars(11); - string name = new string(buffer); - int nullPoint = name.IndexOf((char)0); - if(nullPoint != -1) - name = name.Substring(0,nullPoint); - _fieldDescriptions[i].Name = name; - - // read the field type - _fieldDescriptions[i].DbaseType = (char) reader.ReadByte(); - - // read the field data address, offset from the start of the record. - _fieldDescriptions[i].DataAddress = reader.ReadInt32(); - - // read the field length in bytes - int tempLength = (int) reader.ReadByte(); - if (tempLength < 0) tempLength = tempLength + 256; - _fieldDescriptions[i].Length = tempLength; - - // read the field decimal count in bytes - _fieldDescriptions[i].DecimalCount = (int) reader.ReadByte(); - - // read the reserved bytes. - //reader.skipBytes(14); - reader.ReadBytes(14); - } - - // Last byte is a marker for the end of the field definitions. - reader.ReadBytes(1); - } - - /// - /// Set the number of records in the file - /// - /// - protected void SetNumRecords(int inNumRecords) - { - _numRecords = inNumRecords; - } - - /// - /// Write the header data to the DBF file. - /// - /// - public void WriteHeader(BinaryWriter writer) - { - // write the output file type. - writer.Write((byte)_fileType); - - writer.Write((byte)(_updateDate.Year - 1900)); - writer.Write((byte)_updateDate.Month); - writer.Write((byte)_updateDate.Day); - - // write the number of records in the datafile. - writer.Write(_numRecords); - - // write the length of the header structure. - writer.Write((short)_headerLength); - - // write the length of a record - writer.Write((short)_recordLength); - - // write the reserved bytes in the header - for (int i=0; i<20; i++) - writer.Write((byte)0); - - // write all of the header records - int tempOffset = 0; - for (int i=0; i<_fieldDescriptions.Length; i++) - { - // write the field name - for (int j=0; j<11; j++) - { - if (_fieldDescriptions[i].Name.Length > j) - writer.Write((byte) _fieldDescriptions[i].Name[j]); - else writer.Write((byte)0); - } - - // write the field type - writer.Write((char)_fieldDescriptions[i].DbaseType); - - // write the field data address, offset from the start of the record. - writer.Write(0); - tempOffset += _fieldDescriptions[i].Length; - - // write the length of the field. - writer.Write((byte)_fieldDescriptions[i].Length); - - // write the decimal count. - writer.Write((byte)_fieldDescriptions[i].DecimalCount); - - // write the reserved bytes. - for (int j=0; j<14; j++) writer.Write((byte)0); - } - - // write the end of the field definitions marker - writer.Write((byte)0x0D); - } + // collection of header records. - /// - /// Returns the fields in the dbase file. - /// - public DbaseFieldDescriptor[] Fields - { - get - { - return _fieldDescriptions; - } - } - } -} + /// + /// Initializes a new instance of the DbaseFileHeader class. + /// + public DbaseFileHeader() + { + NumRecords = 0; + } + + /// + /// Return the date this file was last updated. + /// + /// + public DateTime LastUpdateDate { get; private set; } + + /// + /// Return the number of fields in the records. + /// + /// + public int NumFields { get; set; } + + /// + /// Return the number of records in the file. + /// + /// + public int NumRecords { get; set; } + + /// + /// Return the length of the records in bytes. + /// + /// + public int RecordLength { get; private set; } + + /// + /// Return the length of the header. + /// + /// + public int HeaderLength { get; private set; } + + /// + /// Returns the fields in the dbase file. + /// + public DbaseFieldDescriptor[] Fields { get; private set; } + + /// + /// Add a column to this DbaseFileHeader. + /// + /// The name of the field to add. + /// The type is one of (C N L or D) character, number, logical(true/false), or date. + /// The Field length is the total length in bytes reserved for this column. + /// The decimal count only applies to numbers(N), and floating point values (F), and refers to the number of characters to reserve after the decimal point. + public void AddColumn(string fieldName, char fieldType, int fieldLength, int decimalCount) + { + if (fieldLength <= 0) + { + fieldLength = 1; + } + if (Fields == null) + { + Fields = new DbaseFieldDescriptor[0]; + } + int tempLength = 1; // the length is used for the offset, and there is a * for deleted as the first byte + DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[Fields.Length + 1]; + for (int i = 0; i < Fields.Length; i++) + { + Fields[i].DataAddress = tempLength; + tempLength = tempLength + Fields[i].Length; + tempFieldDescriptors[i] = Fields[i]; + } + tempFieldDescriptors[Fields.Length] = new DbaseFieldDescriptor(); + tempFieldDescriptors[Fields.Length].Length = fieldLength; + tempFieldDescriptors[Fields.Length].DecimalCount = decimalCount; + tempFieldDescriptors[Fields.Length].DataAddress = tempLength; + + // set the field name + string tempFieldName = fieldName; + if (tempFieldName == null) + { + tempFieldName = "NoName"; + } + if (tempFieldName.Length > 11) + { + tempFieldName = tempFieldName.Substring(0, 11); + Trace.Write("FieldName " + fieldName + " is longer than 11 characters, truncating to " + tempFieldName); + } + tempFieldDescriptors[Fields.Length].Name = tempFieldName; + + // the field type + if ((fieldType == 'C') || (fieldType == 'c')) + { + tempFieldDescriptors[Fields.Length].DbaseType = 'C'; + if (fieldLength > 254) + { + Trace.WriteLine("Field Length for " + fieldName + " set to " + fieldLength + " Which is longer than 254, not consistent with dbase III"); + } + } + else if ((fieldType == 'S') || (fieldType == 's')) + { + tempFieldDescriptors[Fields.Length].DbaseType = 'C'; + Trace.WriteLine("Field type for " + fieldName + " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character."); + if (fieldLength > 254) + { + Trace.WriteLine("Field Length for " + fieldName + " set to " + fieldLength + " Which is longer than 254, not consistent with dbase III"); + } + tempFieldDescriptors[Fields.Length].Length = 8; + } + else if ((fieldType == 'D') || (fieldType == 'd')) + { + tempFieldDescriptors[Fields.Length].DbaseType = 'D'; + if (fieldLength != 8) + { + Trace.WriteLine("Field Length for " + fieldName + " set to " + fieldLength + " Setting to 8 digets YYYYMMDD"); + } + tempFieldDescriptors[Fields.Length].Length = 8; + } + else if ((fieldType == 'F') || (fieldType == 'f')) + { + tempFieldDescriptors[Fields.Length].DbaseType = 'F'; + if (fieldLength > 20) + { + Trace.WriteLine("Field Length for " + fieldName + " set to " + fieldLength + " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII."); + } + } + else if ((fieldType == 'N') || (fieldType == 'n')) + { + tempFieldDescriptors[Fields.Length].DbaseType = 'N'; + if (fieldLength > 18) + { + Trace.WriteLine("Field Length for " + fieldName + " set to " + fieldLength + " Preserving length, but should be set to Max of 18 for dbase III specification."); + } + if (decimalCount < 0) + { + Trace.WriteLine("Field Decimal Position for " + fieldName + " set to " + decimalCount + " Setting to 0 no decimal data will be saved."); + tempFieldDescriptors[Fields.Length].DecimalCount = 0; + } + if (decimalCount > fieldLength - 1) + { + Trace.WriteLine("Field Decimal Position for " + fieldName + " set to " + decimalCount + " Setting to " + (fieldLength - 1) + " no non decimal data will be saved."); + tempFieldDescriptors[Fields.Length].DecimalCount = fieldLength - 1; + } + } + else if ((fieldType == 'L') || (fieldType == 'l')) + { + tempFieldDescriptors[Fields.Length].DbaseType = 'L'; + if (fieldLength != 1) + { + Trace.WriteLine("Field Length for " + fieldName + " set to " + fieldLength + " Setting to length of 1 for logical fields."); + } + tempFieldDescriptors[Fields.Length].Length = 1; + } + else + { + throw new NotSupportedException("Unsupported field type " + fieldType + " For column " + fieldName); + } + // the length of a record + tempLength = tempLength + tempFieldDescriptors[Fields.Length].Length; + + // set the new fields. + Fields = tempFieldDescriptors; + HeaderLength = 33 + 32*Fields.Length; + NumFields = Fields.Length; + RecordLength = tempLength; + } + + /// + /// Remove a column from this DbaseFileHeader. + /// + /// + /// return index of the removed column, -1 if no found. + public int RemoveColumn(string fieldName) + { + int retCol = -1; + int tempLength = 1; + DbaseFieldDescriptor[] tempFieldDescriptors = + new DbaseFieldDescriptor[Fields.Length - 1]; + for (int i = 0, j = 0; i < Fields.Length; i++) + { + if (fieldName.ToLower() != (Fields[i].Name.Trim().ToLower())) + { + // if this is the last field and we still haven't found the + // named field + if (i == j && i == Fields.Length - 1) + { + return retCol; + } + tempFieldDescriptors[j] = Fields[i]; + tempFieldDescriptors[j].DataAddress = tempLength; + tempLength += tempFieldDescriptors[j].Length; + // only increment j on non-matching fields + j++; + } + else + { + retCol = i; + } + } + + // set the new fields. + Fields = tempFieldDescriptors; + HeaderLength = 33 + 32*Fields.Length; + NumFields = Fields.Length; + RecordLength = tempLength; + + return retCol; + } + + /// + /// Read the header data from the DBF file. + /// + /// BinaryReader containing the header. + public void ReadHeader(BinaryReader reader) + { + // type of reader. + _fileType = reader.ReadByte(); + if (_fileType != 0x03) + { + throw new NotSupportedException("Unsupported DBF reader Type " + _fileType); + } + + // parse the update date information. + int year = (int) reader.ReadByte(); + int month = (int) reader.ReadByte(); + int day = (int) reader.ReadByte(); + LastUpdateDate = new DateTime(year + 1900, month, day); + + // read the number of records. + NumRecords = reader.ReadInt32(); + + // read the length of the header structure. + HeaderLength = reader.ReadInt16(); + + // read the length of a record + RecordLength = reader.ReadInt16(); + + // skip the reserved bytes in the header. + //in.skipBytes(20); + reader.ReadBytes(20); + // calculate the number of Fields in the header + NumFields = (HeaderLength - FileDescriptorSize - 1)/FileDescriptorSize; + + // read all of the header records + Fields = new DbaseFieldDescriptor[NumFields]; + for (int i = 0; i < NumFields; i++) + { + Fields[i] = new DbaseFieldDescriptor(); + + // read the field name + char[] buffer = new char[11]; + buffer = reader.ReadChars(11); + string name = new string(buffer); + int nullPoint = name.IndexOf((char) 0); + if (nullPoint != -1) + { + name = name.Substring(0, nullPoint); + } + Fields[i].Name = name; + + // read the field type + Fields[i].DbaseType = (char) reader.ReadByte(); + + // read the field data address, offset from the start of the record. + Fields[i].DataAddress = reader.ReadInt32(); + + // read the field length in bytes + int tempLength = (int) reader.ReadByte(); + if (tempLength < 0) + { + tempLength = tempLength + 256; + } + Fields[i].Length = tempLength; + + // read the field decimal count in bytes + Fields[i].DecimalCount = (int) reader.ReadByte(); + + // read the reserved bytes. + //reader.skipBytes(14); + reader.ReadBytes(14); + } + + // Last byte is a marker for the end of the field definitions. + reader.ReadBytes(1); + } + + /// + /// Write the header data to the DBF file. + /// + /// + public void WriteHeader(BinaryWriter writer) + { + // write the output file type. + writer.Write((byte) _fileType); + + writer.Write((byte) (LastUpdateDate.Year - 1900)); + writer.Write((byte) LastUpdateDate.Month); + writer.Write((byte) LastUpdateDate.Day); + + // write the number of records in the datafile. + writer.Write(NumRecords); + + // write the length of the header structure. + writer.Write((short) HeaderLength); + + // write the length of a record + writer.Write((short) RecordLength); + + // write the reserved bytes in the header + for (int i = 0; i < 20; i++) + { + writer.Write((byte) 0); + } + + // write all of the header records + int tempOffset = 0; + for (int i = 0; i < Fields.Length; i++) + { + // write the field name + for (int j = 0; j < 11; j++) + { + if (Fields[i].Name.Length > j) + { + writer.Write((byte) Fields[i].Name[j]); + } + else + { + writer.Write((byte) 0); + } + } + + // write the field type + writer.Write((char) Fields[i].DbaseType); + + // write the field data address, offset from the start of the record. + writer.Write(0); + tempOffset += Fields[i].Length; + + // write the length of the field. + writer.Write((byte) Fields[i].Length); + + // write the decimal count. + writer.Write((byte) Fields[i].DecimalCount); + + // write the reserved bytes. + for (int j = 0; j < 14; j++) + { + writer.Write((byte) 0); + } + } + + // write the end of the field definitions marker + writer.Write((byte) 0x0D); + } + + /// + /// Set the number of records in the file + /// + /// + protected void SetNumRecords(int inNumRecords) + { + NumRecords = inNumRecords; + } + } +} \ No newline at end of file