From f32c149bae607bf18f721b579bb2afd06cecd195 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 11 Feb 2022 15:06:05 -0800 Subject: [PATCH] Import dotnetDBF for error handling. --- BodyshopUploader.sln | 28 + BodyshopUploader/ImEXOnlinePartner.csproj | 13 +- BodyshopUploader/Releases/RELEASES | 4 +- .../Utils/Decoder/EstimateDecoder.cs | 11 +- Copying.TXT | 504 ++++++++++++++++ .../DotNetDBF.Enumerable.csproj | 38 ++ DotNetDBF.Enumerable/Enumerable.cs | 271 +++++++++ DotNetDBF.Enumerable/Obsolete.cs | 128 +++++ DotNetDBF.Enumerable/sn.snk | Bin 0 -> 596 bytes DotNetDBF.Test/DotNetDBF.Test.csproj | 43 ++ DotNetDBF.Test/Issue19Test.cs | 31 + DotNetDBF.Test/Program.cs | 16 + DotNetDBF.Test/Properties/AssemblyInfo.cs | 11 + DotNetDBF.Test/Test.cs | 544 ++++++++++++++++++ DotNetDBF.Test/dbfs/Readme.md | 22 + DotNetDBF.Test/dbfs/cp1251.dbf | Bin 0 -> 781 bytes DotNetDBF.Test/dbfs/cp1251_summary.txt | 11 + DotNetDBF.Test/dbfs/dbase_03.dbf | Bin 0 -> 9286 bytes DotNetDBF.Test/dbfs/dbase_03_summary.txt | 40 ++ DotNetDBF.Test/dbfs/dbase_30.dbf | Bin 0 -> 137775 bytes DotNetDBF.Test/dbfs/dbase_30.fpt | Bin 0 -> 46720 bytes DotNetDBF.Test/dbfs/dbase_30_summary.txt | 154 +++++ DotNetDBF.Test/dbfs/dbase_31.dbf | Bin 0 -> 7963 bytes DotNetDBF.Test/dbfs/dbase_31_summary.txt | 20 + DotNetDBF.Test/dbfs/dbase_83.dbf | Bin 0 -> 54449 bytes DotNetDBF.Test/dbfs/dbase_83.dbt | Bin 0 -> 40387 bytes DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf | Bin 0 -> 54449 bytes .../dbfs/dbase_83_missing_memo_record_0.yml | 16 + DotNetDBF.Test/dbfs/dbase_83_record_0.yml | 16 + DotNetDBF.Test/dbfs/dbase_83_record_9.yml | 23 + DotNetDBF.Test/dbfs/dbase_83_schema.txt | 19 + DotNetDBF.Test/dbfs/dbase_83_summary.txt | 24 + DotNetDBF.Test/dbfs/dbase_8b.dbf | Bin 0 -> 1826 bytes DotNetDBF.Test/dbfs/dbase_8b.dbt | Bin 0 -> 5120 bytes DotNetDBF.Test/dbfs/dbase_8b_summary.txt | 15 + DotNetDBF.Test/dbfs/dbase_f5.dbf | Bin 0 -> 946697 bytes DotNetDBF.Test/dbfs/dbase_f5.fpt | Bin 0 -> 36179 bytes DotNetDBF.Test/dbfs/dbase_f5_summary.txt | 68 +++ .../dbfs/foxprodb/FOXPRO-DB-TEST.DBC | Bin 0 -> 10123 bytes .../dbfs/foxprodb/FOXPRO-DB-TEST.DCT | Bin 0 -> 10688 bytes .../dbfs/foxprodb/FOXPRO-DB-TEST.DCX | Bin 0 -> 5632 bytes DotNetDBF.Test/dbfs/foxprodb/calls.CDX | Bin 0 -> 6144 bytes DotNetDBF.Test/dbfs/foxprodb/calls.FPT | Bin 0 -> 1728 bytes DotNetDBF.Test/dbfs/foxprodb/calls.dbf | Bin 0 -> 5017 bytes DotNetDBF.Test/dbfs/foxprodb/contacts.CDX | Bin 0 -> 6144 bytes DotNetDBF.Test/dbfs/foxprodb/contacts.FPT | Bin 0 -> 960 bytes DotNetDBF.Test/dbfs/foxprodb/contacts.dbf | Bin 0 -> 10450 bytes DotNetDBF.Test/dbfs/foxprodb/setup.CDX | Bin 0 -> 3072 bytes DotNetDBF.Test/dbfs/foxprodb/setup.dbf | Bin 0 -> 526 bytes DotNetDBF.Test/dbfs/foxprodb/types.CDX | Bin 0 -> 3072 bytes DotNetDBF.Test/dbfs/foxprodb/types.dbf | Bin 0 -> 471 bytes dotnetdbf/.gitignore | 9 + dotnetdbf/DBFBase.cs | 44 ++ dotnetdbf/DBFException.cs | 62 ++ dotnetdbf/DBFField.cs | 309 ++++++++++ dotnetdbf/DBFFieldType.cs | 86 +++ dotnetdbf/DBFHeader.cs | 182 ++++++ dotnetdbf/DBFReader.cs | 487 ++++++++++++++++ dotnetdbf/DBFWriter.cs | 528 +++++++++++++++++ dotnetdbf/DBTHeader.cs | 37 ++ dotnetdbf/DotNetDBF.csproj | 33 ++ dotnetdbf/DotNetDBF.sln | 33 ++ dotnetdbf/DotNetDBF.sln.DotSettings | 9 + dotnetdbf/MemoValue.cs | 164 ++++++ dotnetdbf/Properties/MoreAssemblyInfo.cs | 3 + dotnetdbf/README.md | 11 + dotnetdbf/Utils.cs | 175 ++++++ dotnetdbf/original java src/DBFBase.java | 36 ++ dotnetdbf/original java src/DBFException.java | 27 + dotnetdbf/original java src/DBFField.java | 283 +++++++++ dotnetdbf/original java src/DBFHeader.java | 167 ++++++ dotnetdbf/original java src/DBFReader.java | 315 ++++++++++ dotnetdbf/original java src/DBFWriter.java | 350 +++++++++++ dotnetdbf/original java src/Utils.java | 172 ++++++ dotnetdbf/sn.snk | Bin 0 -> 596 bytes 75 files changed, 5585 insertions(+), 7 deletions(-) create mode 100644 Copying.TXT create mode 100644 DotNetDBF.Enumerable/DotNetDBF.Enumerable.csproj create mode 100644 DotNetDBF.Enumerable/Enumerable.cs create mode 100644 DotNetDBF.Enumerable/Obsolete.cs create mode 100644 DotNetDBF.Enumerable/sn.snk create mode 100644 DotNetDBF.Test/DotNetDBF.Test.csproj create mode 100644 DotNetDBF.Test/Issue19Test.cs create mode 100644 DotNetDBF.Test/Program.cs create mode 100644 DotNetDBF.Test/Properties/AssemblyInfo.cs create mode 100644 DotNetDBF.Test/Test.cs create mode 100644 DotNetDBF.Test/dbfs/Readme.md create mode 100644 DotNetDBF.Test/dbfs/cp1251.dbf create mode 100644 DotNetDBF.Test/dbfs/cp1251_summary.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_03.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_03_summary.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_30.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_30.fpt create mode 100644 DotNetDBF.Test/dbfs/dbase_30_summary.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_31.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_31_summary.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_83.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_83.dbt create mode 100644 DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_83_missing_memo_record_0.yml create mode 100644 DotNetDBF.Test/dbfs/dbase_83_record_0.yml create mode 100644 DotNetDBF.Test/dbfs/dbase_83_record_9.yml create mode 100644 DotNetDBF.Test/dbfs/dbase_83_schema.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_83_summary.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_8b.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_8b.dbt create mode 100644 DotNetDBF.Test/dbfs/dbase_8b_summary.txt create mode 100644 DotNetDBF.Test/dbfs/dbase_f5.dbf create mode 100644 DotNetDBF.Test/dbfs/dbase_f5.fpt create mode 100644 DotNetDBF.Test/dbfs/dbase_f5_summary.txt create mode 100644 DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DBC create mode 100644 DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCT create mode 100644 DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCX create mode 100644 DotNetDBF.Test/dbfs/foxprodb/calls.CDX create mode 100644 DotNetDBF.Test/dbfs/foxprodb/calls.FPT create mode 100644 DotNetDBF.Test/dbfs/foxprodb/calls.dbf create mode 100644 DotNetDBF.Test/dbfs/foxprodb/contacts.CDX create mode 100644 DotNetDBF.Test/dbfs/foxprodb/contacts.FPT create mode 100644 DotNetDBF.Test/dbfs/foxprodb/contacts.dbf create mode 100644 DotNetDBF.Test/dbfs/foxprodb/setup.CDX create mode 100644 DotNetDBF.Test/dbfs/foxprodb/setup.dbf create mode 100644 DotNetDBF.Test/dbfs/foxprodb/types.CDX create mode 100644 DotNetDBF.Test/dbfs/foxprodb/types.dbf create mode 100644 dotnetdbf/.gitignore create mode 100644 dotnetdbf/DBFBase.cs create mode 100644 dotnetdbf/DBFException.cs create mode 100644 dotnetdbf/DBFField.cs create mode 100644 dotnetdbf/DBFFieldType.cs create mode 100644 dotnetdbf/DBFHeader.cs create mode 100644 dotnetdbf/DBFReader.cs create mode 100644 dotnetdbf/DBFWriter.cs create mode 100644 dotnetdbf/DBTHeader.cs create mode 100644 dotnetdbf/DotNetDBF.csproj create mode 100644 dotnetdbf/DotNetDBF.sln create mode 100644 dotnetdbf/DotNetDBF.sln.DotSettings create mode 100644 dotnetdbf/MemoValue.cs create mode 100644 dotnetdbf/Properties/MoreAssemblyInfo.cs create mode 100644 dotnetdbf/README.md create mode 100644 dotnetdbf/Utils.cs create mode 100644 dotnetdbf/original java src/DBFBase.java create mode 100644 dotnetdbf/original java src/DBFException.java create mode 100644 dotnetdbf/original java src/DBFField.java create mode 100644 dotnetdbf/original java src/DBFHeader.java create mode 100644 dotnetdbf/original java src/DBFReader.java create mode 100644 dotnetdbf/original java src/DBFWriter.java create mode 100644 dotnetdbf/original java src/Utils.java create mode 100644 dotnetdbf/sn.snk diff --git a/BodyshopUploader.sln b/BodyshopUploader.sln index a70e77d..9985848 100644 --- a/BodyshopUploader.sln +++ b/BodyshopUploader.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 16.0.29613.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImEXOnlinePartner", "BodyshopUploader\ImEXOnlinePartner.csproj", "{76B98E9B-A33A-464F-A07B-56E773376543}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetDBF", "dotnetdbf\DotNetDBF.csproj", "{9508821B-3B85-4088-8076-D0F0AF00DB55}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetDBF.Enumerable", "DotNetDBF.Enumerable\DotNetDBF.Enumerable.csproj", "{83E3893B-E266-475F-BAA4-0BFA8393CDCF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +31,30 @@ Global {76B98E9B-A33A-464F-A07B-56E773376543}.Test|Any CPU.Build.0 = Test|Any CPU {76B98E9B-A33A-464F-A07B-56E773376543}.Test|x86.ActiveCfg = Test|x86 {76B98E9B-A33A-464F-A07B-56E773376543}.Test|x86.Build.0 = Test|x86 + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Debug|x86.ActiveCfg = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Debug|x86.Build.0 = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Release|Any CPU.Build.0 = Release|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Release|x86.ActiveCfg = Release|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Release|x86.Build.0 = Release|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Test|Any CPU.ActiveCfg = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Test|Any CPU.Build.0 = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Test|x86.ActiveCfg = Debug|Any CPU + {9508821B-3B85-4088-8076-D0F0AF00DB55}.Test|x86.Build.0 = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Debug|x86.ActiveCfg = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Debug|x86.Build.0 = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Release|Any CPU.Build.0 = Release|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Release|x86.ActiveCfg = Release|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Release|x86.Build.0 = Release|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Test|Any CPU.ActiveCfg = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Test|Any CPU.Build.0 = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Test|x86.ActiveCfg = Debug|Any CPU + {83E3893B-E266-475F-BAA4-0BFA8393CDCF}.Test|x86.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BodyshopUploader/ImEXOnlinePartner.csproj b/BodyshopUploader/ImEXOnlinePartner.csproj index 84dd97c..0050fc0 100644 --- a/BodyshopUploader/ImEXOnlinePartner.csproj +++ b/BodyshopUploader/ImEXOnlinePartner.csproj @@ -348,9 +348,6 @@ - - 6.0.0.3 - 3.5.1 @@ -537,6 +534,16 @@ 2.5.1 + + + {83e3893b-e266-475f-baa4-0bfa8393cdcf} + DotNetDBF.Enumerable + + + {9508821b-3b85-4088-8076-d0f0af00db55} + DotNetDBF + + diff --git a/BodyshopUploader/Releases/RELEASES b/BodyshopUploader/Releases/RELEASES index 75d99b0..223cae2 100644 --- a/BodyshopUploader/Releases/RELEASES +++ b/BodyshopUploader/Releases/RELEASES @@ -56,4 +56,6 @@ CA63387A44437B55CD283AB4A2CB92385F1321E7 ImEXOnlinePartner-1.0.28-full.nupkg 550 36869986F57C7F0E25610A10F43C740DAC7639A2 ImEXOnlinePartner-1.0.29-delta.nupkg 56217 6F104EF819C1791FD334CE21CEE9C6B8DB3FC105 ImEXOnlinePartner-1.0.29-full.nupkg 5510931 D441404BE86E0A676462DD367D4355894C2FFDBA ImEXOnlinePartner-1.0.30-delta.nupkg 62664 -66963D1624BD0C662CDF6E93B87D9AE4FA8073FF ImEXOnlinePartner-1.0.30-full.nupkg 5512346 \ No newline at end of file +66963D1624BD0C662CDF6E93B87D9AE4FA8073FF ImEXOnlinePartner-1.0.30-full.nupkg 5512346 +8F1739F9B7C202F7E2453E7D5027F7422637D206 ImEXOnlinePartner-1.0.31-delta.nupkg 152092 +6B3D3A0DD81D6F0C302ACE42DEDDDFC11270DD22 ImEXOnlinePartner-1.0.31-full.nupkg 5609086 \ No newline at end of file diff --git a/BodyshopUploader/Utils/Decoder/EstimateDecoder.cs b/BodyshopUploader/Utils/Decoder/EstimateDecoder.cs index acf426b..ead78a9 100644 --- a/BodyshopUploader/Utils/Decoder/EstimateDecoder.cs +++ b/BodyshopUploader/Utils/Decoder/EstimateDecoder.cs @@ -1077,8 +1077,8 @@ namespace BodyshopPartner.Utils.Decoder for (int i = 0; i < reader.RecordCount; i++) { - - var readValues = reader.NextRecord(); + logger.Debug($"Reading line {i +1}"); + var readValues = reader.NextRecord(false); dynamic lin = new JObject(); //Mising est_seq @@ -1165,7 +1165,12 @@ namespace BodyshopPartner.Utils.Decoder } catch (IOException ex) { - logger.Trace(ex, "Unable to open LIN file. Retrying. "); + logger.Error(ex, "Unable to open LIN file. Retrying. "); + retryNumber++; + Thread.Sleep(retrySleep); + }catch(Exception Ex) + { + logger.Error(Ex, "Unable to open LIN file. Retrying. Unknown Exception "); retryNumber++; Thread.Sleep(retrySleep); } diff --git a/Copying.TXT b/Copying.TXT new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/Copying.TXT @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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.1 of the License, or (at your option) any later version. + + This library 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/DotNetDBF.Enumerable/DotNetDBF.Enumerable.csproj b/DotNetDBF.Enumerable/DotNetDBF.Enumerable.csproj new file mode 100644 index 0000000..3a1eaa5 --- /dev/null +++ b/DotNetDBF.Enumerable/DotNetDBF.Enumerable.csproj @@ -0,0 +1,38 @@ + + + net40;netstandard2.0 + True + sn.snk + dotnetdbf.enumerable + Jay Tuley + Ekon Benefits + For dotnetdbf projects using .net 4.0 projects this is an enumeration framework in which makes it easy to use Linq to Objects. + lgpl + https://github.com/ekonbenefits/dotnetdbf + clipper xbase dbf linq + LGPL-2.1-or-later + https://github.com/ekonbenefits/dotnetdbf + https://github.com/ekonbenefits/dotnetdbf.git + gits + True + True + snupkg + true + 6.0.0.2 + True + True + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DotNetDBF.Enumerable/Enumerable.cs b/DotNetDBF.Enumerable/Enumerable.cs new file mode 100644 index 0000000..81b2b5c --- /dev/null +++ b/DotNetDBF.Enumerable/Enumerable.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Runtime.CompilerServices; +using ImpromptuInterface; +using Dynamitey; + +namespace DotNetDBF.Enumerable +{ + /// + /// Interface to get the contents of the DBF Wrapper + /// + public interface IDBFInterceptor + { + /// + /// Does field exist in row + /// + /// + bool Exists(string fieldName); + + /// + /// Gets the data row. + /// + /// + object[] GetDataRow(); + } + +#pragma warning disable 618 + public class DBFInterceptor : DBFIntercepter +#pragma warning restore 618 + { + public DBFInterceptor(object[] wrappedObj, string[] fieldNames) : base(wrappedObj, fieldNames) + { + } + } + + + /// + /// DBF Dynamic Wrapper + /// + public abstract class BaseDBFInterceptor : Dynamitey.DynamicObjects.BaseObject, IDBFInterceptor + { + private readonly string[] _fieldNames; + private readonly object[] _wrappedArray; + + protected BaseDBFInterceptor(object[] wrappedObj, string[] fieldNames) + { + _wrappedArray = wrappedObj; + _fieldNames = fieldNames; + } + + public override IEnumerable GetDynamicMemberNames() + { + return _fieldNames; + } + + public bool Exists(string fieldName) + { + return _fieldNames.Contains(fieldName); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = null; + var tLookup = binder.Name; + var tIndex = Array.FindIndex(_fieldNames, + it => it.Equals(tLookup, StringComparison.InvariantCultureIgnoreCase)); + + if (tIndex < 0) + return false; + + + result = _wrappedArray[tIndex]; + + + if (TryTypeForName(tLookup, out var outType)) + { + result = Dynamic.CoerceConvert(result, outType); + } + + return true; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + var tLookup = binder.Name; + var tIndex = Array.FindIndex(_fieldNames, + it => it.Equals(tLookup, StringComparison.InvariantCultureIgnoreCase)); + + if (tIndex < 0) + return false; + + if (TryTypeForName(tLookup, out var outType)) + { + value = Dynamic.CoerceConvert(value, outType); + } + + _wrappedArray[tIndex] = value; + + return true; + } + + public object[] GetDataRow() + { + return _wrappedArray; + } + } + + /// + /// Enumerable API + /// + public static partial class DBFEnumerable + { + /// + /// New Blank Row Dynamic object that matches writer; + /// + /// The writer. + /// + public static dynamic NewBlankRow(this DBFWriter writer) + { + var fields = writer.Fields.Select(it => it.Name).ToArray(); + var obj = new object[fields.Length]; + return new Enumerable.DBFInterceptor(obj, fields); + } + + public static void CopyRecordTo(this IDBFInterceptor original, IDBFInterceptor dest) + { + foreach (var fieldName in Dynamitey.Dynamic.GetMemberNames(dest, true)) + { + try + { + var val = Dynamic.InvokeGet(original, fieldName); + Dynamic.InvokeSet(dest, fieldName, val); + } + catch + { + // ignored + } + } + } + + + /// + /// Writes the record. + /// + /// The writer. + /// The value. + public static void WriteRecord(this DBFWriter writer, Enumerable.IDBFInterceptor value) + { + writer.WriteRecord(value.GetDataRow()); + } + + /// + /// Adds the record. + /// + /// The writer. + /// The value. + public static void AddRecord(this DBFWriter writer, Enumerable.IDBFInterceptor value) + { + writer.AddRecord(value.GetDataRow()); + } + + /// + /// Return all the records. T should be interface with getter properties that match types and names of the database. + /// Optionally instead of T being and interface you can pass in an anonymous object with properties that match that + /// database and then you'll get an IEnumerable of that anonymous type with the data filled in. + /// + /// + /// The reader. + /// The prototype. Anonymous class instance + /// Indicates if parsing error throws an exception. Default: true + /// + public static IEnumerable AllRecords(this DBFReader reader, T prototype = null, bool throwOnParsingError = true) where T : class + { + var tType = typeof(T); + + var tProperties = tType.GetProperties() + .Where( + it => + Array.FindIndex(reader.Fields, + f => f.Name.Equals(it.Name, StringComparison.InvariantCultureIgnoreCase)) >= 0) + .ToList(); + var tProps = tProperties + .Select( + it => + Array.FindIndex(reader.Fields, + jt => jt.Name.Equals(it.Name, StringComparison.InvariantCultureIgnoreCase))) + .Where(it => it >= 0) + .ToArray(); + + var tOrderedProps = tProps.OrderBy(it => it).ToArray(); + var tReturn = new List(); + + + if (tType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) + { + var tAnon = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); + while (tAnon != null) + { + tReturn.Add((T) Activator.CreateInstance(tType, tAnon)); + tAnon = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); + } + + + return tReturn; + } + + var t = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); + + while (t != null) + { + var interceptor = new Enumerable.DBFInterceptor(t, tProperties.Select(it => it.Name).ToArray()); + + tReturn.Add(interceptor.ActLike(typeof(Enumerable.IDBFInterceptor))); + t = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); + } + + + return tReturn; + } + + /// + /// Returns a list of dynamic objects whose properties and types match up with that database name. + /// + /// The reader. + /// The where column name. + /// What the were column should equal. + /// Indicates if parsing error throws an exception. Default: true + /// + public static IEnumerable DynamicAllRecords(this DBFReader reader, string whereColumn = null, + dynamic whereColumnEquals = null, bool throwOnParsingError = true) + { + var props = reader.GetSelectFields().Select(it => it.Name).ToArray(); + + int? whereColumnIndex = null; + if (!String.IsNullOrEmpty(whereColumn)) + { + whereColumnIndex = Array.FindIndex(props, + it => it.Equals(whereColumn, StringComparison.InvariantCultureIgnoreCase)); + } + + + var tReturn = new List(); + var t = reader.NextRecord(throwOnParsingError); + + while (t != null) + { + if (whereColumnIndex is int i) + { + dynamic tO = t[i]; + if (!tO.Equals(whereColumnEquals)) + { + t = reader.NextRecord(throwOnParsingError); + continue; + } + } + + + var interceptor = new Enumerable.DBFInterceptor(t, props); + + + tReturn.Add(interceptor); + t = reader.NextRecord(throwOnParsingError); + } + + + return tReturn; + } + } +} \ No newline at end of file diff --git a/DotNetDBF.Enumerable/Obsolete.cs b/DotNetDBF.Enumerable/Obsolete.cs new file mode 100644 index 0000000..f6ec08f --- /dev/null +++ b/DotNetDBF.Enumerable/Obsolete.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +// ReSharper disable StringLiteralTypo +// ReSharper disable IdentifierTypo + + +namespace DotNetDBF.Enumerable +{ + /// + /// Interface to get the contents of the DBF Wrapper + /// + /// + [Obsolete("DotNetDBF.Enumerable.IDBFInterceptor is the new interface name",error:true)] + public interface IDBFIntercepter:IDBFInterceptor + { + + } + + + [Obsolete("DotNetDBF.Enumerable.DBFInterceptor is the new class name")] + public class DBFIntercepter : DBFEnumerable.DBFIntercepter + { + public DBFIntercepter(object[] wrappedObj, string[] fieldNames) : base(wrappedObj, fieldNames) + { + } + } + + +} + + +namespace DotNetDBF.Enumerable +{ + public static partial class DBFEnumerable + { + [Obsolete("DotNetDBF.Enumerable.IDBFIntercepter is the new interface name")] + public interface IDBFIntercepter : DotNetDBF.Enumerable.IDBFIntercepter + { + } + + [Obsolete("DotNetDBF.Enumerable.DBFIntercepter is the new class name")] + public class DBFIntercepter : DotNetDBF.Enumerable.Enuemrable.DBFIntercepter, IDBFIntercepter + { + public DBFIntercepter(object[] wrappedObj, string[] fieldNames) + : base(wrappedObj, fieldNames) + { + } + } + } + + + [Obsolete("DBFEnumerable is the new class name")] + public static class Enuemrable + { + /// + /// New Blank Row Dynamic object that matches writer; + /// + /// The writer. + /// + public static dynamic NewBlankRow(DBFWriter writer) + { + return writer.NewBlankRow(); + } + + + /// + /// Writes the record. + /// + /// The writer. + /// The value. + public static void WriteRecord(DBFWriter writer, IDBFIntercepter value) + { + writer.WriteRecord(value); + } + + /// + /// Adds the record. + /// + /// The writer. + /// The value. + public static void AddRecord(DBFWriter writer, IDBFIntercepter value) + { + writer.AddRecord(writer, value); + } + + /// + /// Return all the records. T should be interface with getter properties that match types and names of the database. + /// Optionally instead of T being and interface you can pass in an anonymous object with properties that match that + /// database and then you'll get an IEnumerable of that anonymous type with the data filled in. + /// + /// + /// The reader. + /// The prototype. Anonymous class instance + /// + public static IEnumerable AllRecords(DBFReader reader, T prototype = null) where T : class + { + return reader.AllRecords(prototype); + } + + /// + /// Returns a list of dynamic objects whose properties and types match up with that database name. + /// + /// The reader. + /// The where column name. + /// What the were column should equal. + /// + public static IEnumerable DynamicAllRecords(DBFReader reader, string whereColumn = null, + dynamic whereColumnEquals = null) + { + return reader.DynamicAllRecords(whereColumn, (object) whereColumnEquals); + } + + + [Obsolete("DotNetDBF.Enumerable.IDBFIntercepter is the new interface name",error:true)] + public interface IDBFIntercepter : DotNetDBF.Enumerable.IDBFInterceptor + { + } + + [Obsolete("DotNetDBF.Enumerable.DBFIntercepter is the new class name")] + public class DBFIntercepter : DotNetDBF.Enumerable.BaseDBFInterceptor, IDBFIntercepter + { + public DBFIntercepter(object[] wrappedObj, string[] fieldNames) + : base(wrappedObj, fieldNames) + { + } + } + } +} \ No newline at end of file diff --git a/DotNetDBF.Enumerable/sn.snk b/DotNetDBF.Enumerable/sn.snk new file mode 100644 index 0000000000000000000000000000000000000000..d5261479f3c77251a879736e89c16cf8f60fe5bc GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097p6Y5cy_=k$|;mz+N4lN3aiA1dyL%30Q zUbtdLm!X6E%`Vu%rHtW)wpZ3T6dAAhu-=b}H1fCQ+d^VCRhI@ICM>&eHHxHeN!}3Hxum?+@>eBHix#hh*8(=Kl$_G-Rp(se zTaOa*%I*Q!pfB~Kx?`P3NqI-;9Yl^}3hh5wX9= zsz#6S`4nKiKKlN3Y9496rz|^?rZnqu4tlPm>d3>WY}P|Fs&wGC4A1aFg2G}!u-^2^ zLqjW`arQ8N>VERNRoOMKs*m(g7w-w@Qq%{HLRPAjy!q76+4T)kU2 iEvd}XFTc(XA90r-ZzK=p_S11}sf{~a7j0j$-g%r> + + netcoreapp2.0;net462 + Exe + Copyright © 2017 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DotNetDBF.Test/Issue19Test.cs b/DotNetDBF.Test/Issue19Test.cs new file mode 100644 index 0000000..0b28ea2 --- /dev/null +++ b/DotNetDBF.Test/Issue19Test.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace DotNetDBF.Test +{ + + [TestFixture] + public class Issue19Test + { + private Stream DbfsData(string filename) => typeof(Issue19Test).Assembly + .GetManifestResourceStream($"{nameof(DotNetDBF)}.{nameof(Test)}.dbfs.{filename}"); + + [Test] + public void ReadTest() + { + using (var dbfstream = DbfsData("dbase_8b.dbf")) + using (var memoStream = DbfsData("dbase_8b.dbt")) + using (DBFReader dbfr = new DBFReader(dbfstream) { DataMemo = memoStream}) + { + object[] record = dbfr.NextRecord(); + //This line would hang issue #19 https://github.com/ekonbenefits/dotnetdbf/issues/19 + Assert.Throws(() => record[5].ToString()); + } + } + } +} diff --git a/DotNetDBF.Test/Program.cs b/DotNetDBF.Test/Program.cs new file mode 100644 index 0000000..d68d107 --- /dev/null +++ b/DotNetDBF.Test/Program.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DotNetDBF.Test +{ +#if !NETCOREAPP2_0 + public class Program + { + public static void Main() + { + + } + } +#endif +} diff --git a/DotNetDBF.Test/Properties/AssemblyInfo.cs b/DotNetDBF.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e5445c0 --- /dev/null +++ b/DotNetDBF.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8d5436e3-f584-40a0-acdc-65346eceadb0")] diff --git a/DotNetDBF.Test/Test.cs b/DotNetDBF.Test/Test.cs new file mode 100644 index 0000000..6d1a775 --- /dev/null +++ b/DotNetDBF.Test/Test.cs @@ -0,0 +1,544 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using DotNetDBF; +using DotNetDBF.Enumerable; +using NUnit.Framework; + +namespace DotNetDBFTest +{ + public interface ITestInterface + { + string F1 { get; set; } + + string F2 { get; set; } + + string F3 { get; set; } + } + + [TestFixture] + public class DotNetDBFTest : AssertionHelper + { + private string TestPath([CallerMemberName] string name = null) + => Path.Combine(Path.GetTempPath(), name + "_121212.dbf"); + + private string TestRAFPath([CallerMemberName] string name = null) + => Path.Combine(Path.GetTempPath(), name + "_raf-1212.dbf"); + + + private string TestClipLongPath([CallerMemberName] string name = null) + => Path.Combine(Path.GetTempPath(), name + "_cliplong.dbf"); + + private string TestMemoPath([CallerMemberName] string name = null) + => Path.Combine(Path.GetTempPath(), name + "_clipmemo.dbf"); + + private string TestSelectPath([CallerMemberName] string name = null) + => Path.Combine(Path.GetTempPath(), name + "_select.dbf"); + + private string GetCharacters(int aLength) + { + var chars = new[] {"a", "b", "c", "d", "e", "f", "g", " "}; + var returnval = string.Join(string.Empty, + Enumerable.Range(0, aLength).Select(it => chars[it % chars.Length]).ToArray()); + Assert.That(returnval.Length, EqualTo(aLength), "GetCharacters() did not return correct length string"); + return returnval; + } + + + private static void println(String s) + { + Console.WriteLine(s); + } + + + [Test] + public void checkDataType_N() + { + Decimal writtenValue; + using ( + Stream fos = + File.Open(TestPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var writer = new DBFWriter()) + { + var field = new DBFField("F1", NativeDbType.Numeric, 15, 0); + writer.Fields = new[] {field}; + + writtenValue = 123456789012345L; + writer.AddRecord(writtenValue); + writer.Write(fos); + } + using ( + Stream fis = + File.Open(TestPath(), + FileMode.Open, + FileAccess.Read)) + using (var reader = new DBFReader(fis)) + { + var readValues = reader.NextRecord(); + + Assert.That(readValues[0], EqualTo(writtenValue), "Written Value Equals Read"); + } + } + + + [Test] + public void checkLongCharLengthWithClipper() + { + var fieldLength = 750; + string writtenValue; + using ( + Stream fos = + File.Open(TestClipLongPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + var writer = new DBFWriter(); + var field = new DBFField("F1", NativeDbType.Char, fieldLength); + writer.Fields = new[] {field}; + + writtenValue = GetCharacters(fieldLength); + writer.AddRecord(writtenValue); + writer.Write(fos); + } + using ( + Stream fis = + File.Open(TestClipLongPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + var reader = new DBFReader(fis); + Assert.That(reader.Fields.First().FieldLength, EqualTo(fieldLength)); + var readValues = reader.NextRecord(); + + Assert.That(readValues[0], EqualTo(writtenValue), "Written Value not equaling Read"); + } + } + + + [Test] + public void checkDataType_M() + { + var fieldLength = 2400; + MemoValue writtenValue; + using (Stream fos = File.Open(TestMemoPath(), FileMode.OpenOrCreate, FileAccess.ReadWrite)) + using (var writer = new DBFWriter + { + DataMemoLoc = Path.ChangeExtension(TestMemoPath(), "DBT") + }) + { + var field = new DBFField("F1", NativeDbType.Memo); + writer.Fields = new[] {field}; + + writtenValue = new MemoValue(GetCharacters(fieldLength)); + writer.AddRecord(writtenValue); + writer.Write(fos); + } + using (Stream fis = File.Open(TestMemoPath(), FileMode.OpenOrCreate, FileAccess.ReadWrite)) + using (var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(TestMemoPath(), "DBT") + }) + { + var readValues = reader.NextRecord(); + + Assert.That(readValues[0], EqualTo(writtenValue), "Written Value not equaling Read"); + } + } + + [Test] + public void checkSelect() + { + var fieldLength = 2400; + string writtenValue; + using ( + Stream fos = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + var writer = new DBFWriter + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }; + var field = new DBFField("F1", NativeDbType.Memo); + var field2 = new DBFField("F2", NativeDbType.Numeric, 10); + var field3 = new DBFField("F3", NativeDbType.Char, 10); + writer.Fields = new[] {field, field2, field3}; + + writtenValue = "alpha"; + writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); + writer.Write(fos); + } + using ( + Stream fis = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }; + reader.SetSelectFields("F3"); + var readValues = reader.NextRecord(); + + Assert.That(readValues[0], StartsWith(writtenValue), "Written Value not equaling Read"); + } + } + + [Test] + public void checkSelectDynamic() + { + var fieldLength = 2400; + string writtenValue; + string writtenMemo; + using ( + Stream fos = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var writer = new DBFWriter + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var field = new DBFField("F1", NativeDbType.Memo); + var field2 = new DBFField("F2", NativeDbType.Numeric, 10); + var field3 = new DBFField("F3", NativeDbType.Char, 10); + writer.Fields = new[] {field, field2, field3}; + + writtenValue = "alpha"; + writtenMemo = GetCharacters(fieldLength); + writer.AddRecord(new MemoValue(writtenMemo), 10, writtenValue); + writer.Write(fos); + } + using ( + Stream fis = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + reader.SetSelectFields("F1", "F3"); + var readValues = reader.DynamicAllRecords().First(); + + Assert.That(readValues.F1.ToString(), EqualTo(writtenMemo), "Written Value not equaling Read"); + Assert.That(readValues.F3, EqualTo(writtenValue), "Written Value not equaling Read"); + } + } + + [Test] + public void checkAnnoymous() + { + var fieldLength = 2400; + string writtenValue; + using ( + Stream fos = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + var writer = new DBFWriter + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }; + var field = new DBFField("F1", NativeDbType.Memo); + var field2 = new DBFField("F2", NativeDbType.Numeric, 10); + var field3 = new DBFField("F3", NativeDbType.Char, 10); + writer.Fields = new[] {field, field2, field3}; + + writtenValue = "alpha"; + writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); + writer.Write(fos); + } + using ( + Stream fis = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }; + + var readValues = reader.AllRecords(new {F2 = default(decimal), F3 = default(string)}); + + Assert.That(readValues.First().F3, StartsWith(writtenValue), "Written Value not equaling Read"); + } + } + + + [Test] + public void checkProxy() + { + var fieldLength = 2400; + string writtenValue; + using ( + Stream fos = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + using (var writer = new DBFWriter { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + + ; + var field = new DBFField("F1", NativeDbType.Memo); + var field2 = new DBFField("F2", NativeDbType.Numeric, 10); + var field3 = new DBFField("F3", NativeDbType.Char, 10); + writer.Fields = new[] {field, field2, field3}; + + writtenValue = "alpha"; + writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); + writer.Write(fos); + } + } + using ( + Stream fis = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + using (var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + + var readValues = reader.AllRecords(); + + Assert.That(readValues.First().F3, StartsWith(writtenValue), "Written Value not equaling Read"); + } + } + } + + [Test] + public void checkDynamicProxy() + { + var fieldLength = 2400; + string writtenValue; + using ( + Stream fos = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var writer = new DBFWriter + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var field = new DBFField("F1", NativeDbType.Memo); + var field2 = new DBFField("F2", NativeDbType.Numeric, 10); + var field3 = new DBFField("F3", NativeDbType.Char, 10); + writer.Fields = new[] {field, field2, field3}; + + writtenValue = "alpha"; + writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); + writer.Write(fos); + } + using ( + Stream fis = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var readValues = reader.DynamicAllRecords(); + + Assert.That(readValues.First().F3, StartsWith(writtenValue), "Written Value not equaling Read"); + } + } + + + [Test] + public void checkDynamicProxyWhere() + { + var fieldLength = 2400; + string writtenValue; + using ( + Stream fos = + File.Open(TestSelectPath(), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var writer = new DBFWriter + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var field = new DBFField("F1", NativeDbType.Memo); + var field2 = new DBFField("F2", NativeDbType.Numeric, 10); + var field3 = new DBFField("F3", NativeDbType.Char, 10); + writer.Fields = new[] {field, field2, field3}; + + writtenValue = "alpha"; + writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 10, writtenValue); + + writer.AddRecord(new MemoValue(GetCharacters(fieldLength)), 12, "beta"); + + writer.Write(fos); + } + + using (var reader = new DBFReader(TestSelectPath()) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var readValues = reader.DynamicAllRecords(); + + Assert.That(Equals(readValues.Count(), 2), "All Records not matching"); + } + + using (var reader = new DBFReader(TestSelectPath()) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var readValues = reader.DynamicAllRecords(whereColumn: "F2", whereColumnEquals: 10); + + Assert.That(Equals(readValues.Count(), 1), "All Records not matching"); + } + + using (var reader = new DBFReader(TestSelectPath()) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var readValues = reader.DynamicAllRecords(whereColumn: "F2", whereColumnEquals: 12); + + Assert.That(Equals(readValues.Count(), 1), "All Records not matching"); + } + using (var reader = new DBFReader(TestSelectPath()) + { + DataMemoLoc = Path.ChangeExtension(TestSelectPath(), "DBT") + }) + { + var readValues = reader.DynamicAllRecords(whereColumn: "F2", whereColumnEquals: 13); + + Assert.That(Equals(readValues.Count(), 0), "All Records not matching"); + } + } + + [Test] + public void checkRAFwriting() + { + println("Writing in RAF mode ... "); + + if (File.Exists(TestRAFPath())) + { + File.Delete(TestRAFPath()); + } + using (var writer = new DBFWriter(TestRAFPath())) + { + var fields = new DBFField[2]; + + fields[0] = new DBFField("F1", NativeDbType.Char, 10); + + fields[1] = new DBFField("F2", NativeDbType.Numeric, 2); + + writer.Fields = fields; + + + writer.WriteRecord("Red", 10); + writer.WriteRecord("Blue", 20); + } + + println("done."); + + println("Appending to this file"); + + using (var writer = new DBFWriter(TestRAFPath())) + { + writer.WriteRecord("Green", 33); + + writer.WriteRecord("Yellow", 44); + } + println("done."); + } + + [Test] + public void ShowPaths() + { + println(TestPath()); + + println(TestRAFPath()); + + println(TestClipLongPath()); + } + + [Test] + public void Test() + { + using ( + Stream fis = + File.Open(@"f:\st\dev\testdata\p.dbf", + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + using (var reader = new DBFReader(fis) + { + DataMemoLoc = Path.ChangeExtension(@"f:\st\dev\testdata\p.dbf", "DBT") + }) + { + var readValues = reader.NextRecord(); + + Console.WriteLine(readValues); + } + } + + + [Test] + public void test1() + { + Assert.DoesNotThrow(() => { new DBFWriter(); }, "Can't Create empty DBFWriter Object"); + } + + [Test] + public void test2() + { + Assert.DoesNotThrow(() => { new DBFField(); }, "Can't Create empty DBFWriter Object"); + } + + + [Test] + public void test3() + { + WriteSample(); + ReadSample(); + } + + public void WriteSample([CallerMemberName] string name = null) + { + var field = new DBFField {Name = "F1", DataType = NativeDbType.Numeric}; + var writer = new DBFWriter {Fields = new[] {field}}; + writer.AddRecord(3); + using ( + Stream fos = + File.Open(TestPath(name), + FileMode.OpenOrCreate, + FileAccess.ReadWrite)) + { + writer.Write(fos); + } + } + + + public void ReadSample([CallerMemberName] string name = null) + { + using (var reader = new DBFReader(TestPath(name))) + { + Assert.That(reader.RecordCount, EqualTo(1)); + } + } + } +} \ No newline at end of file diff --git a/DotNetDBF.Test/dbfs/Readme.md b/DotNetDBF.Test/dbfs/Readme.md new file mode 100644 index 0000000..43ae652 --- /dev/null +++ b/DotNetDBF.Test/dbfs/Readme.md @@ -0,0 +1,22 @@ +Most of the DBFs here are from the spec directory of the dbf ruby gem + +Copyright (c) 2006-2017 Keith Morrison + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/DotNetDBF.Test/dbfs/cp1251.dbf b/DotNetDBF.Test/dbfs/cp1251.dbf new file mode 100644 index 0000000000000000000000000000000000000000..eea8b4bb573187e6bfa770b48e2ac7d7defbcd4a GIT binary patch literal 781 zcmXqD=3-}IU|`5#%mk9Cfbk>)Ly#X_%#RVs1gS*^evZDbP`)!OkdcBc31su;rzGj6 zBqgH>k2tOZ5Ewpq^YHWQ2cO=3c>7NG{kzvMUcG+t_Qm(7A78zD3X-Q9Fnajz^&gel9h69byo;ZOaNDBdzcMm^2c>eCugHKO^J_CB|&C@3@zP)%04!Y0ZKRkW*1&cs4n6gxL(dqzL2z=DYP$!kn#f6e<8UO_=F}E76k|92^<6wj zlaTCaQZv$waF7Trhy{U`+D*f{B4wJkuR&{w=oDtM))%5aq)4L z`6es>DUi>u(z?S|`6w^vAFk6f8N2br`zOURncF^2{w5gz04`IR2=(xyhE*8iuv=3&$U)RhGQZ*LgKCYcTjO(0`HNbe{i*Kz^S7lT63IPoa5Y zL@%CoI~e)dBl1yEmWf+rJHKDLw-4UVZy%MPPG^aE2DokO{Egf{gB?Gd|GU+~tiY^F z-TH+3PqMU1CaGD3t@{hQ)4%d-qkpUX-4di#GA-6_eM0>wwbZX)C^zFL<9uG_+s8N0 z_eYlf4f^f+e-o^qkBqLr&Gq6fjfh6`qOibs?0-9GYC6QbL7N zh-g0_j)|wqH5>~~DrVf!#F6feCeWdJqUnD3$){*ixJwhzMfg391`~=rEuNtkO}@qd z8Je_0ks%d|qe-Gj`ujPio+gLmvCyPr%$#k4H=85ghi%d^>B~0lPLq;>ZK4VZ)z~UW zg)z{r!jNdyT9-mZB;rU3Wg8t$z#fNJ(iD!Fr^yX)6f}_-)0kVD7y_CI5>HMO=wJsx zlLGI)VVjujI!6}JM38iJgug(O!VD?zDJ@NsBEcP9K$EEBCd=#2G(A9XsDIb`7mb@p zOhim=+yqNwEIsXkCc@+1;-;N#lTsl~BtjB4y1Yr~HGP35%>`1@IWj|&Vj5{x z+a`i*nkyRwGKcNt<@pXG`;?Cc?swBrz*|qoyL^LLi9`%=%D+j zNr`|af{}{4aS`y*S!43Br_(}3NGW$e*d`_CJgx@?e@AMXfs35wOvg|Ku1Kto^tdVnv~E8Q|}o$4D9k` z)b=38(_=@3F?gyXp&%?1SBa`X76wVlUqITSbwaGSi>7gb~(kdj#7&IcF ziBO5KcKt0)6eH&80-9=1=kl9}WSe@7n*@u6wN1>thLB(B!P5y2X{~+RCMLtU32YU7 zUTFX^7h#6rvA9VKitJY#xX;ieBk*($O#qRzP0aefW1&gJ+{R5d?c0xS5>Cfnztb_M z0^39|L7MsekY_5)FSTd_GesGxzskay4$_y12^S|>nk0-z>Mtt*k*De4@=bl&CJcG% zDCXG2P0W1Y?7=p99ecM;T(ICw2T{3zgM7Pe+NOu|O+>?Z_uuIl<`Ta8*4axYfBp~I C{ML&A literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_03_summary.txt b/DotNetDBF.Test/dbfs/dbase_03_summary.txt new file mode 100644 index 0000000..6172a5c --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_03_summary.txt @@ -0,0 +1,40 @@ + +Database: dbase_03.dbf +Type: (03) dBase III without memo file +Memo File: false +Records: 14 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +Point_ID C 12 0 +Type C 20 0 +Shape C 20 0 +Circular_D C 20 0 +Non_circul C 60 0 +Flow_prese C 20 0 +Condition C 20 0 +Comments C 60 0 +Date_Visit D 8 0 +Time C 10 0 +Max_PDOP N 5 1 +Max_HDOP N 5 1 +Corr_Type C 36 0 +Rcvr_Type C 36 0 +GPS_Date D 8 0 +GPS_Time C 10 0 +Update_Sta C 36 0 +Feat_Name C 20 0 +Datafile C 20 0 +Unfilt_Pos N 10 0 +Filt_Pos N 10 0 +Data_Dicti C 20 0 +GPS_Week N 6 0 +GPS_Second N 12 3 +GPS_Height N 16 3 +Vert_Prec N 16 1 +Horz_Prec N 16 1 +Std_Dev N 16 6 +Northing N 16 3 +Easting N 16 3 +Point_ID N 9 0 diff --git a/DotNetDBF.Test/dbfs/dbase_30.dbf b/DotNetDBF.Test/dbfs/dbase_30.dbf new file mode 100644 index 0000000000000000000000000000000000000000..1d105071969d51767c58654061d0ababf5b68c52 GIT binary patch literal 137775 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

Wbpw4@-q;QQ{+v!BPFmNiH6 zd)L0t-#zc#bG~!VT{wKp;i~6(KR*00u0=WYF3+nsHqUI8T9q@NS2={~Bi^kw+&I)? z=WDA~uXGcBx!?ZoZq2QFeWSA006=99KOXUJc2oLZT5YYBHyal@zc6L~{MKr%RcT$U zuX&|q%-`u9F8qJ1R%z86mC~B$U94c{mLo-c&NnNyW^2={oS%ZP=QnGc4XnR4Wxlb7 zskO$&+6Aw2amxIeO0%>H4QpH8#zUCT4TCY|;aV01>w)Yg-u(CQ{g6j1jpVj4iqrm8 zKkOdwwmYNW|KCP}hAZ`@FW!OhS$-DgMLXJ6iP#m37{bW-^Y4v=9J3aDFYfD};E#Y0 zd;bIc;*yvX3yUJUhGo(;z(QjFuLk~h;L+DVV&D-e-fHM4N_9nZFV2PsEJMu+0^~Wd zygV;B@^Ti6rjkh%`o)(bG(Srx0lz}! zFUy#Qfn53y74yW9>YxI`~=&-2k=!t>HFeb)K;SA53(ZW*f;9Ozb|lWm*?k2 zZ>0XwKfNC_p7(KmH5b^PFHsp}u^fY$H6%RWRvTv#@Ni70{%gNA#`&Z{)>ml%J`B!h zDv98c?O5s%nFUWTBgldnXuXZq&C~TIv$c0&{uA~C_**eF@KqUh`l=U+4Jqm}Nuzyl zl*7;OH_oL$8rHo4JSTu>l%0#h_H2uf-i3CjaTcjPSZ6eYu=<+;z1!S0#RHxj(u%g< z3A&CEI_46!Unp+v(&NTB_gQ$}08c+Ht5AUl`cbxb;4;*nj;SeavB){Q4{^rx9AnZ{ zfIo^`U0w;ID{t>R)SNn=(3GaNH^dAp{o+Xj?;O*H6$x}T@DV}PECs2J+jDYEc)kHh z9>(DYq<&~AZTPy`T1@8xzrO=tN-_>*dtQstq@^DUv*S+sb~0}R7SD3zZ1iz zAB#rR4b>hvtt#Uk8OZJg-*5>Be*$#iZ>I%TKKfloP_Zh5_8*#rZ+4=P*=>qAczBMX z%3XnxR$jPl#kad3AFDlsxSS=a+=k}o!k0S()sAlzXg*(v*5%){V@^<-BZWTZDVFa) zjA1Ja!lxX|oMctdIM`!U9R;$dU^uM-<$;Tgo`OoHaGi**^M*OS!m%m@McGC?%_ ziREQ7W=9&=eCAh}fN(FJz%jW>EvscEDQek^bG0bz$#kw1D|phRkX>n8?e|Ms`(7ZE zgj@B(y(X+SFi#dRnXBbXjbL~HduZXDU zfdETnmY`gC{4b=Jq5z&-fJ&h|w+XzeOfRdXuejKhH(=^vXn_=9dd-;m*0x^frketU zckTgf&(vFv;$9S9gVjY#1%4P!f#y$4Jv!HtkVol6KYjKU1%Iqu+1Iw@u=?5El3&}?C5qY1S32;K4e-i1|U;jQiOBYdh*Vg#K&&!XE7|jf&2$1I2}k>XtpqLXnPI+z_RF!dLQwxCHjl!Q3Z-M z&M^$py2WX%vP6k1D#lVX#KAuRyL38Yh%h&;8=tqG;hF}|vlZ7o4X;F*fZDz-sz~cI z&~@6+ZNEgv85JT={n-%m_bt37*jgR(O~Dy*riVl70Nhh0$$F8_Tov>>eh&7S6{eX4OcyA)a z-HEY1jYr0`@W(N14pmRREx6Dn7vXuNFpu+L!S7w|)9>Fq5GMtV0WXAVU|p~kG=#Z4a-fqV?jqS<66dP8{7}i0PecdYiGb($t4B>Ca2ggOWsvj+X>JS$$ z3hJB{{L+fDIQ3tN$N6RS$I*xy5gytDzSv?MxzA|N(4$!6Jb+>SrSw(N>7&5$i~kuR z>yd+p=j11WZlOn?G@g@a@Vw4BiLd1_>HuAIeGx`sC{Zx56Wu3dLz-YL*XI83&J?IU zHMgGgWegkgimshjQD^BV@rVUqH4&3sR@zAx~|gpcg7 z0bG(hY6mB1si^xn1dOI)<6Rv50_%GdR&kVtID&)B8^>hAbOLyGa%h{Qq39la6}kMN zDv-SZO<*~va0+|&BHzpN9?{o4tZ1ti>JTMl2FmFi? z7++95U(F@^jzuT;mH^aV8)ro~KklS1@2wcw6q*X~r*Qk0zq7-GkY0wDXexXO2H}ma znyfh4{Aj<=o1c*jJ$Hw(J##`GAM5ud(2Xeb6JH{p*0HsFC`Pc!YRD=K?c6B9s5w8n z5YdOc`T4pyvtpul1M^Ox{b`Ix@C0rS5wcyei5_vnIu2fM?Zg@K(VuhHIBpkr>-QxL zH)PfW#OuExv>`T4T(9H%{C z>Gx-Gt47zZ$yfz}B;}WVoN0#Pl-~BBzV_0lF%F+`0{9BX4IFNr1Lw<0R~feq#%ydX zA)_nTty>GF9vKd{QI$aZUi=mkVl#SilohEx-48|Js5G{V PJo_J`y}&%h`2BwY28#NP literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_31_summary.txt b/DotNetDBF.Test/dbfs/dbase_31_summary.txt new file mode 100644 index 0000000..35e98ae --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_31_summary.txt @@ -0,0 +1,20 @@ + +Database: dbase_31.dbf +Type: (31) Visual FoxPro with AutoIncrement field +Memo File: false +Records: 77 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +PRODUCTID I 4 0 +PRODUCTNAM C 40 0 +SUPPLIERID I 4 0 +CATEGORYID I 4 0 +QUANTITYPE C 20 0 +UNITPRICE Y 8 4 +UNITSINSTO I 4 0 +UNITSONORD I 4 0 +REORDERLEV I 4 0 +DISCONTINU L 1 0 +_NullFlags 0 1 0 diff --git a/DotNetDBF.Test/dbfs/dbase_83.dbf b/DotNetDBF.Test/dbfs/dbase_83.dbf new file mode 100644 index 0000000000000000000000000000000000000000..f3744360cdbc25fa9fd3b1d4207b1a3eab3d0c1a GIT binary patch literal 54449 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 literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_83.dbt b/DotNetDBF.Test/dbfs/dbase_83.dbt new file mode 100644 index 0000000000000000000000000000000000000000..6fc14570161a8c4901c6b28923901cfc5b8f9581 GIT binary patch literal 40387 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 literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_83_missing_memo_record_0.yml b/DotNetDBF.Test/dbfs/dbase_83_missing_memo_record_0.yml new file mode 100644 index 0000000..27680cf --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_83_missing_memo_record_0.yml @@ -0,0 +1,16 @@ +--- +- 87 +- 2 +- 0 +- 0 +- 87 +- '1' +- Assorted Petits Fours +- graphics/00000001/t_1.jpg +- graphics/00000001/1.jpg +- 0.0 +- 0.0 +- +- 5.51 +- true +- true diff --git a/DotNetDBF.Test/dbfs/dbase_83_record_0.yml b/DotNetDBF.Test/dbfs/dbase_83_record_0.yml new file mode 100644 index 0000000..40d640f --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_83_record_0.yml @@ -0,0 +1,16 @@ +--- + - 87 + - 2 + - 0 + - 0 + - 87 + - '1' + - Assorted Petits Fours + - graphics/00000001/t_1.jpg + - graphics/00000001/1.jpg + - 0.0 + - 0.0 + - "Our Original assortment...a little taste of heaven for everyone. Let us\r\nselect a special assortment of our chocolate and pastel favorites for you.\r\nEach petit four is its own special hand decorated creation. Multi-layers of\r\nmoist cake with combinations of specialty fillings create memorable cake\r\nconfections. Varietes include; Luscious Lemon, Strawberry Hearts, White\r\nChocolate, Mocha Bean, Roasted Almond, Triple Chocolate, Chocolate Hazelnut,\r\nGrand Orange, Plum Squares, Milk chocolate squares, and Raspberry Blanc." + - 5.51 + - true + - true diff --git a/DotNetDBF.Test/dbfs/dbase_83_record_9.yml b/DotNetDBF.Test/dbfs/dbase_83_record_9.yml new file mode 100644 index 0000000..32fd752 --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_83_record_9.yml @@ -0,0 +1,23 @@ +--- +- 34 +- 1 +- 0 +- 0 +- 34 +- AB01 +- Apricot Brandy Fruitcake +- graphics/00000001/t_AB01.jpg +- graphics/00000001/AB01.jpg +- 37.95 +- 37.95 +- "Once tasted you will understand why we won The\r\nBoston Herald's Fruitcake Taste-off. + Judges liked its generous size,\r\nluscious appearance, moist texture and fruit + to cake ratio ... commented one\r\njudge \"It's a lip Smacker!\" Our signature fruitcake + is baked with carefully\r\nselected ingredients that will be savored until the last + moist crumb is\r\ndevoured each golden slice is brimming with Australian glaced + apricots,\r\ntoasted pecans, candied orange peel, and currants, folded gently into + a\r\nbrandy butter batter and slowly baked to perfection and then generously\r\nimbibed + with \"Holiday Spirits\". Presented in a gift tin. (3lbs. 4oz)" +- 0.0 +- false +- true diff --git a/DotNetDBF.Test/dbfs/dbase_83_schema.txt b/DotNetDBF.Test/dbfs/dbase_83_schema.txt new file mode 100644 index 0000000..803db88 --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_83_schema.txt @@ -0,0 +1,19 @@ +ActiveRecord::Schema.define do + create_table "dbase_83" do |t| + t.column "id", :integer + t.column "catcount", :integer + t.column "agrpcount", :integer + t.column "pgrpcount", :integer + t.column "order", :integer + t.column "code", :string, :limit => 50 + t.column "name", :string, :limit => 100 + t.column "thumbnail", :string, :limit => 254 + t.column "image", :string, :limit => 254 + t.column "price", :float + t.column "cost", :float + t.column "desc", :text + t.column "weight", :float + t.column "taxable", :boolean + t.column "active", :boolean + end +end \ No newline at end of file diff --git a/DotNetDBF.Test/dbfs/dbase_83_summary.txt b/DotNetDBF.Test/dbfs/dbase_83_summary.txt new file mode 100644 index 0000000..24a3483 --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_83_summary.txt @@ -0,0 +1,24 @@ + +Database: dbase_83.dbf +Type: (83) dBase III with memo file +Memo File: true +Records: 67 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +ID N 19 0 +CATCOUNT N 19 0 +AGRPCOUNT N 19 0 +PGRPCOUNT N 19 0 +ORDER N 19 0 +CODE C 50 0 +NAME C 100 0 +THUMBNAIL C 254 0 +IMAGE C 254 0 +PRICE N 13 2 +COST N 13 2 +DESC M 10 0 +WEIGHT N 13 2 +TAXABLE L 1 0 +ACTIVE L 1 0 diff --git a/DotNetDBF.Test/dbfs/dbase_8b.dbf b/DotNetDBF.Test/dbfs/dbase_8b.dbf new file mode 100644 index 0000000000000000000000000000000000000000..9e0ec1363efd7aacb2aa608ef60e41f228a3d1c8 GIT binary patch literal 1826 zcmchXy-ve06op-o5JIq^PE7X+Dm!iSGe&8`kCH}e&A?bl)MjZ!5(*L{Y`g#?ug05j z?L<~;2`1z-Soi4KSD%lSd}K$bCxno1@`bCe$3bxAFZ_Uq3qpcJGQ7!f%b>Syew&1g z>%fl*nXj=wJlOBgd>(e&XRp{l*3QT2WqX>C_znAFc7J{pr#^27MDJq$`F=kMleC+k zd}3eRrGNem(rpf=#YU7e&%uSVJ2tY$ld0{v9%WeB6s3R|@aIxFQ-#&>ArIri=u#jS z@J*GQPH-&wT$vvTqf7CPAu69LWepV*$%jz*z95V)#W8`X*qM$GrX8PnUuk~jo5Heq zQM9HCyXC|5Fh5~*DG(c$`7YloZ>Df0pXbT^gwdri90-f`rcvHZ;YvQ%@(H6$VYo0a z%>MvAL{bQU79MbX-70@5tE`5)DXTn#Vhhcts3BW5tNT^`@1GB&eLgy3dVD?jiy!fz B1Ev4~ literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_8b.dbt b/DotNetDBF.Test/dbfs/dbase_8b.dbt new file mode 100644 index 0000000000000000000000000000000000000000..527663a15c1f59e9735f4cd1d695c22da12fc912 GIT binary patch literal 5120 zcmeHKOHRWu6f|9-N=OJ;B9Sb6fF6M^SRkbI24JWciKS4g-iupti4y1M=~R7j7tNw_ zyU0x6^Lz1(W2aNqe3tVTsL7R@bU0owU4h;1T0K;gT$u8Eo#Z;VH`Ax2|Ni!<`Hi{H zbm(}LBghfURph8tM~dyw;gWhJkCn53nNqheX_~6p?CoH#&j0%=E7R|`_^1~^^q*IY zSB)dc5zDJcj!Jc;n7_*&h5Nr;8C^#I8|7H#UeALa4l@zE5zKuRIm+XZBbKYw-NOA} z*q?H}iqC(2-m%KwIgTJlJnRi;pQA_M{(o1W|DUb>Htzppcr!+i6a3H2Hu~Q^gJXCz zMvp-M-TQy0H@aw70KD__T{7wa#d z|DeFWqO3P^#BLOGxss3aIOK?jdjvlJ?*HGbDRg%L!0b=!$JytYQ|#eY9a>`n{{I8{ Cd(!Oy literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_8b_summary.txt b/DotNetDBF.Test/dbfs/dbase_8b_summary.txt new file mode 100644 index 0000000..7be9219 --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_8b_summary.txt @@ -0,0 +1,15 @@ + +Database: dbase_8b.dbf +Type: (8b) dBase IV with memo file +Memo File: true +Records: 10 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +CHARACTER C 100 0 +NUMERICAL N 20 2 +DATE D 8 0 +LOGICAL L 1 0 +FLOAT F 20 18 +MEMO M 10 0 diff --git a/DotNetDBF.Test/dbfs/dbase_f5.dbf b/DotNetDBF.Test/dbfs/dbase_f5.dbf new file mode 100644 index 0000000000000000000000000000000000000000..ed95dfadb39d516a76fe9e6187d83fb52f985c7a GIT binary patch literal 946697 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 literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/dbase_f5_summary.txt b/DotNetDBF.Test/dbfs/dbase_f5_summary.txt new file mode 100644 index 0000000..6506b37 --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_f5_summary.txt @@ -0,0 +1,68 @@ + +Database: dbase_f5.dbf +Type: (f5) FoxPro with memo file +Memo File: true +Records: 975 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +NF N 5 0 +SEXE C 1 0 +NOM C 20 0 +COG1 C 15 0 +COG2 C 15 0 +TELEFON C 9 0 +RENOM C 15 0 +NFP N 5 0 +NFM N 5 0 +ARXN C 10 0 +DATN D 8 0 +LLON C 15 0 +MUNN C 15 0 +COMN C 15 0 +PROV C 15 0 +PAIN C 15 0 +OFIC C 15 0 +ARXB C 10 0 +DATB D 8 0 +LLOB C 15 0 +MUNB C 15 0 +COMB C 15 0 +PAIB C 15 0 +DRIB C 30 0 +INAB C 30 0 +OFTB C 10 0 +OFNB C 20 0 +AXC1 C 10 0 +DTC1 D 8 0 +LLC1 C 15 0 +NFC1 N 5 0 +TCA1 C 10 0 +OTC1 C 10 0 +ONC1 C 20 0 +AXC2 C 10 0 +DTC2 D 8 0 +LLC2 C 15 0 +NFC2 N 5 0 +TCA2 C 10 0 +OTC2 C 10 0 +ONC2 C 20 0 +AXC3 C 10 0 +DTC3 D 8 0 +LLC3 C 15 0 +NFC3 N 5 0 +TCA3 C 10 0 +OTC3 C 10 0 +ONC3 C 20 0 +ARXD C 10 0 +DATD D 8 0 +LLOD C 15 0 +OFTD C 10 0 +OFND C 20 0 +OBS1 C 70 0 +OBS2 C 70 0 +OBS3 C 70 0 +OBS4 C 70 0 +OBSE M 10 0 +GHD C 15 0 diff --git a/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DBC b/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DBC new file mode 100644 index 0000000000000000000000000000000000000000..d1d1c0717e3065260c775e4e96ccefe6d454cf52 GIT binary patch literal 10123 zcmd^_S##4c5P;2n-}f!$UjRAtQy%WB+vP#bI4$Dyab42qTT zDNFNit2UYSuQK$fzi&To)~vC0(7(R?W2mS7lXoYee+TF<2Kt+=MzhvxKZfRs*H8@p z|5#(Zmi=VnouR)~uTS1@Bzuf~g3an|=X~>Ms@BTt6W^hto}FcF%6I(Fw+x++-*GN^ zmq&uJqQ*#MZRV2n&rxB^p->^)A@?J0OE>Tz1~b)jW{g$|5~6AVa!LlFaGGIY3nvam zG!?|54Hm9S5E6qk{Xf(g#(`?dLC8k;iLhl=h>Rf?hH42C2b{k{j*GtV9pT%qh^jzE zNR$#JjyMR}=3OuKshsauL$k;kzehrl666|C5%j(*JSS=M$p3DPsM!4=e-IC z9rctmMk{SajctHJg?z@nn2Zp}5DTRQxe-*=-C5vNGTfvfYWXTbR3I^lLD0h8d2|y5 zc0!A(97>9w)HXw@LQcD3Bnhud#6l@SZUI$=^f)OaLqL=gKlVhGGV04(TowWkRSz?$#jfAogWQcUc-B_ZT31eeTmG83xF`8l-e(mZMWR+n8YN z(;%M*;nPlkHt>fhaQ2%a#hes=L=r#-cOTFoXM>&?GD{xRAbUaA^$0NymOP|EPV>2r zANRU~D9tcH9@Zd5kGtM*<&#G=$gUg8S%>caZLlQMAd{rGCxmo;f;BbcIC)fq%;z{% zD;Ne#9@8L0F)cz^{OfdS?%xK;;~JzNNYVu>1LO$}vd2Rg63PxHzcIlq!T@~F!IBp>$SAJ(IbD7+W?KW~B@J>26O79M zi4U9V@c3{pITxxtGB+r`-hh(oN$m=h_=u!{q<)U`0l7d*kXJ#)RkZ>#&&;WYAbb%7 zrGOm02C524?e5PBVi}TMhbA1%OO_@Mn$SwI>gWUfI;CVk` literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCT b/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCT new file mode 100644 index 0000000000000000000000000000000000000000..440506285c536137554ae11416405a98222d838d GIT binary patch literal 10688 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 W`k8DzEzQ#UM}IZF!gzQ@!e0WUmN3@< literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCX b/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCX new file mode 100644 index 0000000000000000000000000000000000000000..d962cefe1d553a6d6782e9aa14cd00cf957c20cf GIT binary patch literal 5632 zcmeHKS#KOg5bj+cSq}*|rpIBEfEQxGo&tkSfFv&n@Pdd!jLb@ir%~@r$2-n!&!oF& zc8ARq*Dv62@Bj!2@e4p8&TwDhKE(BgcrrcL&RPM9CyOlG(r9X`NLz4*YRnKS*n+FtRt?QD;Thx*=@+q8Lf26*# zm@*h?(e|Vd<+XjnA~S|SF5k_! zio|||Fv|aVWOakOL^>X;)p$;=^6JgWbNGlrb1CiTDikM)04 z?S~uynR$EA(f<5@xOA7|&d*OyOq5GAkK-rsllUq8wDBsu4nM*D#w+lgal&{Fo;UUx zZ@`=I7Q7Abz`JlB-h=ny1Naa=f{)=7_!K^a&*2NW0AIpa@HKn`-@IM0W)%dEI0N$C4JFkar|xgEZZu zgRiU+Nt#5^GFph+Yl4AUq^^I8s#Gdt`%rl8p|eW|+1y+-gU?-q!>e(4xgH+G;bOH@ z!*aUXS-|{|E)U}##^H51(B~ez9tSrZ@UGN*SK;0P%&XP#HZ8mpM@O~r=1K>u(Q>tO zlOEoRd$-`|b}c%lhsU+>V(;Z<4fc4CNn%OUu@j?`PHz#{RSXd%V=MC{6@4w~w5elf zZYs2GBI)1l9?!fx_wMiHw>(BW9zSE!uB1$;*+V+grBZDrOovIzsgOeWULa^PC~Ybg zPES+LsckkR-xHF!mS@xSh)u_(X0mIWa7T$uC6|GSXDpWpX|^eq4%?Johqb(Rrt5Ge ziB(RMuE~OSQ~kD095>y5Csa)&~!Wwi5t)~?-HNq{6aH}h{9Q#lPN4S zpvA)Y5rWFVPnn{~+8FCXa>9hl0);Qv+Z0~FEt+1@g!B~HeC}D)4%GQ<>QhxGnB`EB PBcRsL`dUp{mE`|lBUs$; literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/calls.CDX b/DotNetDBF.Test/dbfs/foxprodb/calls.CDX new file mode 100644 index 0000000000000000000000000000000000000000..1542679f1d6430d290cd5d5e6bdead1839983513 GIT binary patch literal 6144 zcmeH}L2iRE5Jkt>U}I14D2tw>SBR2LrKsIxpGIU8DM~guKrYpN&(IU}Cd8lsWfd<| zYeqtzff4fm0}nvK5Eg#LP;ip80|MGn^SF~9a)dYu88r9!5lsW&Jt-xjc!fDX9uW@y zua=MZ&0@LPzO9yv^*StI$NekV3kqd>`0+Gn2|8dp_8X{o$K&>}Pf#ICig7@Px4q~8 zmuMt_)>g_)K!y?(=2)OcgC$yYh&aOvJrYjtO)#w|7rA&E2gp}5bhb3Q$d$~l?GIoJ z`+F$(?D(IDn)%fgc5Qfc9K!O9}&^c5p6*O&;IB@|LRKA)bxrp=F9@{IgcdXrYqx9D^;}3{v ziBhg$$x0OGdFP#XW}PI-(ENHxqauZGTy!%hwRu7SIx4#(jiXlkt#Qaz$9c zRc%~jTs1v3aRF(~y#P5{3mN7OSd8Qs_l!~NKl&DWw9vAPwOs@IVs{HY)Wkaf~bY4Mu9_`hXO`Gc<(a}huY9py%>3>JW$rU0|bEmm7q9?oiXl?dp+`}_5x z{CV1OLy|vyLPv4Rc=`R~@}0!%{dx!s0zyb6%|MpPrn6r4XPaKLz>h z^d)q%B3KE2JpOnn#V$YpmFLZVhWt|rt?!^k-%!-BI*TsA>Ui|Mt%&kZ&e)a4w>X5# zrG#L*mR#HhP9~J!JLd?gWCNb$G{^=Q1vpyGr)Sge@86Yw{QjDptiPvSsb4C}KfKfv zGcGW;ouwUo&kBS8nU~SY>@qp;M^G`#^kZL^Th)}`Pp72!zv`utB%QlOegG{6;V1wA literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/calls.dbf b/DotNetDBF.Test/dbfs/foxprodb/calls.dbf new file mode 100644 index 0000000000000000000000000000000000000000..40967473f8c40c93d864a133077d243f4c82c8e4 GIT binary patch literal 5017 zcmd^@KX21O7{)JkfEH*2;!jG6#5=SLQlf5*G;xA(l(d!NBDTbd&tNs=BHL-3iHfhl zz{-fk%!X80`2dv=104}VH)toy`&@#QLO%g_QSNbkKH2*Dz2|)|%b9fUiV)&k>UN6d zRI@B0>|(9fbjmpI;DR)NCY!ys>K1JmmpJFn8pr%{(X9yK&hYs3-}5osfxl(@!VFY@l}fk;?n+d?Mqd;I}&yv&}gqygYp8 zyd`nG_kHT^=S6YES{e)~=z3c!l0J1*5J(vp$doqCL}+S^<}RaoldoDp0|gox_ahm{ zirUia83cytBG9k{=%`s{G_Av|MguiFYAyi{oCXVOzqus#GCLa02UfGuTm~8>0-&jJ z)HJPq?KLe{vlrAX(46NEeghl+`tg-UbHLgdgoz{-m}kLAHFH37VT@*x(HykikI=BL zM`0)IJ)uBF#HqZmV$M`Cc$tu=P@ECc6ryH@(Y$`VuF*ivMuU2hP*eg)Z4SvcZHF+9pPN8~3=TB#n)@$u+G|!=%|??2 z8f0*wf!Ex5SJ7yQ)oe8LK$GDPn+4Q0yY0giyau`v54?O2~E>W}{DXsuL-^XVBf2Xy;L>7Jno*=tQ literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/contacts.CDX b/DotNetDBF.Test/dbfs/foxprodb/contacts.CDX new file mode 100644 index 0000000000000000000000000000000000000000..d0722b0d0b4f2c4462e9b3fc642962551d11fb30 GIT binary patch literal 6144 zcmeH}v2MaJ5QcyIoJI|nVs+p-`U(|gV1W)&5GAwX9xdmCsPb?He>I^V2X!$3iTSjEw`6p8XSS_Mh#?rmc4k8)+is4hM4o zxBv40s5%1jydcsF+LARkvw;WxA;HRl%#i*L7eE(f8NB!W*Z$R`AK|-@8)Ec_Q|5a9 zyMh1h*I_G%0H+S8X&LkXG~&O8ahX$=JRfKbiL_;<>hC*n<^MEpRQMmg;GPNpxpz2C NpYT7;Cx%r$|1ZPROmhGL literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/contacts.FPT b/DotNetDBF.Test/dbfs/foxprodb/contacts.FPT new file mode 100644 index 0000000000000000000000000000000000000000..31b3dfa3d54a62ec6c7ee2b48d964cfa3137d4d2 GIT binary patch literal 960 zcmeH^K}th05Qd*8@CrjW+D)(`=-agxyATAidVn#RIF0q zNW$dHH-EaWJMFq|uJeEV%>+)g?e{ykwJ4D@#B9^nQ3ZrsvoN}c$GW!zCk}h?g-2Km z3c*W`J1RBG9?oa|^@W)NtOo*2mBI43NhIW8@~mfzBETL*mrG6#mY9;69O}<11QvZQ zFgp;eiuPQg5w7&dH|1y(CAoDf2DL1Lvgvh&d-H=K#nxQ*?|b~n4HXc|vjQ`ZwFPD=O2 zaS!|e#82VCfeR8p0CD0MaN>@5?Ium?xQPciK$EG|di=a!^Lob5yyMCr?0o!KlBAz@ zDo^0go%ZOFBw@>RRqXclrlh|G=nniWww7)?Zr@Z5O~SFA;vxvw9d!mvxSD}CHPxP~ zU_S#_n@wADoLB^Z2fzM|uNE5C-B;5t{uTmE`*&Q`)oujv&wzippc`LyEs*G7s|oS{ zmVw*afo_?J68;(L>U_bmIU-)&na z#P>bmpQPeXaP6h5nG*gL@bviFmZ8n`f6vf2tcKo6BqaP4o_E@QOMTflkBx?AOZa&P zt{JM{nLhsmz|;9}=(gK#CeNUG4fwmM_>=izXqu}_?$-%^cYe+0A3SCK4dChdXKO9Z zwzX!zA>nT_{F|1mC9_A8j2{yHy8#%gt-}Q9JFbL(1U#KTr)wQMT0B4r|B_+<26y^8 zG;6`tRKMB`p-_;S3}PBypz3% zj-rBhGPpay4e23Ro03Vw!8H;y%6)<`AHPmJzqkJsv<$+zPw!5YLw_AhvCc`e zUal>XChTrt1)U18rc$p|6j`avNHgBNrhTsO$J*OUbA6t+600&`@pyXh?8cEjiJ<>app?nQ?NVcgL5*X$RHs(%d;Vy(^%d!=($FzTv|bzaO7J_$2`{s zZ%d?k2`k8_Lm|@Km*uJ)e|k+?TxoWE*k{$?4#dbnZ1UM5xr3ptViX78k++xxc e4=UE&R+{2@+iEOk;O1Ski^SLYqLt0L{^&28pQf<@ literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/setup.CDX b/DotNetDBF.Test/dbfs/foxprodb/setup.CDX new file mode 100644 index 0000000000000000000000000000000000000000..92787a1dac70d17e59f5bbec319f4f55d0317d53 GIT binary patch literal 3072 zcmZQzVSocJh6jvr&M0G8hX5l3D5^)Jdss&@5uRfPrh)%ZaF_A_e+CAAegPI1W(GC| zB6N=`8LA=R?HU>H=jiLoz=iOE5km?iGms0yObFpo)-VhKPH@@J$&j5|8K0M!n>q}= zIO-)LLVz8Z+hOJZ1x9|L4tskRHZ~^4EkY}br!W^xA;OGNB|{|y;zJ?>T!a1nLL8k# Kf}I_Ge1ZYmOC<&X literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/setup.dbf b/DotNetDBF.Test/dbfs/foxprodb/setup.dbf new file mode 100644 index 0000000000000000000000000000000000000000..e214c3d87a2c02bcc2f7abca9c4f6735258b194a GIT binary patch literal 526 zcmXruXOUrMU|`5#GzXHXfDtI+?HU>H=jiJSlyPPR5=N*>7{VNVLP3&1;AspbSbz{2 z@TTQg6cpv_rX=Z>q!yRxr6eUIOOF_~g0rKKPp|?hKol4_3eNt1A&$-=Bc<^W(l^W_Hz|;UN<^epU9@qc? literal 0 HcmV?d00001 diff --git a/DotNetDBF.Test/dbfs/foxprodb/types.dbf b/DotNetDBF.Test/dbfs/foxprodb/types.dbf new file mode 100644 index 0000000000000000000000000000000000000000..ebb3bc67106206cc764d098c333fc447fc353c84 GIT binary patch literal 471 zcmXruXOUrIU|`5#GzXHXfRUMj!P(z0#L+n + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + + */ +/** + Base class for DBFReader and DBFWriter. + */ + +using System; +using System.Text; + +namespace DotNetDBF +{ + public abstract class DBFBase + { + + public Encoding CharEncoding { get; set; } = Encoding.GetEncoding("utf-8"); + + public int BlockSize { get; set; } = 512; + + private string _nullSymbol; + public string NullSymbol + { + get => _nullSymbol ?? DBFFieldType.Unknown; + set + { + if (value != null && value.Length != 1) + throw new ArgumentException(nameof(NullSymbol)); + _nullSymbol = value; + } + } + + } +} \ No newline at end of file diff --git a/dotnetdbf/DBFException.cs b/dotnetdbf/DBFException.cs new file mode 100644 index 0000000..0a39d92 --- /dev/null +++ b/dotnetdbf/DBFException.cs @@ -0,0 +1,62 @@ +/* + DBFException + Represents exceptions happen in the JAvaDBF classes. + + This file is part of DotNetDBF packege. + + original author (javadbf): anil@linuxense.com 2004/03/31 + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + + */ + +using System; +using System.IO; + +namespace DotNetDBF +{ + public class DBTException : DBFException + { + + public DBTException(string msg) : base(msg) + { + } + + public DBTException(string msg, Exception internalException) + : base(msg, internalException) + { + } + } + + public class DBFRecordException : DBFException + { + public int Record { get; } + + public DBFRecordException(string msg, int record) : base(msg) + { + Record = record; + } + + public DBFRecordException(string msg, Exception internalException) + : base(msg, internalException) + { + } + } + + public class DBFException : IOException + { + public DBFException() : base() + { + } + + public DBFException(string msg) : base(msg) + { + } + + public DBFException(string msg, Exception internalException) + : base(msg, internalException) + { + } + } +} \ No newline at end of file diff --git a/dotnetdbf/DBFField.cs b/dotnetdbf/DBFField.cs new file mode 100644 index 0000000..f432ada --- /dev/null +++ b/dotnetdbf/DBFField.cs @@ -0,0 +1,309 @@ +/* + DBFField + Class represents a "field" (or column) definition of a DBF data structure. + + This file is part of DotNetDBF packege. + + original author (javadbf): anil@linuxense.com 2004/03/31 + + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + + + */ + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace DotNetDBF +{ + [DebuggerDisplay("Field:{Name}, Length:{FieldLength}")] + public class DBFField + { + public const int SIZE = 32; + public byte dataType; /* 11 */ + public byte decimalCount; /* 17 */ + public int fieldLength; /* 16 */ + public byte[] fieldName = new byte[11]; /* 0-10*/ + public byte indexFieldFlag; /* 31 */ + + /* other class variables */ + public int nameNullIndex = 0; + public int reserv1; /* 12-15 */ + public short reserv2; /* 18-19 */ + public short reserv3; /* 21-22 */ + public byte[] reserv4 = new byte[7]; /* 24-30 */ + public byte setFieldsFlag; /* 23 */ + public byte workAreaId; /* 20 */ + + public DBFField() + { + } + + public DBFField(string fieldName, NativeDbType type) + { + Name = fieldName; + DataType = type; + } + + public DBFField(string fieldName, + NativeDbType type, + int fieldLength) + { + Name = fieldName; + DataType = type; + FieldLength = fieldLength; + } + + public DBFField(string fieldName, + NativeDbType type, + int fieldLength, + int decimalCount) + { + Name = fieldName; + DataType = type; + FieldLength = fieldLength; + DecimalCount = decimalCount; + } + + public int Size => SIZE; + + /** + Returns the name of the field. + + @return Name of the field as String. + */ + + public string Name + { + get => Encoding.ASCII.GetString(fieldName, 0, nameNullIndex); + set + { + if (value == null) + { + throw new ArgumentException("Field name cannot be null"); + } + + if (value.Length == 0 + || value.Length > 10) + { + throw new ArgumentException( + "Field name should be of length 0-10"); + } + + fieldName = Encoding.ASCII.GetBytes(value); + nameNullIndex = fieldName.Length; + } + } + + /** + Returns the data type of the field. + + @return Data type as byte. + */ + + public Type Type => Utils.TypeForNativeDBType(DataType); + + + public NativeDbType DataType + { + get => (NativeDbType)dataType; + set + { + switch (value) + { + case NativeDbType.Date: + fieldLength = 8; /* fall through */ + goto default; + case NativeDbType.Memo: + fieldLength = 10; + goto default; + case NativeDbType.Logical: + fieldLength = 1; + goto default; + default: + dataType = (byte)value; + break; + } + } + } + + /** + Returns field length. + + @return field length as int. + */ + + public int FieldLength + { + get + { + if (DataType == NativeDbType.Char) + { + return fieldLength + (decimalCount * 256); + } + + return fieldLength; + } + /** + Length of the field. + This method should be called before calling setDecimalCount(). + + @param Length of the field as int. + */ + set + { + if (value <= 0) + { + throw new ArgumentException( + "Field length should be a positive number"); + } + + switch (DataType) + { + case NativeDbType.Date: + case NativeDbType.Memo: + case NativeDbType.Logical: + throw new NotSupportedException( + "Cannot set length on this type of field"); + case NativeDbType.Char when value > 255: + fieldLength = value % 256; + decimalCount = (byte) (value / 256); + return; + default: + fieldLength = value; + break; + } + } + } + + /** + Returns the decimal part. This is applicable + only if the field type if of numeric in nature. + + If the field is specified to hold integral values + the value returned by this method will be zero. + + @return decimal field size as int. + */ + + public int DecimalCount + { + get => decimalCount; + /** + Sets the decimal place size of the field. + Before calling this method the size of the field + should be set by calling setFieldLength(). + + @param Size of the decimal field. + */ + set + { + if (value < 0) + { + throw new ArgumentException( + "Decimal length should be a positive number"); + } + + if (value > fieldLength) + { + throw new ArgumentException( + "Decimal length should be less than field length"); + } + + decimalCount = (byte) value; + } + } + + public bool Read(BinaryReader reader) + { + var t_byte = reader.ReadByte(); /* 0 */ + if (t_byte == DBFFieldType.EndOfField) + { + //System.out.println( "End of header found"); + return false; + } + + reader.Read(fieldName, 1, 10); /* 1-10 */ + fieldName[0] = t_byte; + + for (var i = 0; i < fieldName.Length; i++) + { + if (fieldName[i] + == 0) + { + nameNullIndex = i; + break; + } + } + + dataType = reader.ReadByte(); /* 11 */ + reserv1 = reader.ReadInt32(); /* 12-15 */ + fieldLength = reader.ReadByte(); /* 16 */ + decimalCount = reader.ReadByte(); /* 17 */ + reserv2 = reader.ReadInt16(); /* 18-19 */ + workAreaId = reader.ReadByte(); /* 20 */ + reserv3 = reader.ReadInt16(); /* 21-22 */ + setFieldsFlag = reader.ReadByte(); /* 23 */ + reader.Read(reserv4, 0, 7); /* 24-30 */ + indexFieldFlag = reader.ReadByte(); /* 31 */ + return true; + } + + /** + Writes the content of DBFField object into the stream as per + DBF format specifications. + + @param os OutputStream + @throws IOException if any stream related issues occur. + */ + + public void Write(BinaryWriter writer) + { + // Field Name + writer.Write(fieldName); /* 0-10 */ + writer.Write(new byte[11 - fieldName.Length], + 0, + 11 - fieldName.Length); + + // data type + writer.Write(dataType); /* 11 */ + writer.Write(reserv1); /* 12-15 */ + writer.Write((byte) fieldLength); /* 16 */ + writer.Write(decimalCount); /* 17 */ + writer.Write(reserv2); /* 18-19 */ + writer.Write(workAreaId); /* 20 */ + writer.Write(reserv3); /* 21-22 */ + writer.Write(setFieldsFlag); /* 23 */ + writer.Write(reserv4); /* 24-30*/ + writer.Write(indexFieldFlag); /* 31 */ + } + + /** + Creates a DBFField object from the data read from the given DataInputStream. + + The data in the DataInputStream object is supposed to be organised correctly + and the stream "pointer" is supposed to be positioned properly. + + @param in DataInputStream + @return Returns the created DBFField object. + @throws IOException If any stream reading problems occurs. + */ + + internal static DBFField CreateField(BinaryReader reader) + { + var field = new DBFField(); + if (field.Read(reader)) + { + return field; + } + else + { + return null; + } + } + } +} \ No newline at end of file diff --git a/dotnetdbf/DBFFieldType.cs b/dotnetdbf/DBFFieldType.cs new file mode 100644 index 0000000..1f226b0 --- /dev/null +++ b/dotnetdbf/DBFFieldType.cs @@ -0,0 +1,86 @@ +/* + DBFFieldType + Class for reading the records assuming that the given + InputStream comtains DBF data. + + This file is part of DotNetDBF packege. + + author (DotNetDBF): Jay Tuley 6/28/2007 + + License: LGPL (http://www.gnu.org/copyleft/lesser.html) + + */ + +using System.Data; + + +namespace DotNetDBF +{ + public enum NativeDbType : byte + { + Autoincrement = (byte) 0x2B, //+ in ASCII + Timestamp = (byte) 0x40, //@ in ASCII + Binary = (byte) 0x42, //B in ASCII + Char = (byte) 0x43, //C in ASCII + Date = (byte) 0x44, //D in ASCII + Float = (byte) 0x46, //F in ASCII + Ole = (byte) 0x47, //G in ASCII + Long = (byte) 0x49, //I in ASCII + Logical = (byte) 0x4C, //L in ASCII + Memo = (byte) 0x4D, //M in ASCII + Numeric = (byte) 0x4E, //N in ASCII + Double = (byte) 0x4F, //O in ASCII + } + + public static class DBFFieldType + { + public const byte EndOfData = 0x1A; //^Z End of File + public const byte EndOfField = 0x0D; //End of Field + public const byte False = 0x46; //F in Ascii + public const byte Space = 0x20; //Space in ascii + public const byte True = 0x54; //T in ascii + public const byte UnknownByte = 0x3F; //Unknown Bool value + public const string Unknown = "?"; //Unknown value + + public static DbType FromNative(NativeDbType @byte) + { + switch (@byte) + { + case NativeDbType.Char: + return DbType.AnsiStringFixedLength; + case NativeDbType.Logical: + return DbType.Boolean; + case NativeDbType.Numeric: + return DbType.Decimal; + case NativeDbType.Date: + return DbType.Date; + case NativeDbType.Float: + return DbType.Decimal; + case NativeDbType.Memo: + return DbType.AnsiString; + default: + return DbType.Object; + } + } + + public static NativeDbType FromDbType(DbType dbType) + { + switch (dbType) + { + case DbType.AnsiStringFixedLength: + return NativeDbType.Char; + case DbType.Boolean: + return NativeDbType.Logical; + case DbType.Decimal: + return NativeDbType.Numeric; + case DbType.Date: + return NativeDbType.Date; + case DbType.AnsiString: + return NativeDbType.Memo; + default: + throw new DBFException( + $"Unsupported Type {dbType}"); + } + } + } +} \ No newline at end of file diff --git a/dotnetdbf/DBFHeader.cs b/dotnetdbf/DBFHeader.cs new file mode 100644 index 0000000..a1c070f --- /dev/null +++ b/dotnetdbf/DBFHeader.cs @@ -0,0 +1,182 @@ +/* + DBFHeader + Class for reading the metadata assuming that the given + InputStream carries DBF data. + + This file is part of DotNetDBF packege. + + original author (javadbf): anil@linuxense.com 2004/03/31 + + License: LGPL (http://www.gnu.org/copyleft/lesser.html) + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + + + */ + +using System; +using System.Collections.Generic; +using System.IO; + +namespace DotNetDBF +{ + [Obsolete("Use DBFSignature instead", error:true)] + public static class DBFSigniture + { + public const byte NotSet = 0, + WithMemo = 0x80, + DBase3 = 0x03, + DBase3WithMemo = DBase3 | WithMemo; + } + + public static class DBFSignature + { + public const byte NotSet = 0, + WithMemo = 0x80, + DBase3 = 0x03, + DBase3WithMemo = DBase3 | WithMemo; + } + + [Flags] + public enum MemoFlags : byte + { + } + + + public class DBFHeader + { + public const byte HeaderRecordTerminator = 0x0D; + + internal byte Signature { get; set; } /* 0 */ + internal byte Year { set; get; } /* 1 */ + internal byte Month { set; get; } /* 2 */ + internal byte Day { set; get; } /* 3 */ + internal int NumberOfRecords { set; get; } /* 4-7 */ + internal short HeaderLength { set; get; } /* 8-9 */ + internal short RecordLength { set; get; } /* 10-11 */ + private short _reserv1; /* 12-13 */ + private byte _incompleteTransaction; /* 14 */ + private byte _encryptionFlag; /* 15 */ + private int _freeRecordThread; /* 16-19 */ + private int _reserv2; /* 20-23 */ + private int _reserv3; /* 24-27 */ + private byte _mdxFlag; /* 28 */ + internal byte LanguageDriver { get; set; } /* 29 */ + private short _reserv4; /* 30-31 */ + internal DBFField[] FieldArray { set; get; } /* each 32 bytes */ + + + public DBFHeader() + { + Signature = DBFSignature.DBase3; + } + + + + internal short Size => (short) (sizeof(byte) + + sizeof(byte) + sizeof(byte) + sizeof(byte) + + sizeof(int) + + sizeof(short) + + sizeof(short) + + sizeof(short) + + sizeof(byte) + + sizeof(byte) + + sizeof(int) + + sizeof(int) + + sizeof(int) + + sizeof(byte) + + sizeof(byte) + + sizeof(short) + + (DBFField.SIZE * FieldArray.Length) + + sizeof(byte)); + + internal short RecordSize + { + get + { + var tRecordLength = 0; + for (var i = 0; i < FieldArray.Length; i++) + { + tRecordLength += FieldArray[i].FieldLength; + } + + return (short) (tRecordLength + 1); + } + } + + internal void Read(BinaryReader dataInput) + { + Signature = dataInput.ReadByte(); /* 0 */ + Year = dataInput.ReadByte(); /* 1 */ + Month = dataInput.ReadByte(); /* 2 */ + Day = dataInput.ReadByte(); /* 3 */ + NumberOfRecords = dataInput.ReadInt32(); /* 4-7 */ + + HeaderLength = dataInput.ReadInt16(); /* 8-9 */ + RecordLength = dataInput.ReadInt16(); /* 10-11 */ + + _reserv1 = dataInput.ReadInt16(); /* 12-13 */ + _incompleteTransaction = dataInput.ReadByte(); /* 14 */ + _encryptionFlag = dataInput.ReadByte(); /* 15 */ + _freeRecordThread = dataInput.ReadInt32(); /* 16-19 */ + _reserv2 = dataInput.ReadInt32(); /* 20-23 */ + _reserv3 = dataInput.ReadInt32(); /* 24-27 */ + _mdxFlag = dataInput.ReadByte(); /* 28 */ + LanguageDriver = dataInput.ReadByte(); /* 29 */ + _reserv4 = dataInput.ReadInt16(); /* 30-31 */ + + + var v_fields = new List(); + + var field = DBFField.CreateField(dataInput); /* 32 each */ + while (field != null) + { + v_fields.Add(field); + field = DBFField.CreateField(dataInput); + } + + FieldArray = v_fields.ToArray(); + //System.out.println( "Number of fields: " + _fieldArray.length); + } + + internal void Write(BinaryWriter dataOutput) + { + dataOutput.Write(Signature); /* 0 */ + var tNow = DateTime.Now; + Year = (byte) (tNow.Year - 1900); + Month = (byte) (tNow.Month); + Day = (byte) (tNow.Day); + + dataOutput.Write(Year); /* 1 */ + dataOutput.Write(Month); /* 2 */ + dataOutput.Write(Day); /* 3 */ + + //System.out.println( "Number of records in O/S: " + numberOfRecords); + dataOutput.Write(NumberOfRecords); /* 4-7 */ + + HeaderLength = Size; + dataOutput.Write(HeaderLength); /* 8-9 */ + + RecordLength = RecordSize; + dataOutput.Write(RecordLength); /* 10-11 */ + + dataOutput.Write(_reserv1); /* 12-13 */ + dataOutput.Write(_incompleteTransaction); /* 14 */ + dataOutput.Write(_encryptionFlag); /* 15 */ + dataOutput.Write(_freeRecordThread); /* 16-19 */ + dataOutput.Write(_reserv2); /* 20-23 */ + dataOutput.Write(_reserv3); /* 24-27 */ + + dataOutput.Write(_mdxFlag); /* 28 */ + dataOutput.Write(LanguageDriver); /* 29 */ + dataOutput.Write(_reserv4); /* 30-31 */ + + foreach (var field in FieldArray) + { + field.Write(dataOutput); + } + + dataOutput.Write(HeaderRecordTerminator); /* n+1 */ + } + } +} \ No newline at end of file diff --git a/dotnetdbf/DBFReader.cs b/dotnetdbf/DBFReader.cs new file mode 100644 index 0000000..6782f67 --- /dev/null +++ b/dotnetdbf/DBFReader.cs @@ -0,0 +1,487 @@ +/* + DBFReader + Class for reading the records assuming that the given + InputStream contains DBF data. + + This file is part of DotNetDBF package. + + original author (javadbf): anil@linuxense.com 2004/03/31 + + License: LGPL (http://www.gnu.org/copyleft/lesser.html) + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + */ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using System.Linq; + +namespace DotNetDBF +{ + public class DBFReader : DBFBase, IDisposable + { + private BinaryReader _dataInputStream; + private DBFHeader _header; + private Stream _dataMemo; + + private string _dataMemoLoc; + + private int[] _selectFields = new int[] {}; + private int[] _orderedSelectFields = new int[] {}; + /* Class specific variables */ + private bool _isClosed = true; + + + /** + Initializes a DBFReader object. + + When this constructor returns the object + will have completed reading the header (meta date) and + header information can be queried there on. And it will + be ready to return the first row. + + @param InputStream where the data is read from. + */ + + + public void SetSelectFields(params string[] aParams) + { + _selectFields = + aParams.Select( + it => + Array.FindIndex(_header.FieldArray, + jt => jt.Name.Equals(it, StringComparison.OrdinalIgnoreCase))).ToArray(); + _orderedSelectFields = _selectFields.OrderBy(it => it).ToArray(); + } + + public DBFField[] GetSelectFields() + { + return _selectFields.Any() + ? _selectFields.Select(it => _header.FieldArray[it]).ToArray() + : _header.FieldArray; + } + + + public DBFReader(string anIn) + { + try + { + _dataInputStream = new BinaryReader( + File.Open(anIn, + FileMode.Open, + FileAccess.Read, + FileShare.Read) + ); + + var dbtPath = Path.ChangeExtension(anIn, "dbt"); + if (File.Exists(dbtPath)) + { + _dataMemoLoc = dbtPath; + } + + _isClosed = false; + _header = new DBFHeader(); + _header.Read(_dataInputStream); + + /* it might be required to leap to the start of records at times */ + var t_dataStartIndex = _header.HeaderLength + - (32 + (32 * _header.FieldArray.Length)) + - 1; + if (t_dataStartIndex > 0) + { + _dataInputStream.ReadBytes((t_dataStartIndex)); + } + } + catch (IOException ex) + { + throw new DBFException("Failed To Read DBF", ex); + } + } + + public DBFReader(Stream anIn) + { + try + { + _dataInputStream = new BinaryReader(anIn); + _isClosed = false; + _header = new DBFHeader(); + _header.Read(_dataInputStream); + + /* it might be required to leap to the start of records at times */ + var t_dataStartIndex = _header.HeaderLength + - (32 + (32 * _header.FieldArray.Length)) + - 1; + if (t_dataStartIndex > 0) + { + _dataInputStream.ReadBytes((t_dataStartIndex)); + } + } + catch (IOException e) + { + throw new DBFException("Failed To Read DBF", e); + } + } + + /** + Returns the number of records in the DBF. + */ + + public int RecordCount => _header.NumberOfRecords; + + /** + Returns the asked Field. In case of an invalid index, + it returns a ArrayIndexOutOfBoundsException. + + @param index. Index of the field. Index of the first field is zero. + */ + + public DBFField[] Fields => _header.FieldArray; + + #region IDisposable Members + + ///

Performs application-defined tasks associated with freeing, releasing, + /// or resetting unmanaged resources. + /// 2 + public void Dispose() + { + Close(); + } + + #endregion + + + public string DataMemoLoc + { + get => _dataMemoLoc; + set => _dataMemoLoc = value; + } + + + + public delegate Stream LazyStream(); + + private Stream _loadedStream; + + private LazyStream GetLazyStreamFromLocation() + { + + if (_dataMemo == null && !string.IsNullOrEmpty(_dataMemoLoc)) + { + return () => _loadedStream ?? + (_loadedStream = File.Open(_dataMemoLoc, FileMode.Open, FileAccess.Read, + FileShare.Read)); + } + if (_dataMemo != null) + { + return () => _dataMemo; + } + return null; + } + + public Stream DataMemo + { + get => _dataMemo; + set => _dataMemo = value; + } + + public override string ToString() + { + var sb = + new StringBuilder(_header.Year + "/" + _header.Month + "/" + + _header.Day + "\n" + + "Total records: " + _header.NumberOfRecords + + "\nHeader length: " + _header.HeaderLength + + ""); + + for (var i = 0; i < _header.FieldArray.Length; i++) + { + sb.Append(_header.FieldArray[i].Name); + sb.Append("\n"); + } + + return sb.ToString(); + } + + public void Close() + { + + + _loadedStream?.Close(); + _dataMemo?.Close(); + _dataInputStream.Close(); + + _dataMemo?.Dispose(); + + + + _isClosed = true; + } + + /** + Reads the returns the next row in the DBF stream. + @returns The next row as an Object array. Types of the elements + these arrays follow the convention mentioned in the class description. + */ + + public object[] NextRecord(bool throwOnParsingError = true) + { + return NextRecord(_selectFields, _orderedSelectFields, throwOnParsingError); + } + + internal object[] NextRecord(IEnumerable selectIndexes, IList sortedIndexes, bool throwOnParsingError = true) + { + if (_isClosed) + { + throw new DBFException("Source is not open"); + } + var tOrderdSelectIndexes = sortedIndexes; + + var recordObjects = new object[_header.FieldArray.Length]; + + try + { + var isDeleted = false; + do + { + if (isDeleted) + { + _dataInputStream.ReadBytes(_header.RecordLength - 1); + } + + int t_byte = _dataInputStream.ReadByte(); + if (t_byte == DBFFieldType.EndOfData) + { + return null; + } + + isDeleted = (t_byte == '*'); + } while (isDeleted); + + var j = 0; + var k = -1; + for (var i = 0; i < _header.FieldArray.Length; i++) + { + if (tOrderdSelectIndexes.Count == j && j != 0 + || + (tOrderdSelectIndexes.Count > j && tOrderdSelectIndexes[j] > i && tOrderdSelectIndexes[j] != k)) + { + _dataInputStream.BaseStream.Seek(_header.FieldArray[i].FieldLength, SeekOrigin.Current); + continue; + } + if (tOrderdSelectIndexes.Count > j) + k = tOrderdSelectIndexes[j]; + j++; + + + switch (_header.FieldArray[i].DataType) + { + case NativeDbType.Char: + + var b_array = new byte[ + _header.FieldArray[i].FieldLength + ]; + _dataInputStream.Read(b_array, 0, b_array.Length); + + recordObjects[i] = CharEncoding.GetString(b_array).TrimEnd(); + break; + + case NativeDbType.Date: + + var t_byte_year = new byte[4]; + _dataInputStream.Read(t_byte_year, + 0, + t_byte_year.Length); + + var t_byte_month = new byte[2]; + _dataInputStream.Read(t_byte_month, + 0, + t_byte_month.Length); + + var t_byte_day = new byte[2]; + _dataInputStream.Read(t_byte_day, + 0, + t_byte_day.Length); + + try + { + var tYear = CharEncoding.GetString(t_byte_year); + var tMonth = CharEncoding.GetString(t_byte_month); + var tDay = CharEncoding.GetString(t_byte_day); + + if (int.TryParse(tYear, out var tIntYear) && + int.TryParse(tMonth, out var tIntMonth) && + int.TryParse(tDay, out var tIntDay)) + { + recordObjects[i] = new DateTime( + tIntYear, + tIntMonth, + tIntDay); + } + else + { + recordObjects[i] = null; + } + } + catch (ArgumentOutOfRangeException) + { + /* this field may be empty or may have improper value set */ + recordObjects[i] = null; + } + + break; + + case NativeDbType.Float: + + try + { + var t_float = new byte[ + _header.FieldArray[i].FieldLength + ]; + _dataInputStream.Read(t_float, 0, t_float.Length); + var tParsed = CharEncoding.GetString(t_float); + var tLast = tParsed.Substring(tParsed.Length - 1); + if (tParsed.Length > 0 + && tLast != " " + && tLast != NullSymbol) + { + // + // A Float in FoxPro has 20 significant digits, since it is + // stored as a string with possible E-postfix notation. + // An IEEE 754 float or double can not handle this number of digits + // correctly. Therefor the only correct implementation is to use a decimal. + // + recordObjects[i] = decimal.Parse(tParsed, + NumberStyles.Float | NumberStyles.AllowLeadingWhite, + NumberFormatInfo.InvariantInfo); + } + else + { + recordObjects[i] = null; + } + } + catch (FormatException e) + { + if (throwOnParsingError) + throw new DBFException("Failed to parse Float", e); + + recordObjects[i] = default(decimal); + } + + break; + + case NativeDbType.Numeric: + + try + { + var t_numeric = new byte[ + _header.FieldArray[i].FieldLength + ]; + _dataInputStream.Read(t_numeric, + 0, + t_numeric.Length); + var tParsed = + CharEncoding.GetString(t_numeric); + var tLast = tParsed.Substring(tParsed.Length - 1); + if (tParsed.Length > 0 + && tLast != " " + && tLast != NullSymbol) + { + recordObjects[i] = decimal.Parse(tParsed, + NumberStyles.Float | NumberStyles.AllowLeadingWhite, + NumberFormatInfo.InvariantInfo); + } + else + { + recordObjects[i] = null; + } + } + catch (FormatException e) + { + if (throwOnParsingError) + throw new DBFException( + "Failed to parse Number", e); + + recordObjects[i] = default(decimal); + } + + break; + + case NativeDbType.Logical: + + var t_logical = _dataInputStream.ReadByte(); + //todo find out whats really valid + if (t_logical == 'Y' || t_logical == 't' + || t_logical == 'T' + || t_logical == 't') + { + recordObjects[i] = true; + } + else if (t_logical == DBFFieldType.UnknownByte) + { + recordObjects[i] = DBNull.Value; + } + else + { + recordObjects[i] = false; + } + break; + + case NativeDbType.Memo: + if ( + + string.IsNullOrEmpty(_dataMemoLoc) && + + _dataMemo is null) + { + throw new Exception("Memo Location Not Set"); + } + + + var rawMemoPointer = _dataInputStream.ReadBytes(_header.FieldArray[i].FieldLength); + var memoPointer = CharEncoding.GetString(rawMemoPointer); + if (string.IsNullOrEmpty(memoPointer)) + { + recordObjects[i] = DBNull.Value; + break; + } + + if (!long.TryParse(memoPointer, out var tBlock)) + { + //Because Memo files can vary and are often the least important data, + //we will return null when it doesn't match our format. + recordObjects[i] = DBNull.Value; + break; + } + + + recordObjects[i] = new MemoValue(tBlock, this, + _dataMemoLoc, + GetLazyStreamFromLocation()); + break; + default: + { + byte[] data = _dataInputStream.ReadBytes(_header.FieldArray[i].FieldLength); + + recordObjects[i] = data != null ? (object)data : DBNull.Value; + + break; + } + } + } + } + catch (EndOfStreamException) + { + return null; + } + catch (IOException e) + { + throw new DBFException("Problem Reading File", e); + } + + return selectIndexes.Any() ? selectIndexes.Select(it => recordObjects[it]).ToArray() : recordObjects; + } + } +} diff --git a/dotnetdbf/DBFWriter.cs b/dotnetdbf/DBFWriter.cs new file mode 100644 index 0000000..552484a --- /dev/null +++ b/dotnetdbf/DBFWriter.cs @@ -0,0 +1,528 @@ +/* + DBFWriter + Class for defining a DBF structure and addin data to that structure and + finally writing it to an OutputStream. + + This file is part of DotNetDBF packege. + + original author (javadbf): anil@linuxense.com 2004/03/31 + + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace DotNetDBF +{ + public class DBFWriter : DBFBase, IDisposable + { + private DBFHeader header; + private Stream raf; + private int recordCount; + private List v_records = new List(); + private Stream _dataMemo; + + private string _dataMemoLoc; + + /// Creates an empty Object. + public DBFWriter() + { + header = new DBFHeader(); + } + + + /// Creates a DBFWriter which can append to records to an existing DBF file. + /// @param dbfFile. The file passed in should be a valid DBF file. + /// @exception Throws DBFException if the passed in file does exist but not a valid DBF file, or if an IO error occurs. + public DBFWriter(string dbfFile) + { + try + { + raf = + File.Open(dbfFile, + FileMode.OpenOrCreate, + FileAccess.ReadWrite); + + DataMemoLoc = Path.ChangeExtension(dbfFile, "dbt"); + + /* before proceeding check whether the passed in File object + is an empty/non-existent file or not. + */ + if (raf.Length == 0) + { + header = new DBFHeader(); + return; + } + + header = new DBFHeader(); + header.Read(new BinaryReader(raf)); + + /* position file pointer at the end of the raf */ + raf.Seek(-1, SeekOrigin.End); + /* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */ + var lastByte = raf.ReadByte(); /* Advances to end of stream */ + if (lastByte == DBFFieldType.EndOfData) + { + raf.Seek(-1, SeekOrigin.End); + } + } + catch (FileNotFoundException e) + { + throw new DBFException("Specified file is not found. ", e); + } + catch (IOException e) + { + throw new DBFException(" while reading header", e); + } + recordCount = header.NumberOfRecords; + } + + public DBFWriter(Stream dbfFile) + { + raf = dbfFile; + + /* before proceeding check whether the passed in File object + is an empty/non-existent file or not. + */ + if (raf.Length == 0) + { + header = new DBFHeader(); + return; + } + + header = new DBFHeader(); + header.Read(new BinaryReader(raf)); + + /* position file pointer at the end of the raf */ + raf.Seek(-1, SeekOrigin.End); + /* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */ + var lastByte = raf.ReadByte(); /* Advances to end of stream */ + if (lastByte == DBFFieldType.EndOfData) + { + raf.Seek(-1, SeekOrigin.End); + } + + recordCount = header.NumberOfRecords; + } + + public byte Signature + { + get => header.Signature; + set => header.Signature = value; + } + + + + public string DataMemoLoc + { + get => _dataMemoLoc; + set + { + _dataMemoLoc = value; + + _dataMemo?.Close(); + _dataMemo = File.Open(_dataMemoLoc, + FileMode.OpenOrCreate, + FileAccess.ReadWrite); + } + } + + public Stream DataMemo + { + get => _dataMemo; + set => _dataMemo = value; + } + + public byte LanguageDriver + { + set + { + if (header.LanguageDriver != 0x00) + { + throw new DBFException("LanguageDriver has already been set"); + } + + header.LanguageDriver = value; + } + } + + + public DBFField[] Fields + { + get => header.FieldArray; + + + set + { + if (header.FieldArray != null) + { + throw new DBFException("Fields has already been set"); + } + + if (value == null + || value.Length == 0) + { + throw new DBFException("Should have at least one field"); + } + + for (var i = 0; i < value.Length; i++) + { + if (value[i] == null) + { + throw new DBFException("Field " + (i + 1) + " is null"); + } + } + + header.FieldArray = value; + + try + { + if (raf != null + && raf.Length == 0) + { + /* + this is a new/non-existent file. So write header before proceeding + */ + header.Write(new BinaryWriter(raf)); + } + } + catch (IOException e) + { + throw new DBFException("Error accessing file", e); + } + } + } + + #region IDisposable Members + + /// Performs application-defined tasks associated with freeing, releasing, + /// or resetting unmanaged resources. + /// 2 + public void Dispose() + { + Close(); + } + + #endregion + + /** + Add a record. + */ + + public void WriteRecord(params object[] values) + { + if (raf == null) + { + throw new DBFException( + "Not initialized with file for WriteRecord use, use AddRecord instead"); + } + AddRecord(values, true); + } + + public void AddRecord(params object[] values) + { + if (raf != null) + { + throw new DBFException( + "Appending to a file, requires using WriteRecord instead"); + } + AddRecord(values, false); + } + + private void AddRecord(object[] values, bool writeImmediately) + { + if (header.FieldArray == null) + { + throw new DBFException( + "Fields should be set before adding records"); + } + + if (values == null) + { + throw new DBFException("Null cannot be added as row"); + } + + if (values.Length + != header.FieldArray.Length) + { + throw new DBFException( + "Invalid record. Invalid number of fields in row"); + } + + for (var i = 0; i < header.FieldArray.Length; i++) + { + var fld = header.FieldArray[i]; + var val = values[i]; + + if (val is null || val is DBNull) + { + continue; + } + + void ThrowErrorIfNot() + { + if (!(val is T)) + { + throw new DBFRecordException($"Invalid value '{val}' for field {i}({fld.Name}{fld.DataType})", 0); + } + } + + switch (fld.DataType) + { + case NativeDbType.Char: + //ignore all objects have ToString() + break; + + case NativeDbType.Logical: + ThrowErrorIfNot(); + break; + + case NativeDbType.Numeric: + ThrowErrorIfNot(); + break; + + case NativeDbType.Date: + ThrowErrorIfNot(); + break; + + case NativeDbType.Float: + ThrowErrorIfNot(); + break; + case NativeDbType.Memo: + ThrowErrorIfNot(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + if (!writeImmediately) + { + v_records.Add(values); + } + else + { + try + { + WriteRecord(new BinaryWriter(raf), values); + recordCount++; + } + catch (IOException e) + { + throw new DBFException( + "Error occured while writing record. ", e); + } + } + } + + ///Writes the set data to the OutputStream. + public void Write(Stream tOut) + { + try + { + var outStream = new BinaryWriter(tOut); + + header.NumberOfRecords = v_records.Count; + header.Write(outStream); + + /* Now write all the records */ + var t_recCount = v_records.Count; + for (var i = 0; i < t_recCount; i++) + { + /* iterate through records */ + + var t_values = (object[]) v_records[i]; + + WriteRecord(outStream, t_values); + } + + outStream.Write(DBFFieldType.EndOfData); + outStream.Flush(); + } + catch (IOException e) + { + throw new DBFException("Error Writing", e); + } + } + + public void Close() + { + /* everything is written already. just update the header for record count and the END_OF_DATA mark */ + header.NumberOfRecords = recordCount; + if (raf != null) + { + raf.Seek(0, SeekOrigin.Begin); + header.Write(new BinaryWriter(raf)); + raf.Seek(0, SeekOrigin.End); + raf.WriteByte(DBFFieldType.EndOfData); + + raf.Close(); + _dataMemo?.Close(); + + } else if (!string.IsNullOrEmpty(DataMemoLoc)) + { + DataMemo.Close(); + } + + } + + private void WriteRecord(BinaryWriter dataOutput, object[] objectArray) + { + dataOutput.Write((byte) ' '); + for (var j = 0; j < header.FieldArray.Length; j++) + { + /* iterate through fields */ + + switch (header.FieldArray[j].DataType) + { + case NativeDbType.Char: + if (objectArray[j] != null && objectArray[j] != DBNull.Value) + { + var str_value = objectArray[j].ToString(); + dataOutput.Write( + Utils.textPadding(str_value, + CharEncoding, + header.FieldArray[j]. + FieldLength + ) + ); + } + else + { + dataOutput.Write( + Utils.textPadding("", + CharEncoding, + header.FieldArray[j]. + FieldLength + ) + ); + } + + break; + + case NativeDbType.Date: + if (objectArray[j] != null && objectArray[j] != DBNull.Value) + { + var tDate = (DateTime) objectArray[j]; + + dataOutput.Write( + CharEncoding.GetBytes(tDate.ToString("yyyyMMdd"))); + } + else + { + dataOutput.Write( + Utils.FillArray(new byte[8], DBFFieldType.Space)); + } + + break; + + case NativeDbType.Float: + + if (objectArray[j] != null && objectArray[j] != DBNull.Value) + { + var tDouble = Convert.ToDecimal(objectArray[j]); + dataOutput.Write( + Utils.NumericFormating( + tDouble, + CharEncoding, + header.FieldArray[j].FieldLength, + header.FieldArray[j].DecimalCount + ) + ); + } + else + { + dataOutput.Write( + Utils.textPadding( + NullSymbol, + CharEncoding, + header.FieldArray[j].FieldLength, + Utils.ALIGN_RIGHT + ) + ); + } + + break; + + case NativeDbType.Numeric: + + if (objectArray[j] != null && objectArray[j] != DBNull.Value) + { + var tDecimal = Convert.ToDecimal(objectArray[j]); + dataOutput.Write( + Utils.NumericFormating( + tDecimal, + CharEncoding, + header.FieldArray[j].FieldLength, + header.FieldArray[j].DecimalCount + ) + ); + } + else + { + dataOutput.Write( + Utils.textPadding( + NullSymbol, + CharEncoding, + header.FieldArray[j].FieldLength, + Utils.ALIGN_RIGHT + ) + ); + } + + break; + case NativeDbType.Logical: + + if (objectArray[j] != null && objectArray[j] != DBNull.Value) + { + if ((bool) objectArray[j]) + { + dataOutput.Write(DBFFieldType.True); + } + else + { + dataOutput.Write(DBFFieldType.False); + } + } + else + { + dataOutput.Write(DBFFieldType.UnknownByte); + } + + break; + + case NativeDbType.Memo: + if (objectArray[j] != null && objectArray[j] != DBNull.Value) + { + var tMemoValue = ((MemoValue) objectArray[j]); + + tMemoValue.Write(this); + + dataOutput.Write(Utils.NumericFormating(tMemoValue.Block, CharEncoding, 10, 0)); + } + else + { + dataOutput.Write( + Utils.textPadding("", + CharEncoding, + 10 + ) + ); + } + + + break; + + default: + throw new DBFException("Unknown field type " + + header.FieldArray[j].DataType); + } + } /* iterating through the fields */ + } + } +} diff --git a/dotnetdbf/DBTHeader.cs b/dotnetdbf/DBTHeader.cs new file mode 100644 index 0000000..b7d70f4 --- /dev/null +++ b/dotnetdbf/DBTHeader.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace DotNetDBF +{ + public class DBTHeader + { + public const byte FieldTerminator = 0x1A; + + + private int _nextBlock; /* 0-3*/ + private byte _version = 0x03; + + + internal int NextBlock + { + get => _nextBlock; + set => _nextBlock = value; + } + + internal byte Version + { + get => _version; + set => _version = value; + } + + internal void Write(BinaryWriter dataOutput) + { + dataOutput.Write(_nextBlock); + dataOutput.Write(new byte[12]); + dataOutput.Write(_version); + dataOutput.Write(new byte[495]); + } + } +} \ No newline at end of file diff --git a/dotnetdbf/DotNetDBF.csproj b/dotnetdbf/DotNetDBF.csproj new file mode 100644 index 0000000..08dff07 --- /dev/null +++ b/dotnetdbf/DotNetDBF.csproj @@ -0,0 +1,33 @@ + + + net35;netstandard2.0 + True + sn.snk + 7.0.0.0 + Ekon Benefits + Copyright 2009-2017 + This is a basic file parser for reading and writing xBase DBF files particularlly Clipper. Code originally derived from javadbf. + https://github.com/ekonbenefits/dotnetdbf + clipper xbase dbf + Anil Kumar, Jay Tuley + True + True + https://github.com/ekonbenefits/dotnetdbf.git + git + True + True + LGPL-2.1-or-later + snupkg + true + + + + + + + + + + + + diff --git a/dotnetdbf/DotNetDBF.sln b/dotnetdbf/DotNetDBF.sln new file mode 100644 index 0000000..8f447fc --- /dev/null +++ b/dotnetdbf/DotNetDBF.sln @@ -0,0 +1,33 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetDBF", "DotNetDBF\DotNetDBF.csproj", "{C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetDBF.Enumerable", "DotNetDBF.Enumerable\DotNetDBF.Enumerable.csproj", "{3BB97ECD-325D-4288-B355-57CFC1404019}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetDBF.Test", "DotNetDBF.Test\DotNetDBF.Test.csproj", "{8D5436E3-F584-40A0-ACDC-65346ECEADB0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5E9AE18-1EA3-4C90-AFAB-5323093BE4AC}.Release|Any CPU.Build.0 = Release|Any CPU + {3BB97ECD-325D-4288-B355-57CFC1404019}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BB97ECD-325D-4288-B355-57CFC1404019}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BB97ECD-325D-4288-B355-57CFC1404019}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BB97ECD-325D-4288-B355-57CFC1404019}.Release|Any CPU.Build.0 = Release|Any CPU + {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D5436E3-F584-40A0-ACDC-65346ECEADB0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/dotnetdbf/DotNetDBF.sln.DotSettings b/dotnetdbf/DotNetDBF.sln.DotSettings new file mode 100644 index 0000000..01258ab --- /dev/null +++ b/dotnetdbf/DotNetDBF.sln.DotSettings @@ -0,0 +1,9 @@ + + DBT + False + <data><IncludeFilters /><ExcludeFilters /></data> + <data /> + True + True + True + True \ No newline at end of file diff --git a/dotnetdbf/MemoValue.cs b/dotnetdbf/MemoValue.cs new file mode 100644 index 0000000..7f5f914 --- /dev/null +++ b/dotnetdbf/MemoValue.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace DotNetDBF +{ + public class MemoValue + { + public const string MemoTerminator = "\x1A"; + private bool _loaded; + private bool _new; + + + public MemoValue(string aValue) + { + _lockName = $"DotNetDBF.Memo.new.{Guid.NewGuid()}"; + Value = aValue; + } + + + internal MemoValue(long block, DBFBase aBase, + string fileLoc, DBFReader.LazyStream fileStream) + { + _block = block; + _base = aBase; + _fileLoc = fileLoc; + _fileStream = fileStream; + if (string.IsNullOrEmpty(fileLoc)) + { + _lockName = fileStream(); + } + else + { + _lockName = $"DotNetDBF.Memo.read.{_fileLoc}"; + } + } + + private readonly DBFBase _base; + private readonly object _lockName; + private long _block; + private readonly string _fileLoc; + private string _value; + private readonly DBFReader.LazyStream _fileStream; + + internal long Block => _block; + + internal void Write(DBFWriter aBase) + { + lock (_lockName) + { + if (!_new) + return; + + var raf = aBase.DataMemo; + + /* before proceeding check whether the passed in File object + is an empty/non-existent file or not. + */ + if (raf == null) + { + throw new InvalidDataException("Null Memo Field Stream from Writer"); + } + + var tWriter = new BinaryWriter(raf, aBase.CharEncoding); //Don't close the stream could be used else where; + + if (raf.Length == 0) + { + var tHeader = new DBTHeader(); + tHeader.Write(tWriter); + } + + var tValue = _value; + if ((tValue.Length + sizeof(int)) % aBase.BlockSize != 0) + { + tValue = tValue + MemoTerminator; + } + + var tPosition = raf.Seek(0, SeekOrigin.End); //Got To End Of File + var tBlockDiff = tPosition % aBase.BlockSize; + if (tBlockDiff != 0) + { + tPosition = raf.Seek(aBase.BlockSize - tBlockDiff, SeekOrigin.Current); + } + _block = tPosition / aBase.BlockSize; + var tData = aBase.CharEncoding.GetBytes(tValue); + var tDataLength = tData.Length; + var tNewDiff = (tDataLength % aBase.BlockSize); + tWriter.Write(tData); + if (tNewDiff != 0) + tWriter.Seek(aBase.BlockSize - (tDataLength % aBase.BlockSize), SeekOrigin.Current); + + } + } + + + public string Value + { + get + { + lock (_lockName) + { + if (_new || _loaded) return _value; + var fileStream = _fileStream(); + + var reader = new BinaryReader(fileStream); + + { + reader.BaseStream.Seek(_block * _base.BlockSize, SeekOrigin.Begin); + var builder = new StringBuilder(); + int termIndex; + var softReturn = _base.CharEncoding.GetString(new byte[] {0x8d, 0x0a}); + + do + { + var data = reader.ReadBytes(_base.BlockSize); + if ((data.Length == 0)) + { + throw new DBTException("Missing Data for block or no 1a memo terminator"); + } + var stringVal = _base.CharEncoding.GetString(data); + termIndex = stringVal.IndexOf(MemoTerminator, StringComparison.Ordinal); + if (termIndex != -1) + stringVal = stringVal.Substring(0, termIndex); + builder.Append(stringVal); + } while (termIndex == -1); + _value = builder.ToString().Replace(softReturn, string.Empty); + } + _loaded = true; + + return _value; + } + } + set + { + lock (_lockName) + { + _new = true; + _value = value; + } + } + } + + public override int GetHashCode() + { + return _lockName.GetHashCode(); + } + + public override string ToString() + { + return Value; + } + + public override bool Equals(object obj) + { + if (obj is MemoValue m) + { + return ReferenceEquals(this, obj) || Value.Equals(m.Value); + } + + return false; + } + } +} \ No newline at end of file diff --git a/dotnetdbf/Properties/MoreAssemblyInfo.cs b/dotnetdbf/Properties/MoreAssemblyInfo.cs new file mode 100644 index 0000000..e644cef --- /dev/null +++ b/dotnetdbf/Properties/MoreAssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DotNetDBF.Enumerable,PublicKey=00240000048000009400000006020000002400005253413100040000010001008713EA5197F8878AF1E1CDEF220E2D0A898944AD1643B851775EB8624697A183FBCD2ED8C1A58CE185B657D6381419AFF8B0DE8F8934F2B7E5DC7C19C11DE8D146B113F6794BF604BD2A11334DCF1022A485DD7A6E6BED8873D26363E9692136598B7750AD633747922657FF215347614DE2FDFA7866843F2924C9E5DB2545E1")] \ No newline at end of file diff --git a/dotnetdbf/README.md b/dotnetdbf/README.md new file mode 100644 index 0000000..2caabe0 --- /dev/null +++ b/dotnetdbf/README.md @@ -0,0 +1,11 @@ +dotnetdbf +========= + +This is a basic file parser written in C# for reading and writing xBase DBF files, particularly Clipper. + +For .net 4.0 projects there is an enumeration framework in which makes it easy to use Linq to Objects. + +Code derived from javadbf. + +## Get The Binaries +Use [NuGet](http://nuget.org/packages/dotnetdbf/) from Visual Studio diff --git a/dotnetdbf/Utils.cs b/dotnetdbf/Utils.cs new file mode 100644 index 0000000..214d74b --- /dev/null +++ b/dotnetdbf/Utils.cs @@ -0,0 +1,175 @@ +/* + Utils + Class for contining utility functions. + + This file is part of JavaDBF packege. + + original author (javadbf): anil@linuxense.com 2004/03/31 + + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + ported to C# (DotNetDBF): Jay Tuley 6/28/2007 + + */ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; + +namespace DotNetDBF +{ + public static class Utils + { + public const int ALIGN_LEFT = 10; + public const int ALIGN_RIGHT = 12; + + public static byte[] FillArray(byte[] anArray, byte value) + { + for (var i = 0; i < anArray.Length; i++) + { + anArray[i] = value; + } + return anArray; + } + + public static byte[] trimLeftSpaces(byte[] arr) + { + var tList = new List(arr.Length); + + for (var i = 0; i < arr.Length; i++) + { + if (arr[i] != ' ') + { + tList.Add(arr[i]); + } + } + return tList.ToArray(); + } + + public static byte[] textPadding(string text, + Encoding charEncoding, + int length) + { + return textPadding(text, charEncoding, length, ALIGN_LEFT); + } + + public static byte[] textPadding(string text, + Encoding charEncoding, + int length, + int alignment) + { + return + textPadding(text, + charEncoding, + length, + alignment, + DBFFieldType.Space); + } + + public static byte[] textPadding(string text, + Encoding charEncoding, + int length, + int alignment, + byte paddingByte) + { + var tEncoding = charEncoding; + var inputBytes = tEncoding.GetBytes(text); + if (inputBytes.Length >= length) + { + return inputBytes.Take(length).ToArray(); + } + + var byte_array = FillArray(new byte[length], paddingByte); + + switch (alignment) + { + case ALIGN_LEFT: + Array.Copy(inputBytes, + 0, + byte_array, + 0, + inputBytes.Length); + break; + + case ALIGN_RIGHT: + var t_offset = length - text.Length; + Array.Copy(inputBytes, + 0, + byte_array, + t_offset, + inputBytes.Length); + break; + } + + return byte_array; + } + + public static byte[] NumericFormating(IFormattable doubleNum, + Encoding charEncoding, + int fieldLength, + int sizeDecimalPart) + { + var sizeWholePart = fieldLength + - + (sizeDecimalPart > 0 ? (sizeDecimalPart + 1) : 0); + + var format = new StringBuilder(fieldLength); + + for (var i = 0; i < sizeWholePart; i++) + { + format.Append(i + 1 == sizeWholePart ? "0" : "#"); + } + + if (sizeDecimalPart > 0) + { + format.Append("."); + + for (var i = 0; i < sizeDecimalPart; i++) + { + format.Append("0"); + } + } + + + return + textPadding( + doubleNum.ToString(format.ToString(), + NumberFormatInfo.InvariantInfo), + charEncoding, + fieldLength, + ALIGN_RIGHT); + } + + public static bool contains(byte[] arr, byte value) + { + return + Array.Exists(arr, + delegate(byte anItem) { return anItem == value; }); + } + + + public static Type TypeForNativeDBType(NativeDbType aType) + { + switch (aType) + { + case NativeDbType.Char: + return typeof(string); + case NativeDbType.Date: + return typeof(DateTime); + case NativeDbType.Numeric: + return typeof(decimal); + case NativeDbType.Logical: + return typeof(bool); + case NativeDbType.Float: + return typeof(decimal); + case NativeDbType.Memo: + return typeof(MemoValue); + default: + return typeof(Object); + } + } + } +} \ No newline at end of file diff --git a/dotnetdbf/original java src/DBFBase.java b/dotnetdbf/original java src/DBFBase.java new file mode 100644 index 0000000..ac0e8b5 --- /dev/null +++ b/dotnetdbf/original java src/DBFBase.java @@ -0,0 +1,36 @@ +/* + $Id: DBFBase.java,v 1.3 2004/03/31 15:59:40 anil Exp $ + Serves as the base class of DBFReader adn DBFWriter. + + @author: anil@linuxense.com + + Support for choosing implemented character Sets as + suggested by Nick Voznesensky +*/ +/** + Base class for DBFReader and DBFWriter. +*/ +package com.linuxense.javadbf; + +public abstract class DBFBase { + + protected String characterSetName = "8859_1"; + protected final int END_OF_DATA = 0x1A; + + /* + If the library is used in a non-latin environment use this method to set + corresponding character set. More information: + http://www.iana.org/assignments/character-sets + Also see the documentation of the class java.nio.charset.Charset + */ + public String getCharactersetName() { + + return this.characterSetName; + } + + public void setCharactersetName( String characterSetName) { + + this.characterSetName = characterSetName; + } + +} diff --git a/dotnetdbf/original java src/DBFException.java b/dotnetdbf/original java src/DBFException.java new file mode 100644 index 0000000..e8b2451 --- /dev/null +++ b/dotnetdbf/original java src/DBFException.java @@ -0,0 +1,27 @@ +/* + DBFException + Represents exceptions happen in the JAvaDBF classes. + + This file is part of JavaDBF packege. + + author: anil@linuxense.com + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + $Id: DBFException.java,v 1.2 2004/03/31 10:40:18 anil Exp $ +*/ +package com.linuxense.javadbf; + +import java.io.IOException; + +public class DBFException extends IOException { + + public DBFException() { + + super(); + } + + public DBFException( String msg) { + + super( msg); + } +} diff --git a/dotnetdbf/original java src/DBFField.java b/dotnetdbf/original java src/DBFField.java new file mode 100644 index 0000000..86b47f2 --- /dev/null +++ b/dotnetdbf/original java src/DBFField.java @@ -0,0 +1,283 @@ +/* + DBFField + Class represents a "field" (or column) definition of a DBF data structure. + + This file is part of JavaDBF packege. + + author: anil@linuxense.com + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + $Id: DBFField.java,v 1.7 2004/03/31 10:50:11 anil Exp $ +*/ + +package com.linuxense.javadbf; +import java.io.*; + +/** + DBFField represents a field specification in an dbf file. + + DBFField objects are either created and added to a DBFWriter object or obtained + from DBFReader object through getField( int) query. + +*/ +public class DBFField { + + public static final byte FIELD_TYPE_C = (byte)'C'; + public static final byte FIELD_TYPE_L = (byte)'L'; + public static final byte FIELD_TYPE_N = (byte)'N'; + public static final byte FIELD_TYPE_F = (byte)'F'; + public static final byte FIELD_TYPE_D = (byte)'D'; + public static final byte FIELD_TYPE_M = (byte)'M'; + + /* Field struct variables start here */ + byte[] fieldName = new byte[ 11]; /* 0-10*/ + byte dataType; /* 11 */ + int reserv1; /* 12-15 */ + int fieldLength; /* 16 */ + byte decimalCount; /* 17 */ + short reserv2; /* 18-19 */ + byte workAreaId; /* 20 */ + short reserv3; /* 21-22 */ + byte setFieldsFlag; /* 23 */ + byte[] reserv4 = new byte[ 7]; /* 24-30 */ + byte indexFieldFlag; /* 31 */ + /* Field struct variables end here */ + + /* other class variables */ + int nameNullIndex = 0; + + /** + Creates a DBFField object from the data read from the given DataInputStream. + + The data in the DataInputStream object is supposed to be organised correctly + and the stream "pointer" is supposed to be positioned properly. + + @param in DataInputStream + @return Returns the created DBFField object. + @throws IOException If any stream reading problems occures. + */ + protected static DBFField createField( DataInput in) + throws IOException { + + DBFField field = new DBFField(); + + byte t_byte = in.readByte(); /* 0 */ + if( t_byte == (byte)0x0d) { + + //System.out.println( "End of header found"); + return null; + } + + in.readFully( field.fieldName, 1, 10); /* 1-10 */ + field.fieldName[0] = t_byte; + + for( int i=0; i 10) { + + throw new IllegalArgumentException( "Field name should be of length 0-10"); + } + + this.fieldName = value.getBytes(); + this.nameNullIndex = this.fieldName.length; + } + + /** + Sets the data type of the field. + + @param type of the field. One of the following:
+ C, L, N, F, D, M + */ + public void setDataType( byte value) { + + switch( value) { + + case 'D': + this.fieldLength = 8; /* fall through */ + case 'C': + case 'L': + case 'N': + case 'F': + case 'M': + + this.dataType = value; + break; + + default: + throw new IllegalArgumentException( "Unknown data type"); + } + } + + /** + Length of the field. + This method should be called before calling setDecimalCount(). + + @param Length of the field as int. + */ + public void setFieldLength( int value) { + + if( value <= 0) { + + throw new IllegalArgumentException( "Field length should be a positive number"); + } + + if( this.dataType == FIELD_TYPE_D) { + + throw new UnsupportedOperationException( "Cannot do this on a Date field"); + } + + fieldLength = value; + } + + /** + Sets the decimal place size of the field. + Before calling this method the size of the field + should be set by calling setFieldLength(). + + @param Size of the decimal field. + */ + public void setDecimalCount( int value) { + + if( value < 0) { + + throw new IllegalArgumentException( "Decimal length should be a positive number"); + } + + if( value > fieldLength) { + + throw new IllegalArgumentException( "Decimal length should be less than field length"); + } + + decimalCount = (byte)value; + } + +} diff --git a/dotnetdbf/original java src/DBFHeader.java b/dotnetdbf/original java src/DBFHeader.java new file mode 100644 index 0000000..a8c94f0 --- /dev/null +++ b/dotnetdbf/original java src/DBFHeader.java @@ -0,0 +1,167 @@ +/* + DBFHeader + Class for reading the metadata assuming that the given + InputStream carries DBF data. + + This file is part of JavaDBF packege. + + Author: anil@linuxense.com + License: LGPL (http://www.gnu.org/copyleft/lesser.html) + + $Id$ +*/ + +package com.linuxense.javadbf; + +import java.io.*; +import java.util.*; + +class DBFHeader { + + static final byte SIG_DBASE_III = (byte)0x03; + /* DBF structure start here */ + + byte signature; /* 0 */ + byte year; /* 1 */ + byte month; /* 2 */ + byte day; /* 3 */ + int numberOfRecords; /* 4-7 */ + short headerLength; /* 8-9 */ + short recordLength; /* 10-11 */ + short reserv1; /* 12-13 */ + byte incompleteTransaction; /* 14 */ + byte encryptionFlag; /* 15 */ + int freeRecordThread; /* 16-19 */ + int reserv2; /* 20-23 */ + int reserv3; /* 24-27 */ + byte mdxFlag; /* 28 */ + byte languageDriver; /* 29 */ + short reserv4; /* 30-31 */ + DBFField []fieldArray; /* each 32 bytes */ + byte terminator1; /* n+1 */ + + //byte[] databaseContainer; /* 263 bytes */ + /* DBF structure ends here */ + + DBFHeader() { + + this.signature = SIG_DBASE_III; + this.terminator1 = 0x0D; + } + + void read( DataInput dataInput) throws IOException { + + signature = dataInput.readByte(); /* 0 */ + year = dataInput.readByte(); /* 1 */ + month = dataInput.readByte(); /* 2 */ + day = dataInput.readByte(); /* 3 */ + numberOfRecords = Utils.readLittleEndianInt( dataInput); /* 4-7 */ + + headerLength = Utils.readLittleEndianShort( dataInput); /* 8-9 */ + recordLength = Utils.readLittleEndianShort( dataInput); /* 10-11 */ + + reserv1 = Utils.readLittleEndianShort( dataInput); /* 12-13 */ + incompleteTransaction = dataInput.readByte(); /* 14 */ + encryptionFlag = dataInput.readByte(); /* 15 */ + freeRecordThread = Utils.readLittleEndianInt( dataInput); /* 16-19 */ + reserv2 = dataInput.readInt(); /* 20-23 */ + reserv3 = dataInput.readInt(); /* 24-27 */ + mdxFlag = dataInput.readByte(); /* 28 */ + languageDriver = dataInput.readByte(); /* 29 */ + reserv4 = Utils.readLittleEndianShort( dataInput); /* 30-31 */ + + Vector v_fields = new Vector(); + + DBFField field = DBFField.createField( dataInput); /* 32 each */ + while( field != null) { + + v_fields.addElement( field); + field = DBFField.createField( dataInput); + } + + fieldArray = new DBFField[ v_fields.size()]; + + for( int i=0; i + DBFReader cannot write anythng to a DBF file. For creating DBF files + use DBFWriter. + +

+ Fetching rocord is possible only in the forward direction and + cannot re-wound. In such situation, a suggested approach is to reconstruct the object. + +

+ The nextRecord() method returns an array of Objects and the types of these + Object are as follows: + + + + + + + + + + + + + + + + + + + + + +
xBase TypeJava Type
CString
NInteger
FDouble
LBoolean
Djava.util.Date
+ +*/ +public class DBFReader extends DBFBase { + + DataInputStream dataInputStream; + DBFHeader header; + + /* Class specific variables */ + boolean isClosed = true; + + /** + Initializes a DBFReader object. + + When this constructor returns the object + will have completed reading the hader (meta date) and + header information can be quried there on. And it will + be ready to return the first row. + + @param InputStream where the data is read from. + */ + public DBFReader( InputStream in) throws DBFException { + + try { + + this.dataInputStream = new DataInputStream( in); + this.isClosed = false; + this.header = new DBFHeader(); + this.header.read( this.dataInputStream); + + /* it might be required to leap to the start of records at times */ + int t_dataStartIndex = this.header.headerLength - ( 32 + (32*this.header.fieldArray.length)) - 1; + if( t_dataStartIndex > 0) { + + dataInputStream.skip( t_dataStartIndex); + } + } + catch( IOException e) { + + throw new DBFException( e.getMessage()); + } + } + + + public String toString() { + + StringBuffer sb = new StringBuffer( this.header.year + "/" + this.header.month + "/" + this.header.day + "\n" + + "Total records: " + this.header.numberOfRecords + + "\nHEader length: " + this.header.headerLength + + ""); + + for( int i=0; i 0 && !Utils.contains( t_float, (byte)'?')) { + + recordObjects[i] = new Float( new String( t_float)); + } + else { + + recordObjects[i] = null; + } + } + catch( NumberFormatException e) { + + throw new DBFException( "Failed to parse Float: " + e.getMessage()); + } + + break; + + case 'N': + + try { + + byte t_numeric[] = new byte[ this.header.fieldArray[i].getFieldLength()]; + dataInputStream.read( t_numeric); + t_numeric = Utils.trimLeftSpaces( t_numeric); + + if( t_numeric.length > 0 && !Utils.contains( t_numeric, (byte)'?')) { + + recordObjects[i] = new Double( new String( t_numeric)); + } + else { + + recordObjects[i] = null; + } + } + catch( NumberFormatException e) { + + throw new DBFException( "Failed to parse Number: " + e.getMessage()); + } + + break; + + case 'L': + + byte t_logical = dataInputStream.readByte(); + if( t_logical == 'Y' || t_logical == 't' || t_logical == 'T' || t_logical == 't') { + + recordObjects[i] = Boolean.TRUE; + } + else { + + recordObjects[i] = Boolean.FALSE; + } + break; + + case 'M': + // TODO Later + recordObjects[i] = new String( "null"); + break; + + default: + recordObjects[i] = new String( "null"); + } + } + } + catch( EOFException e) { + + return null; + } + catch( IOException e) { + + throw new DBFException( e.getMessage()); + } + + return recordObjects; + } +} diff --git a/dotnetdbf/original java src/DBFWriter.java b/dotnetdbf/original java src/DBFWriter.java new file mode 100644 index 0000000..8d0e4f1 --- /dev/null +++ b/dotnetdbf/original java src/DBFWriter.java @@ -0,0 +1,350 @@ +/* + DBFWriter + Class for defining a DBF structure and addin data to that structure and + finally writing it to an OutputStream. + + This file is part of JavaDBF packege. + + author: anil@linuxense.com + license: LGPL (http://www.gnu.org/copyleft/lesser.html) + + $Id: DBFWriter.java,v 1.9 2004/03/31 10:57:16 anil Exp $ +*/ +package com.linuxense.javadbf; +import java.io.*; +import java.util.*; + +/** + An object of this class can create a DBF file. + + Create an object,
+ then define fields by creating DBFField objects and
+ add them to the DBFWriter object
+ add records using the addRecord() method and then
+ call write() method. +*/ +public class DBFWriter extends DBFBase { + + /* other class variables */ + DBFHeader header; + Vector v_records = new Vector(); + int recordCount = 0; + RandomAccessFile raf = null; /* Open and append records to an existing DBF */ + boolean appendMode = false; + + /** + Creates an empty Object. + */ + public DBFWriter() { + + this.header = new DBFHeader(); + } + + /** + Creates a DBFWriter which can append to records to an existing DBF file. + @param dbfFile. The file passed in shouls be a valid DBF file. + @exception Throws DBFException if the passed in file does exist but not a valid DBF file, or if an IO error occurs. + */ + public DBFWriter( File dbfFile) + throws DBFException { + + try { + + this.raf = new RandomAccessFile( dbfFile, "rw"); + + /* before proceeding check whether the passed in File object + is an empty/non-existent file or not. + */ + if( !dbfFile.exists() || dbfFile.length() == 0) { + + this.header = new DBFHeader(); + return; + } + + header = new DBFHeader(); + this.header.read( raf); + + /* position file pointer at the end of the raf */ + this.raf.seek( this.raf.length()-1 /* to ignore the END_OF_DATA byte at EoF */); + } + catch( FileNotFoundException e) { + + throw new DBFException( "Specified file is not found. " + e.getMessage()); + } + catch( IOException e) { + + throw new DBFException( e.getMessage() + " while reading header"); + } + + this.recordCount = this.header.numberOfRecords; + } + + /** + Sets fields. + */ + public void setFields( DBFField[] fields) + throws DBFException { + + if( this.header.fieldArray != null) { + + throw new DBFException( "Fields has already been set"); + } + + if( fields == null || fields.length == 0) { + + throw new DBFException( "Should have at least one field"); + } + + for( int i=0; i>8; + + return num2; + } + + public static int littleEndian(int value) { + + int num1 = value; + int mask = 0xff; + int num2 = 0x00; + + num2 |= num1 & mask; + + for( int i=1; i<4; i++) { + + num2<<=8; + mask <<= 8; + num2 |= (num1 & mask)>>(8*i); + } + + return num2; + } + + public static byte[] textPadding( String text, String characterSetName, int length) throws java.io.UnsupportedEncodingException { + + return textPadding( text, characterSetName, length, Utils.ALIGN_LEFT); + } + + public static byte[] textPadding( String text, String characterSetName, int length, int alignment) throws java.io.UnsupportedEncodingException { + + return textPadding( text, characterSetName, length, alignment, (byte)' '); + } + + public static byte[] textPadding( String text, String characterSetName, int length, int alignment, + byte paddingByte) throws java.io.UnsupportedEncodingException { + + if( text.length() >= length) { + + return text.substring( 0, length).getBytes( characterSetName); + } + + byte byte_array[] = new byte[ length]; + Arrays.fill( byte_array, paddingByte); + + switch( alignment) { + + case ALIGN_LEFT: + System.arraycopy( text.getBytes( characterSetName), 0, byte_array, 0, text.length()); + break; + + case ALIGN_RIGHT: + int t_offset = length - text.length(); + System.arraycopy( text.getBytes( characterSetName), 0, byte_array, t_offset, text.length()); + break; + } + + return byte_array; + } + + public static byte[] doubleFormating( Double doubleNum, String characterSetName, int fieldLength, int sizeDecimalPart) throws java.io.UnsupportedEncodingException{ + + int sizeWholePart = fieldLength - (sizeDecimalPart>0?( sizeDecimalPart + 1):0); + + StringBuffer format = new StringBuffer( fieldLength); + + for( int i=0; i 0) { + + format.append( "."); + + for( int i=0; izjV&T&_ zl7bs<#fgV8;QSnNQ=C1|Zh83@dX9}Od*_=GwpoldtC|3dQ1KgEjOh*{5#~iiGh(v~ zXWYoC_sbnC#XV*VTTm5a$yLf;Cyw)XmRi3uDF!p+x~$C>J*IMgpyj7&t>7<$BhuT& zbMzZ@9;MSgtg+2raIk;0TcBPcb=+N#$`;QAg)?+w^G1uncz!1I)pOO}lg#e75|F0wC2c;j7t74)?7@BLN~a~Yn(`<*5uyq(w3{ve*r<-fmQ8Qd zKu|Xf&{rW#Px!MivHS8R^4>_bny2f%SeRac8RTRyW;rnY9-r5+S^G%5xZ{BdzW<!0q>usdDq>dNuuFiGK((@?{NS)d{T4`m$O8Hl+~_H5Snwvy#?2}+;1g~x zah(roF|jS!%P9OB`%E3ibV{RX3KQ0V@%2tP-dfR!?s)&OKSGZ%nB({YG|PI}<7`SZ z78t|9&zK{r_{D{Nj&F+R`7ny$qrM++72>w95gbbTMcP^xUqK0I&xoVM{4HU|q0-x9ZC99pgEX{i93nVp8!s%+im iolScc+0I7VL4?l