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 0000000..d526147 Binary files /dev/null and b/DotNetDBF.Enumerable/sn.snk differ diff --git a/DotNetDBF.Test/DotNetDBF.Test.csproj b/DotNetDBF.Test/DotNetDBF.Test.csproj new file mode 100644 index 0000000..74974dd --- /dev/null +++ b/DotNetDBF.Test/DotNetDBF.Test.csproj @@ -0,0 +1,43 @@ + + + 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 0000000..eea8b4b Binary files /dev/null and b/DotNetDBF.Test/dbfs/cp1251.dbf differ diff --git a/DotNetDBF.Test/dbfs/cp1251_summary.txt b/DotNetDBF.Test/dbfs/cp1251_summary.txt new file mode 100644 index 0000000..924199c --- /dev/null +++ b/DotNetDBF.Test/dbfs/cp1251_summary.txt @@ -0,0 +1,11 @@ + +Database: cp1251.dbf +Type: (30) Visual FoxPro +Memo File: false +Records: 4 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +RN N 4 0 +NAME C 100 0 diff --git a/DotNetDBF.Test/dbfs/dbase_03.dbf b/DotNetDBF.Test/dbfs/dbase_03.dbf new file mode 100644 index 0000000..b6ed141 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_03.dbf differ 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 0000000..1d10507 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_30.dbf differ diff --git a/DotNetDBF.Test/dbfs/dbase_30.fpt b/DotNetDBF.Test/dbfs/dbase_30.fpt new file mode 100644 index 0000000..64a5660 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_30.fpt differ diff --git a/DotNetDBF.Test/dbfs/dbase_30_summary.txt b/DotNetDBF.Test/dbfs/dbase_30_summary.txt new file mode 100644 index 0000000..a9fca0f --- /dev/null +++ b/DotNetDBF.Test/dbfs/dbase_30_summary.txt @@ -0,0 +1,154 @@ + +Database: dbase_30.dbf +Type: (30) Visual FoxPro +Memo File: true +Records: 34 + +Fields: +Name Type Length Decimal +------------------------------------------------------------------------------ +ACCESSNO C 15 0 +ACQVALUE N 12 2 +APPNOTES M 4 0 +APPRAISOR C 75 0 +CABINET C 25 0 +CAPTION C 30 0 +CAT C 1 0 +CATBY C 25 0 +CATDATE D 8 0 +CATTYPE C 15 0 +CLASSES M 4 0 +COLLECTION C 75 0 +CONDDATE D 8 0 +CONDEXAM C 25 0 +CONDITION C 35 0 +CONDNOTES M 4 0 +CONTAINER C 40 0 +COPYRIGHT M 4 0 +CREATOR C 80 0 +CREDIT M 4 0 +CURVALMAX N 12 2 +CURVALUE N 12 2 +DATASET C 15 0 +DATE C 50 0 +DESCRIP M 4 0 +DIMNOTES M 4 0 +DISPVALUE C 10 0 +DRAWER C 20 0 +EARLYDATE N 4 0 +EVENT C 80 0 +EXHIBITID C 36 0 +EXHIBITNO N 7 0 +EXHLABEL1 M 4 0 +EXHLABEL2 M 4 0 +EXHLABEL3 M 4 0 +EXHLABEL4 M 4 0 +EXHSTART D 8 0 +FILMSIZE C 35 0 +FLAGDATE T 8 0 +FLAGNOTES M 4 0 +FLAGREASON C 20 0 +FRAME C 75 0 +FRAMENO C 25 0 +GPARENT C 45 0 +HOMELOC C 60 0 +IMAGEFILE C 60 0 +IMAGENO N 3 0 +INSCOMP C 30 0 +INSDATE D 8 0 +INSPHONE C 25 0 +INSPREMIUM C 20 0 +INSREP C 30 0 +INSVALUE N 10 2 +INVNBY C 25 0 +INVNDATE D 8 0 +LATEDATE N 4 0 +LEGAL M 4 0 +LOANCOND M 4 0 +LOANDATE D 8 0 +LOANDUE D 8 0 +LOANID C 36 0 +LOANINNO C 15 0 +MAINTCYCLE C 10 0 +MAINTDATE D 8 0 +MAINTNOTE M 4 0 +MEDIUM C 75 0 +NEGLOC C 60 0 +NEGNO C 25 0 +NOTES M 4 0 +OBJECTID C 25 0 +OBJNAME C 40 0 +OLDNO C 25 0 +ORIGCOPY C 15 0 +OTHERNO C 25 0 +OUTDATE D 8 0 +PARENT C 40 0 +PEOPLE M 4 0 +PLACE C 100 0 +POLICYNO C 20 0 +PRINTSIZE C 35 0 +PROCESS C 75 0 +PROVENANCE M 4 0 +PUBNOTES M 4 0 +RECAS C 20 0 +RECDATE C 10 0 +RECFROM C 120 0 +RELATION C 36 0 +RELNOTES M 4 0 +ROOM C 25 0 +SGFLAG C 1 0 +SHELF C 20 0 +SITE C 40 0 +SITENO C 12 0 +SLIDENO C 25 0 +STATUS C 20 0 +STATUSBY C 25 0 +STATUSDATE D 8 0 +STERMS M 4 0 +STUDIO C 60 0 +SUBJECTS M 4 0 +TCABINET C 25 0 +TCONTAINER C 40 0 +TDRAWER C 20 0 +TEMPAUTHOR C 25 0 +TEMPBY C 25 0 +TEMPDATE D 8 0 +TEMPLOC C 60 0 +TEMPNOTES M 4 0 +TEMPREASON C 50 0 +TEMPUNTIL C 10 0 +TITLE M 4 0 +TITLESORT C 100 0 +TROOM C 25 0 +TSHELF C 20 0 +TWALL C 20 0 +UDF1 C 75 0 +UDF10 C 75 0 +UDF11 C 20 0 +UDF12 C 20 0 +UDF13 N 12 0 +UDF14 N 12 2 +UDF15 N 12 2 +UDF16 N 12 3 +UDF17 N 12 3 +UDF18 D 8 0 +UDF19 D 8 0 +UDF20 D 8 0 +UDF21 M 4 0 +UDF22 M 4 0 +UDF2 C 75 0 +UDF3 C 75 0 +UDF4 C 75 0 +UDF5 C 75 0 +UDF6 C 75 0 +UDF7 C 75 0 +UDF8 C 75 0 +UDF9 C 75 0 +UPDATED T 8 0 +UPDATEDBY C 25 0 +VALUEDATE D 8 0 +WALL C 20 0 +WEBINCLUDE L 1 0 +ZSORTER C 69 0 +ZSORTERX C 44 0 +PPID C 36 0 diff --git a/DotNetDBF.Test/dbfs/dbase_31.dbf b/DotNetDBF.Test/dbfs/dbase_31.dbf new file mode 100644 index 0000000..057c46b Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_31.dbf differ 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 0000000..f374436 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_83.dbf differ diff --git a/DotNetDBF.Test/dbfs/dbase_83.dbt b/DotNetDBF.Test/dbfs/dbase_83.dbt new file mode 100644 index 0000000..6fc1457 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_83.dbt differ diff --git a/DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf b/DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf new file mode 100644 index 0000000..f374436 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_83_missing_memo.dbf differ 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 0000000..9e0ec13 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_8b.dbf differ diff --git a/DotNetDBF.Test/dbfs/dbase_8b.dbt b/DotNetDBF.Test/dbfs/dbase_8b.dbt new file mode 100644 index 0000000..527663a Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_8b.dbt differ 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 0000000..ed95dfa Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_f5.dbf differ diff --git a/DotNetDBF.Test/dbfs/dbase_f5.fpt b/DotNetDBF.Test/dbfs/dbase_f5.fpt new file mode 100644 index 0000000..bafff00 Binary files /dev/null and b/DotNetDBF.Test/dbfs/dbase_f5.fpt differ 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 0000000..d1d1c07 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DBC differ 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 0000000..4405062 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCT differ 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 0000000..d962cef Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/FOXPRO-DB-TEST.DCX differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/calls.CDX b/DotNetDBF.Test/dbfs/foxprodb/calls.CDX new file mode 100644 index 0000000..1542679 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/calls.CDX differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/calls.FPT b/DotNetDBF.Test/dbfs/foxprodb/calls.FPT new file mode 100644 index 0000000..30957b0 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/calls.FPT differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/calls.dbf b/DotNetDBF.Test/dbfs/foxprodb/calls.dbf new file mode 100644 index 0000000..4096747 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/calls.dbf differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/contacts.CDX b/DotNetDBF.Test/dbfs/foxprodb/contacts.CDX new file mode 100644 index 0000000..d0722b0 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/contacts.CDX differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/contacts.FPT b/DotNetDBF.Test/dbfs/foxprodb/contacts.FPT new file mode 100644 index 0000000..31b3dfa Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/contacts.FPT differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/contacts.dbf b/DotNetDBF.Test/dbfs/foxprodb/contacts.dbf new file mode 100644 index 0000000..d990efb Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/contacts.dbf differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/setup.CDX b/DotNetDBF.Test/dbfs/foxprodb/setup.CDX new file mode 100644 index 0000000..92787a1 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/setup.CDX differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/setup.dbf b/DotNetDBF.Test/dbfs/foxprodb/setup.dbf new file mode 100644 index 0000000..e214c3d Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/setup.dbf differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/types.CDX b/DotNetDBF.Test/dbfs/foxprodb/types.CDX new file mode 100644 index 0000000..a346202 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/types.CDX differ diff --git a/DotNetDBF.Test/dbfs/foxprodb/types.dbf b/DotNetDBF.Test/dbfs/foxprodb/types.dbf new file mode 100644 index 0000000..ebb3bc6 Binary files /dev/null and b/DotNetDBF.Test/dbfs/foxprodb/types.dbf differ diff --git a/dotnetdbf/.gitignore b/dotnetdbf/.gitignore new file mode 100644 index 0000000..bb8f42b --- /dev/null +++ b/dotnetdbf/.gitignore @@ -0,0 +1,9 @@ +bin +obj +/packages + +*.suo + +*.user +.idea/* +.vs/* \ No newline at end of file diff --git a/dotnetdbf/DBFBase.cs b/dotnetdbf/DBFBase.cs new file mode 100644 index 0000000..96ddc00 --- /dev/null +++ b/dotnetdbf/DBFBase.cs @@ -0,0 +1,44 @@ +/* + Serves as the base class of DBFReader adn DBFWriter. + + 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) + + Support for choosing implemented character Sets as + suggested by Nick Voznesensky + + 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; i