pax_global_header00006660000000000000000000000064140012054340014503gustar00rootroot0000000000000052 comment=943a542c909709b5e4e7ccd97831bc2d6b8673f4 univocity-parsers-2.9.1/000077500000000000000000000000001400120543400152225ustar00rootroot00000000000000univocity-parsers-2.9.1/.github/000077500000000000000000000000001400120543400165625ustar00rootroot00000000000000univocity-parsers-2.9.1/.github/FUNDING.yml000066400000000000000000000011231400120543400203740ustar00rootroot00000000000000# These are supported funding model platforms github: [jbax] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: [paypal.me/jeronimobackes] univocity-parsers-2.9.1/.gitignore000066400000000000000000000001651400120543400172140ustar00rootroot00000000000000/target/* /test-output/* /target .idea univocity-parsers.iml univocity-parsers.ipr univocity-parsers.iws .DS_STORE univocity-parsers-2.9.1/LICENSE-2.0.html000066400000000000000000000253661400120543400175030ustar00rootroot00000000000000 Apache License, Version 2.0 - The Apache Software Foundation

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

  1. You must give any other recipients of the Work or Derivative Works a copy of this License; and

  2. You must cause any modified files to carry prominent notices stating that You changed the files; and

  3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and

  4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

univocity-parsers-2.9.1/README.md000066400000000000000000000030571400120543400165060ustar00rootroot00000000000000![thumbnail](./images/uniVocity-parsers.png) Welcome to univocity-parsers ============================ univocity-parsers is a collection of extremely fast and reliable parsers for Java. It provides a consistent interface for handling different file formats, and a solid framework for the development of new parsers. We have finally updated the tutorial, please go to our website: https://www.univocity.com/pages/parsers-tutorial **Bugs, contributions & support** If you find a bug, please report it on github or send us an email on parsers@univocity.com. We try out best to eliminate all bugs as soon as possible and you’ll rarely see a bug open for more than 24 hours after it’s reported. We do our best to answer all questions. Enhancements/suggestions are implemented on a best effort basis. Fell free to submit your contribution via pull requests. Any little bit is appreciated, from improvements on documentation to a full blown rewrite from scratch. For commercial support, customizations or anything in between, please contact support@univocity.com. **Thank you for using our parsers!** Please consider sponsoring our project or [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JKH3JNHLL4Y42&source=url) any amount via PayPal, or Bitcoin on the following address: * 3BcmUPTPfLDuYWWSBxGKkChkq5WMzC94J6 Thank you! The univocity team. univocity-parsers-2.9.1/images/000077500000000000000000000000001400120543400164675ustar00rootroot00000000000000univocity-parsers-2.9.1/images/uniVocity-parsers.png000066400000000000000000002342511400120543400226520ustar00rootroot00000000000000PNG  IHDR_gbKGD pHYs ЗƾtIME  IDATxI$Ys{/ƌjfWUv =WDM$VB $J qAh'P i!A+A jADf5.v5;*3ropw{^37o2 l<=0.Kr!">?&C"{T3CP :T;eo{fg}pM$c)_y;:N!ϓR¶H96ϩPƮUֿ>}v`[Ed;;P֯scy{sw{rR>X"d'=6CD7s[;^ٸߖlo!m;omwΉ\_ ""B]t]GJ# -iuA L{T(h~bJ%x/ۂ(<E6BF쟬ϦmUA RN } )Y]6#'?b/-0,ԕSgm.H*::x@eD.j !ᳮywcOX4MѶG X{)VCJĔHIQVYrvNWwzTr֖V2~qQT#&#HYB zfpjZ8?=뉻o 15xg)=*lk<^GwVtE"r/zöy_sFfs^ǭG3PUf\<*2 dU&9OSטCsT.YV2cmê*%.el#;z,7γ|<s2A}gKV2l>gZI[DzҔ҇PmT;d 0.5WђPX#Бs5"жmRu F`sUbe+ dN2BG ,T<Ұ| qܓד/GNm܇-}8ê>^+#), %19@%+1 6 revam  *l)ك\0s4'DRZTþf}eix?y=y}!w|</zCDEH+җs?,CIJ\?7 r8K՜>/AъRDS.;3p 'A.@d#0)<,raJm͐mx b 2u H㾂Ue.zQϿ#`[FO7%P)t.NhF+'j<{{,9f1jkn)m8xO^A85k9-N>,OgnjK?2r8dhxOjZdKY}kI!mLRr,AOTz~;Wl .'HtX>ʊs5I-:q]'ٞY݌vru$უ,c5#H8,-W:򞬫?іeuמ[)]Ds.1!dvN=j ЏwރQ frש@1z.<<..䫬7k X" 'kaPmġx(@ڪmPð+~JHf8R7M) )yD!BKt]1[}Zo:8A89 g-:V)ElALr Dijvwwp#1!v;%&x`޶iXg -ڹĹ5)Q H6RQ HIP2"HDGq 7@Xrgc-m%*NA5D,4Y2#; K+cd4m%MPIJ%WlSbQtgdжGLgS{GDsa-͠'?q.$O^O^ X4&I7hFUےa_ieun W2f}AZc+`M?gЌ܋Pax_T{?PyGVΫ q'Y?ifA>HbLR0Ht,sEMU 2ś~iKqdԱGm p?l;pu޸O_cBOyǍos??zn5W+)V22:4){] u!>{'O @H*8ZA!ef9߬m#S̒EWZV= h4r܀l!wֈVxD|Q)V5\>rhilڝ;e:!\y:Rj|R,BK0Hș/@t4t9dS4E|Zr$2|/\DQk0N?fh!_k!Ubg\SpԽnil?ɇB@1O@ kiB/bly&.dPpZZM-+ ^!yRy2"f6=Py>D]t YDK L" W);]ppYWK=#VD lWiTb: 8g=^qsJO: |d[FY/W@.ےlWe+oJAi^Uyol휤r:'?Ķ9囏M&;-<9oV ;5l?8tA"'>l#1H'^6_;]Hy!Nj)# j}vpJ( 2RHNXVz"=lF0deLqF "늭r(Q Z d4fWnzk898:Gn=}sL' w>;=' 魃OU.%D,ѕg$;l$uN5wQۢ+Xa#`*O㣓Alԫ>-à=:G&s/hyaj $a1%lFsg0_@_aV j_FFV)D3pͦMa@MN?H ?A0Z̍SB] $\XO]ke `-J %JI2QwJ*&V uF&fZZAQnX2DKw kf){WvmDz]‚t]`+i [?ҁHz1EK<88\V9 HWJ#7*I!ѩr$|9goų<ßcB40O oWO~rEKJkkTvouYaB?L'Ho~1t;8 ( ,eL^`8N9pX_)yZVã}RE-UV^K~zr58mOHF2& Lua􋌄jH`$U $R?ou^ze^}5~TKׯ٧kx_;Jq'~\V_}=.+cUT]yA1T`g6 y 6JGgԗV0=oж"(lsĺ2qC}1.a4*R@1|kQJ16/ 4wQWl4b!5A/z:0SD*ߊyfж "Kbp%!t;;dߡ%9\Ts-D a^ cY)g) t ,,bd_xpkLgxY#2/$ܽnK9"T|àP{3r-F3}:ȇNl=UWs(lԹ*H< \'m^NV֌F?—?*1(/·bao늌۪w'pUU pNw|b~LsRlKwuV"YVdUNVk*!;OI6QrOЉc4F$RG ƴsO[xW~FU5yw]׹r>KZu p {DP|H Q?> ^ |Պ2K;r&*%RZA&2qMS5bTԛNst6s9#K'bD Hl6r2DQ;Fl Pl(-%%A3ǐ9s5ϣT J4z``#:rfܺ~_繺{Aw?}-y2:E#իW>~@a/]lc3." ͼȸB) OLF`%";Qb4g6rzr]ԒBϻ=b 2mY#HZ|!&ި@:;w^+Rm-Wϣ?-aiڍN'{)=:$Q7A=|y[V䦈ތ7opx4gkI+ά!s;TPRḀۛQ*pW Zz4!N jﲥ N*TaKPHq0bk.A#1B0Lwswk3M@ĶQW.޻¥lԍԍS<-& m4I`'28MmeW 1m*f մFff9N&1)G#n*2ȴ¦}+ ꆦCIh@:E!AIqg,~^{-S>M IDAT/;wY.:;*T*3@hk@%KY,Qi»D]Tz;KݪSip֞)]ӞGv9.m雳سŀ-_]y44A~"JLՐ/2^[,8xjYhSlhY-HMqԨx)!,Q9)vl4;eU)م܅m$#4ML&mrt|I*DiжQTJ$\ӊӅ*Cs=xWP';\+4P{+8A\Q_D2]H6`m-#,"2O0"` 7bycAzm^c~9>:fgoo/ oܒaV,ƪj|H6{&] :(vϜ{@pU ;@y~L8d^#9}zv4"u!;zH!QI̋Q+Y^e$NUI`/,FiOp>RdW̧;w?ϝե!I0$9*T>IP)5L*RϔIexh RZ(4 tW^qh @j, Yv, ZEڈt:(y>Os_xgw2Z'yJڶ˳5IBY+9Sps*>Q> c1/ b#pl"d7LVdѽf}ņNq{E]s";Hжs~xQ] ,(iO@^Ǭ(ߤPh dyʛi])1t8.(pnz+e}n#)Bh <^i {{;cgB5ɜywӚa|ԹbEjKbm!UOtJBs]Ij`6E 7jl\Bm"-R.!KRe"\@LF4 M0Sb aa80ST*^ ү~e!$b kD+q,䝭tW R+[֗R$ 1@b8 QZ= } RWH m)6=*e湝e{j]>m=|1.coP)erI dx~nv-X&KփCI$YW2|3/0t7x>d_?].g9 X,qDm] s,S*d b - ÇhG^qi' ވq3`jmr!lv:7_vzd f'KH3OL[e ɖ 2r(sv***R4w#:NbB\MH&aLqTWf`-!&':%ku~&-KfWnX,yH8 u.τ뻖HD2?A =QP{d:5; U3B'Tޡ"]]EOFhe@X[sB śH̊%K׾E\o ?cBHhqO}{_ ګ:E'JvTW઒B,"X8*#]]Gdԗ dR9KSJKV6[f8C-f@Kx*ٹ"vSq2[ܿLOp_8{\쟯̅2ҁ<,kݳ( }gm_p(I28YC?e͆t& f.[>8#Y7zq28(`Ōog&8ױr0HS)#&w?{|wϝ8Z32] xS%#X#4IyZ+R+Z{h4S*ߡqsGS,*4@ED6 "%b+{ ,϶yR.bhؿGO?"׮IEmw^rgqIK-UT U3E|]NYqI Bm+̈́|R)e$XYت}1/f1Af'ABΉ7W)%btbSgTΑ}4^ef:0 1J Q1aU+#8z!x>}Y~v*9Wbkeefc%,(N J͔N)rijBttQBήGECH%v ̎`m.^NJ)Tq*v_~w~{p|8{*S{/cBKjQO"դջʄ8wNF׵, y8X3Q7J:c.!&) Vl4SJE&Z/sMbkb,ɻZXE 6s6y 0;n]g@I(*HMD`s.<"3/%8EfaEt8 6MTT YqHJGX'Y1CcPu}}>|{8gw8:*ߧ]rvßOフ4'>bU*=R>mhBKmRrːd ڲ(wF>vvgTgX xɃ#:x1 sƙKI!"k-תwa탞 H= \ xYOR݆4+@NlRIVժ9='(BJߚm~`TF )z}m92)=VKH?f򰞶US@ `Y0SV`ZvZZ_`wolֱLR VO7MB)i&gnhÜ{q/;rcK2LhDFGm p( D0ci&8$9'S(7q L ؒh HKXl RcMWǭfK+\}|I ^uFܱ,WQWhML6 }qSg?7_  ϯ<;G 9OD޳^H4R 2!Ll6e3tP߻Kb6}H8mx|S5j!JG@hDr&ӫzլ~d\__j$"uC|Lr-3H~ʢA8D?iػv+7n2 wޑ|MUЦF-`(u?޿K O]c2}~|?}. G@ ɩ>6[^y1uI05pL:eta<1G2ؒbXLpFT]E4]uew/Pib <\ůJ~]|oo_R񧯀V,+,}m wB3qFRY;q|r5| vws|$dptܲ[3ջ#tmD].1szKX{{OE {?{?-z1.7IPҠ8:s"UIbKߝѨl=_mr"aq8 ~Q?l m<$•]]b`efWAu3!T.dϕդ&Y}9F_W7Q7бJ13+溋6BX@Cq`os4$0IEOGhR-Y/|OgRצ$gL[;\n $(HiZBRWơzB,e:őR[ GY+8&H9e5 & *J% U}7|3^N޿n u_{1cw_p#ݧ:*cBXYς !YD\MU#U^`tEdCo ?8Iq1$~tm.:} ܻwXL Divɢ Ny1`EG2Uз Վ\USF~/?Ww&9ݗ@bRFo}n~z6>% …=;?&uG8$|q).#PJï!0\,r, miٚ͠yfF}(KuS9<8"0ϣd^{ K,kMvt[/!#DÉ|ZoLHvӮʾN>O7j/})7wU#Zɡ$pLET% IDATyP#>Aw37cP!HK  ^Ku*wh$ rsaxuj {"/q\.\U  ]5oWB|;W'|5|ܿ]YJvmdhBD|{WGUV?JXoL^}evYosy$g}wC2-{{لt!:7:m42mcJHsָmi ͿwOOs9o~65} >QdX缘/ޟ-?J>I`b LpH}ɾ,Tj)ۭ JEiXXX v(! I[Q 7uL# |I#մgȋ1k㦌)O=,QKtɋMz /kiXliE7#IKld-|u(nSkRWqrMM^lNg~m'dK7Dqh"t[MnZU^飽#&3\AJDH!djRD"&Btc PT%*!)'jynZ6D<EJs55EdkHqZzV'ۜ:!8s/9u,%꺥UbE.u-}A6x71ΕXS-CR N۵w",r`ipe.](¹KTYoi 1kkV+ݞas#p^22E(K`p\輟J{KNqitWڂaf|o0N,1.=g+CHI$ÑyO L"Ñ2Ԕ21v oaVI R)MK< k.TTr)8&o9β^im7IWx Gi5s6(ЖRz4BxjB D:;Gi;|O2MXZ8ı7gD+#n&>Aq2KW&t:mOaVA%dR-=`Mwꕺ;&COOya??3;=ƛkطGW.)wp? /nc@Y 83çxɐ?rq̍G4=6bPlNH?j"^)]G3Ζ}@:(NM9- Mgs.>׮Ja%cB`v3)ytaŪ9]G=0?7KeáF"DJFNڭ`Hi4Z4D` C^ 9 AGP3_(>[Um!C"e49K9~!QFt "XvҘnMӜ.Ŗុ`K@')'fpK^O~2A~⃼뿋Kgy]nOmm]]rqp/}OG^9-im3~~&C )ްrGB$U& H$یW{L0;AP"D(7^[x >/2q5l5 w2$RJI"GBnIc1(FEܸ5RTscØ IȓeVt-6'U"'U3vшSFWd Z8"[y*FHJJy|LC$NeV*¯R'Fԥ.EsC9AR}RC=Y͊щyp@Ʉ8` EqnS8ws=L&XXW8J^LLh{Q!J{tf:,,-8v0RIV`c~nc#ZxjI$/T8rHe1Z#Dx k4)-T n$>T~GےBZ~n r^5m,gq|>Wo\w{i_r>7dsK連(F}LYp/>7c{g4<;rB{#?r;AF0:R)'P3EZm0+bb80DqFHdЗ4%?K:_eG_0R[NTdYO$oQeU$j JyaCER(88HDQc]IBj:u$  DE4z_Bg$xj9x+~ $x҂)F(lLyĄ;Gڽ8;Ԑ4=Ӫ): Ii2k0)tYY9K!;O(;EJuw낝iTV(Q&H@7M4(BBPcjPR c1EFPEQ9F !&%w|x;O9<3KV=6,-G1yQ'B s@LpiFqx%qY2#dT)Jex 'nU& [Yf389w#ڭ.@uH%y/P&֥,-i6햢ٰL&9VQ k4TӤTZgp~7 5uP˩ !BiEZb ]x-kIb VJ^.8s>'|#j.YYEўk(pTˎy M "0ZO T%G8°uCyFZ'~Ǩ(Eg:gb@U'(U`tN19*EHy(xQo$"IK J*,"qʢj ͇9~Ρf,mH6a|:Fep$8 Xtݖ !((&dqwy%Ǡj:q\XCe5qeiG]ၕ3<ٷ/|6"W֡qi̡CVPTx[0[rڽj~?:q,3am{P]VtZ$ٝጊblj)W:5X%DV l0*`0ıAMXQU8[aLh眙Z i`x94Al1 !A leZߚf/6wC]#W3nWeW~'?__XͯZ^/~5~sGn{p/__óߒ՛W_ Wۄ_y}D@}579<"`yҍbos{!;DQRjp˒2nO VFnC azGZv,/SOcHYD=O<¸ }R6~5KdDY.#qW% 21Bi0\TF=FGBa}Pwtkٶf٧*&RPk#YcMj ѯN`֎M"BBEcD#D.KOԇQbTHx-DEZQ(RJfZasP@XurFMҲ^: &Yzt$ P tH!C{-t]8 Ker/)fRiȭKhk #BsӁ9IL3 J23C0)듳LrC7 }/mϷ0*NlErPϚTE <|Wu>{$8q6JVb1rǭsz'K#MMfH<)JO'%aEkd'U$S\R.1bFF?ab<! jpCi @h [j>`B`ޖ-9vKr.]+'~][7>YJNN{F5]2Vr-䎗~]gIj`?>7ZO@z"Y krmNcɍƕhQSiC^y:^4^LY[|4-(8g9{ֱwX:1WY~x봚 ]M3:A[ŞQG)ڀЁ%{ΐO ^()Rg33C=8rV#{C*6Ǒ EIWQx,*2 ʶ "fucT) 'yoCM0tyH~B#>'.RE {~nUmV_|k\tF.gva|Qy7XkʧS\}ga_[-Y7p>TYGK*H<ھYHDD,!"D),bq0l7'cRMm0>E% ԈU!|y>-ufBJ0 ɉTDQUdZMѬ9 6&cp;K=KbI",e}s /q r~sB>r-3XÔ˗ $bsӑRuE3BFQFksH R Zx3eIK͌CK]nԙnN3mI'dvm|A-Ay>SP"pӹPT!fkPbqh<-R`9Mm:sc=Lgz!}|_և`;i D;Ij?F 47^E_s߼go/Uox3?ַM_?+x/~fS~7? ~_I{n}Tq-p覧:NOГ 5mGg}3RIcvݞHGqK⻵rrpޓ {TV!*6 :H:e(ѠCdXPK Y(K$gstmeΓ1U]!nɥ!N5jKj(Q[ jx# hg.a-mIZ-@3: iB%:Q3{Z5 &eíG8آWԄY'p:>̷ E+Fk+}> |)%{:Z^/0yDZ)ÊQ-h\XD55guP(b2(;`y!(-Z.nNCҮ{fjv"a6̭ AMsah1;ӕa0Ԭ'*RVSƒ+*]M5%11Urwh&0Qo+Nְg[pwKiGGizgOeZⳳ =bEr^ؼ|v>oÿ ow{[~n^ {G)Ƙ=޸SH?_G\xc$Zɳ_v.&o܃x­IjM 7Y'hy٫ ⽿_G^}h'?~=G]ז4ruon{@4=(3jk೭dWsm~WOl[ Z ,KjAu|)0@Qx*!-qRCQv֤*+J#SG;uѤEIKԉk-ҧ8 cQBVO)☱vd .eQNS)f: '%YL+( e):*#Jz9+$DU:58h%=F'aE "󒒄28=dahlsfd'$c\lsqd~NHø_qqMkk`"pE92.ku-6Kbf)J֘k툤z3nШ[ 7l=T-Xؚ3ӻp-ee)a<6 %QA[mWN߻"x7?Ӿ8c_1?^~_k~;krϟ {vBt,k V+lҬ׈mZ*;sl!ہV#&M Q</Ui1:ؚV[4IIS!p(P3h FcS:'U(bL.+u ?-n? @ 3-m'%U[)xs= Sb@Vjߤp7O9M~3GRkyx5U>wm3_}o?{} W/̟+}rgml\|ͩ_xsSl,yS~!_oMxܖھ*q:`!i)>upbX4BZQ#Z\^md fQ(iY<󽌅9jB.Bm,e80"e R&XFݱY2f4'bmFPR7D2CYrCU&(K)I^N[Hֆ6| ^rma 2L.*i& dVOU¨w8s_eIZ}Ǐ9[APoMM8zgf3{~%*H$OomZX/=bV۽^;|S,B* r!eL%dLYM&88ѮQt$qfΰtZ-*HVdR!ݮ3L&J 4hX푦5.,0^17"#6', Zv3E,NF2 fg: J븲t)yFa#wob^MDZPO3M<~٢'-_0<|`JΝ`R 9v$̙qaQbRbsykQI\qPŁńCbf{QyT6 ǕLƞ(#p6 $BNx Ro5ѕCWqVɄvɤ"IҬFGSED*,$Vz%3ڣ R ""s.l692AkH0$@vRH癟鐪t uN `ӮO2۩##C%vk I"ׂF#g[e)mA92a' sK $D \Y+cseNi N%$J0Z`T5ЏbJG4[RnyZ 4kXH)ðWO &A6IA`RJ$0.4NE8%Rz0F1GH,8Ji)"a<.WIE%(B $bݣ+HcҖI9e)M >Ioml^8cw{ioО?HgH&.@zOO |n^bdzZ}F1`|[dgIH-y>>u.d!cG=Z_s]Egg ĵy8/r BV8yyuiE Pu0$+綉O, "9 ԶR2?Ym1A=_\L{ )) VUIK1qR'IR̔ilՃTT:[KT( B8"0FSc&V%%smO$IޛG۞w {8j *@A)(mLCb4+&YIZӮCCKi1q"jHp@ "$P5םϽgoz}p=zru=<<ߡkk2;E.4}PcX*\gX_I7FP%{|esZS؟t62[V N\sB]??mftfXΰKVA_wE6&(%t"!y뤂륛f`˸2),}W~_zmHKUFNNyYbhYEQeybpyj0|ƃ_A!Dg:)]T(0ڠF+cowK..b< SB _:G5ą?g!+<+Ae!6R&K<'2R3|7Ka3eD]%]9 !Fʐe_3Z0cH>q[)J%]YQd%J/%M`9LbI%xˁZhT%RK(1FGtXgh>oSzfůI^ !畃{)J"D!tUXrmKQ=~/bۀw΢ .0`T#DDZ607hlj?(C`TpQM-.O)9MmݟYT0gd%Azȼe+3 uinΊ.4;sh5V[ !NK QM"8a)1c@+$x0AJ9GAp@3[q/ԾUJF$ɒ~\gƠXK1Gt--TSi,lduNn+xW7z[ !1`"/0YrI%sXr1sXOB=,a ‚H{\C/Ix9% +\{'W@9mZ 0!Ck/!Á9A o_ ѸM+Vc ZTtG+EvJ=pS $)$JʕA皕iC5hkb(Y[[E M= u&?"eFgy^Z (9eGBe z IK˩ eU?*3Bc+O[(k}j;eu& ] z9 &+!ʪek 'cד1@%Zya:}졷 )rд2{.xņı3sGv89aT^), )NlI6 YHPMU+PsoM;L*(iZedtmGt {r5RwXA0,2e'nq3JFu\/:yWj}x%))rV᪠_H1|E~HƋ?z!z.7ndjW(c!"Ȅ' 77߄rNf?3t6\:Cx9 =z8 j,jG8G%YfZOS1K]9(Tt]7ɹEquh<8"] Iu8.+N(2]CS]fw6{iqx,P>!aj_##U+֊LkrdBS䊡CZBh }0`;,Rb7('M[ 9t`opWGJv .C,fG_?5xgE<,x e2,c.{x^}Iyo֌?2DPA`К"3$x@@5^IChEӵ0.><٧uE'NgcguGfb2;V b( T1Llyy©/?rl:䬲{bn#f`O(S^vji&"-mg/'`ώX|46p0Ǵ䈪B.񬚘|yu.9R k-Zit|ǧ/Mf24 dm>o-5m3~6ؾv:ʰB |ˏ|6,A\{Nj߀o[1?Ln_(^Z4H1<Ο?cOB7c~ܭm1}+l§S O>#wy|%]|=ߘ#r;g !sy_u?s 6oW?Bw%,x|q3`c!1!%tgcO<%' Vxֵ8BIԜ]|O~]]/۟K_qe=5;}`?fez˴M%K8Pb_/d(RƠ6YySIgȷ \q})9i~KgN9/{-e|9qSqW򴐉8 DžsP^s1Z:N5L&2EOkL6YJg"9S ~Gi3tPYY~"2<+ä;Z0m> {Orh>^+aXҗ?jιXQ8G&lqX4rU)ɫuii5Ƙ+c:+BjEÉBK㓡N[M1ХJs4lUo;qT;w͏ |\ed )ԑ햼Pְ!^>Gi2cX!'o tMӌ.I!l @Iå2ݻtex'D)m;e`uOן'jcsy%}å`뚧rټ&+?{ՁF$IoR E8PȐP3H!dG)2&a`[s?ΙغF~F~A&;_HՍ.Vj/ydB9)k90&H<ʒmNǴdL[@?۶,3pHM&WM#-lNG}e98>q/w4 6su:l'WĿg$bEy/\q,?,~f2Vm5ƒV㈀\|䅯pLm6mS|y[nKOp+!+h'tU'D%w]=M'W ſoɌ6^G-Aj?^>g?btuM3W~;'n|R@cgqy_]lf%$ۧ?σ ?Mk:Ɉ;_:N|ǡHԨ6`m뺅wygۅċlq[T!xP dښUl\{oC'HPBɌX|s_غLm]09o,[۞#B_%>p $Fh"2XmYƗF ZXiT,/ǼNRi;06ğoer ]UE]x\x-kno|?bSlbxw~ pjA/Zn{᫙^G}>2ג쌛R^& ?o]Ȫ,!$x"Iz3Yخ vgQwHmz7bҵ(DH)zLvy5G|!dPhc۹iwrO\$N^;'S /s. Ȋk-R֏ȹ9goX+RRZTQEIӵ<ǬiBxz^btȮ&t;` ?Me4,`:dхFVx!E &&Shc.ZL%qGp%2xBw8bBb-L'sL| d#=AfB`>H2s%jI 2 4c"#Y]T{! G_Q 0f2g[K0 IbW]Z#gVcԫϿ IyZѶ-{UPM<%n>#!eg뤋wq5#Eք(yO^0Xd_U=ewc7F/<,pP$JytTIJ}::BJ>T]5A_*szw`oeSh ۵H&㽿&.y`>Ğ=.ywƫ_")-oS+Ms^zŶ9m;/WZ>?d%_uߍmZd倧}W?[gG g)d%A*SfYow:N`/ bx l!$K䝿ltڱkO}쏹D3qS#"itIOE |'WFCwF*\[Q(鑥$xDxԨR %d, HHZG1(-Ȕ"XM؞+Y^/U&;gY: -`8y› "Ω22 +2nR}T+}wm[e"fw~=nwCQqmϧҌ<2ػ~s]TsAOyF\ڎf:幯{"p˾^oj2ҘtykCSRڙ_aq xFylA +xRx?k*.ͩogڛ@]JAɿG>.{oٻt6FU#^o_9VR\*B7AQ%9PQd%""K hY֝kD&-A%J!uz >BV JOA0@4P\QIlh9{u G4R!ˢ: &Z$Ťc]7$.F]c,<>h8AFeRIvڹ {sideM8u{2 YOA:#46B9yK!?'9AO%Hn 14bc ND:s+6Vr HD[FZob9BJ",6C(R5NޥP'pL$8/b9qr@Y9 W|3ou.fm+sxt:ZIQ@ 8PE-WpQm0fH:nzx xnbb2>DhֵtMqޥjP@Xm]T.>r2BmkP<..Rjۿ<:/_;x __1|,0i|y!FeI'}=LP OUbgHw5d4m>{Q~;emLB\B)V)&LgR(,J8!KR4!e}w8"9`A:Q`C:|lLIQ/V:E WwE)QdP:EH\H/ QHW]*Մ[ "@c;p~QNYR(۵$ CHMg=ӺJKϥ=ΨO޻Aua xKߏP) 6}6V\)h-,::<'yR^g9AR`Џr"KT d`F)df$ ˰vF W@$ЙuյX+Q]\=ke@h%1VѶ2.(hYT'M^ !!k`(2V1IyBua];R`57DQѵ#A| <ݿLݔ`'ri?{?Y,ڔo䞏08εO|@H2w/0Ym=mc<>ûgf"%"r#J!e$)5a({EК"+Ȋ<[YFyJ'#6Hgzƒq"%!UZƖM@4$j4qBY. r H  :r %q%din!D:k(lӡtVA 4JJF)B˂dG M(>B=]sds,O3`@"3(䃜:êi:O1#C5(4e`JpE(I t!PK#UH蛓6(uu()hZKg %{XucEݝpEB2,cgwɴmZDf4s>'^y[| hufrfo4+CimRoY}̬wcIs˻TAM֣?\% Fw_E['>u OK?(i(%+d_3aЋ4mfT0 LPXA׶lGԆѯVR S Sv!Xz˽Lp`$RHmsRn1>wh0Tk\l.Ytkpkm Nz6ֆ H鄝}܊@kڰ lo`C%uǰC dG'4m+!pR / Ü>iq#xKf$2c>z̉!uY1Af{u^5r#ANTxH!ŅMuT6SѴ ᅴ=I@/oy2pT iB=tjtb66iKͼ d{W}OӦ#ﯲy\xӑ? gbK7Kxm>rG] Ƅ6fv ޻Ykv >~N6ǖY:BġI(ҿ=A(n[yJD_<gia7K~ bN{)CHs^ԕ&￑cͱd_<- `<lg 'D [Μ>JSMT,+RQU)uY͋\aET?>1d&cRW2~~a7q,vA3MZbf%1E2؁Db:09t&)J}ta_e'=ZIV^oiqֱҏ<Η '"Ғ! &`o,hl-ZXcވdJXےIA+GcF1xK#&9u-IE68bOF}vv.fK!$/jwsڤ8#ZH%Zт\ ~7?Fa_~ iv<:sóX'P餾;Mj%+O ( |7XK$@puaR]d%+8b(]LF;tmT툹` {g.m>8BpS b=F4x$p`ns'f'!dXZﻇ;}C(PFqG{?/Zrоs$>@]C.V|nH2 mxwtET(d Be&qHatF$r@Fւ~ڮA((@p0a[t.Ӊy&BG^(m8. c;O8 ]0h"JI|Nё'(%,[BX$4QcG^H j3HCUcxR(``4GJKY䬮 hZhb[khGW>qub]i:@FcB8.%)L2ě@\H́u9&PrxzKk-=]Q7 ; Zkvz L'uM6htBvc0ޚr %IP.RY?W9f +h™߱*aaoAN */{-3ڹҍm0gZC3YXkZ%ْ"-G?X #V%+L.ͷ{80A!3s58{ Lw;O!BiBx?Q p'JgH'G"J3ė+R~=}sB`3z#ɋ_{*z]\]\P1O#LIo#Q@J*B&EY 4i*@R xdhpVQi`mx5 B26[BPZBȤ䒕Atm(6EtBem"%'҂$E[gqG8Jҵu1y"5G.A#B\I9O>Ӟ<%H]ӹxt`Y3CV#:;bc}@5Å ;cm3^a2°9#FeOusb3g}o,KoG9l\qe[thr0 -mMt0mhSa:Z6MҶ Ua}"pҴg-Dl*RŒh)AC^.KmQ̬ӊyg?z[Ic/|}[ٿ|>J,]4B/hWLw>sՓtyJd'OQOx%8w$SYZfL9ĈE3ρ:D1eÕI Cqvt<U'!J {f~㬾/T2^:lq'AD :/Md¤g/4A9DnЦBABk[!y<*=A [b-}3C_1A[DO iQ""XGn p Ҭօ4.Pa }![Gw_t/08 8<ӻt Ɠ)F m?g8I)Ciڀ[a8nuQ!UU̗&jjS4Q@| h1$s1'3z36{$ w\1\istg %q;eoG9&.GcǬ*_u?YS7ᤥ{A-g2=yK%;vNh;O)D! GkE!30UfS(ET"$0XQIOVԚ;3^hܱrnk뷏ۛ1֬n4(vw'QEёtت sϬ!mYYopgP ne3+]yǠt m1*1ĸ2^Kw9!6S_2EX"hʘ ^lªPP'M̰QK2\(6 щ}b4M :3=H$tպHc vv{D4N$f$&;PJ?kiE =EG=bN:3d̈|]b8y{(g;*^}k[T5`mPK 0ZVW*$ dr-B+GR -BRNIdڳsfz"}?|v 8̣+(Ԩ֪l$'HdISem?:QIցZ.aMOIm1sam`a6#T!)Cݘ56GtAu +[Dc I/y"M>Nyg և\䭯k<}뜿99|:k+Hx}B̞BϾSܸ۫=Y*!.2-:u-.#1ٜ;#!\$4)B̶!b̹ݡлQ>O.A‚N$KAI㖤5D\BJ18 Kt2o!;zSXN}̺(>~n6 S_&ӄL"Is!5T5;uFkg3{|黩8rKe&|^ Tof*]JzՉl?Kr!|ru][jӹCUgw,x`fz*ivw_mP)alˏ(x/FyXJ廂; 9 3c*7/C ":KHS**S AEؐ(,`!hKJ=ATU fNR+BĂP̜'`d9L[JVӾeRw8BS7yǬ)f"]p}(?1*lƩEɖeScUcuB6y:b#(At130JgJ5aH#=FM[Ș4\tܸIZv@UGwq$emc\ek7҆6Og}]q{'|s^ n:̙U{"s;Ǥ!#7!au}K/.qsNX IDATYA]k4kݽoCt>p)Ʉ9z|.K$) f̡^T) "RLBQQSYzvHْh2,#J e(\.4^M xl_!k>cٔzsBax7i#2t/SVkfOs<زrҋQVeV\gOVE PO& yԝھBo5$fnԣ39D)L{G'~M_/8}*ʩWձX4\r?tPv~ ;qߋs;VT1͂H_JIT&X%Nٕڄ2N鄘T52"IiPatV6 ٢gP h3Q#%ahV*Mw-Jt^&"a2+ƅ!]iluX2 0+S!z*h]QEĂtKw=$D`3 TM7dRZhB+y7,{:~>aЬ!(l`b4\/yiۻ>CR41Ԭn6y(S$DO݃.?-nWX8d26.\aQsxR@KE ot;o}@.)hn_~wXֶ֜u3Ka ,pD3kj(eދ,R\|3ڇo2s0ACy䃋V 1*hTȒ#GsтCBCLI*ж3D< ekpS6WX) e4+U=sTA$mpFOk@50n@ 'I*\,On!D/,vS h5^QTfDTޑt֐$md&%,@% ")̀gHH%j¤.4[t S̎]zq3r ;\3Ϝ;'֨ 6a29M6MX]k4U=MXV5G7de$]a8rBe8cޞ%hHs g61D?Lqgރw3 $:Y;!Ȇp)jm-1xna>Bm_/<55.aT7h'GwM"g]P[n.(4mh '˩A!1 ȒVHHѠ!H{1NQ:bt^j$Mh:m2TDueeN\>]$>0v̻OhPZQ 4UO::ΞCbw"Dfl i5%o]"79B,feIb* bv}E{#h:7BVY6Yn"IHR**[&uЕHѠG Pڃ8 `)iQS鈗wR, w UJvcbHtpkw3kkS^g(S^~z$ybreFO?+V =9]QC ԥPEv׿ޝN jB rFxK{ekr<hg~x6`e&{yO~?qRsr?ﱾOtF255?bu*zoE:n1So,^">1B (!'Ĉ=MÇD3hcfҠŘst]V*(ƣ+ :;-_S#IeҚ$:޴% $N5yhtPpv΄Glo3ִcu1۫+hYDNɾŻ$`x z;ze(yaJ\h"*!J N g()X-5Q;P XEJ6XUH&RԲHh#f0+_49vĸ`+tijM̴;i[]>𯼇ϳ:|W9zZ'sf#z;؄gBJPfxVA7'*VWٽ&řu?$ѭkSf̰!C;ذ`p)yO΢J9H}s m5[og>q?l?4 /iM-xPZ)SX.ЙtBtBwM^'~AXv"*=|`.ꥸ.ԩ*yKSK=n|㋼~P~Jh{;_[|~AX۾gmL5UCymow<>{>&o|<N#ovn<"ZF F{؂"9bq7Xϖ{RXJ![FfА-I}@5gGac}tZPʲ?ATڨby:X_VxU'''I$gKu=A)y%M$b$=QP DJfBoxQ^':5eN#IVnJMBՆd4X&gDUуx$% GOpGB^ε5Gk|`̴cSt 'h l_[4gKdLjQ}HC2`d8-.t膣CN3"Da6(V):Nx 噏@t=uh.>?7˥-3R;pٽ&Z79D6 @X&Eϱ8- xOVAl"Vf[D:..ӃkmBZ[ֆ(u`Nfd_!aֶ$Zw V7ȟzf.#W7s/jHjNg?D? ^?_W[w34KR1䋍R: ã_ rA|Ev#LvL'ԠR%6BŘYV̪n2>cЌ)锭s(l¥Kpqor5WG((:uӰ74/mSgTEh}5 } W7XIGs'9L#ps>{>Xp@yvH`ђUB)`.;P^PK|0E*'6J3L%CXğ k3/<4b #BR0 ,YIEQ->ufYAr Q175@e@DOJmwwTr7uЮWg-XWk#gwաU"uf}9xYt:mV5:}tI sfdB?JÍORmnqpoxiA+TJ4 {_G)s O%DAY aaY( #h @\} x+\y#rbenR(1@ż{?|Ͽ\هy3Xa??j, \`l `Wo>ݽ5,dBxv`# 65v8`r/g:%EP!"Yf򎎩UHQ֢lQ m 6\!R"!a:q 3W6"!bcI";ƗUi9G2^-%wa YN@މDBqa}o_8 ًhuLd;cofiI} b?7U:aX! hUtv?UQ-/?SVDH(̠!@qI^)rӿwx#}?4U˚.GTUSO*NL;1-^?O1?څER: L놯7_Pe'U͙4FcfJ'ͅVMǙ^P7 f8@FGߒf7 Q%k8$]Ec•ѪgWrWS+T5G0[Dr-0G#^}f8k߸٭5^|)-gTpB?ّqtLy]3YsOnc,vmOD7 [^|fk;Mi™uJ7o2l BGR^tEĢyi̡&&l{duCD[¾ gm,Tٜ0aX(15*&WK y] $:!*O2 6M7J$n2WBU$hH$IPIBGCBrr 0aw|Ҝ'в=9߽N۶\X;O;^&3E+n޸k{O[i۶@$MT쓼bo6C^}knLFRY W G1>3_?}߱Ż`ȗN .}w/Z++4/}9+qZqٿ!z5eo{YBJbKf.*:d$YLFh*777m:&(ڵ\yi[7׈I1ZuXfe&[J9[((B5n9bBą&S͝)6+վslh[G.D20b$=Ͱt5 >!}D1`RD}$9G)%%|* :H]QْN'3|OP.r=bOY&))<Ixɿ3QrK-YHƠtn.3:Tf,e2 y+nBKL[[Ub{+g!I8rw!vuݡ|2m3O3 9:<Ļ0 󁦱tρD$5X:_@RT k )ɱ3}]{B10OR075>&r/R:Muox둏%)wJM-Q"(sg%6 > G90)M5!~Qu(m98sg4[׆(9uck}Rsɵ@191N~ N{kBe`r4!3us>O! uXyD >͑kyGGt $"a\VֆF,FWh=Tk(QqɷK*H$DO,QCb4PDD-#!b; E# )|n$)Xz^@fLTD1y5 IDAT>u 'AR~=8O$_FY?{͆$0kn[~0.)eZS)M#t=WXrboŊpFnݝp*fC5ĞM(47rLaT.O8C ;#׆d/*;R uHz{H2tz$P> zJjqv3HtruSԱ*C)z5bI1Dbni<Ĕb k:!E( %%nQez,) ?11$"m 1vAwc 1&[O )B/'k*eCeeD轕$-Re>=Q/Y:3sOL'Z~fbq依by^?ښ矻a^SOm֝)kui:RƟ4MEmh°S*b k5Z 3C˙3ֳ6Xߨ>`L!yajTf f:"AcMz$c1ㆈH9,%ܲ,*P*ک84"1(X%3Rbȏ+HƈKNH9=,U<^NAË"aCQ$YNBL SȷDMӟ=FaS8ZmbXPD4͠FB EpFkj $@CpIrRBfr4ɧ.g|4c&1d`4*n浻lg/[w9DZT$e}mͭAʈEW{Vhmx=jό1@F+UTVcٴlgAm'O])|4sq֙n_Sv[VP>+s b^:p1'-,Q\#tswmO;Dڄq 17 }K׷(ЪG'AI+=oT;_,IrbĬt]X,l2;x R3"LZ,OuND7p"+SeI?9NlU4 <A6oই#T ,}'G67 {pi$!/lb6ڈ$]|ZS#EVJ`ŠFyHbHwnQƇ\[[5;wy:Ͻ IZFk!% 4YC kY]<*wLї-/pu^F7fQ:b(vPi5 m-S)ώ0ֲsԱXh;RT40Ugf1ey޷>{#"3ʬXlfd[LȖD0AKMAlCҋ 6`0 A%Yl3.VUg 9O=-?so܈Z7Oܳ?899VӦRKz2a@4DvI1C$bK4 Ql =גȱRۑ1sDh]J,-,K-U&YWuS[d\ս viڨRw'evD]6=.ŒIT9:.L0awz =?׎szפDQ4{gc^As0dB>UN??y084{H~Ϟ?0z}ϷC>Bǧ1`K[f_Gӝ8N&~@,=0^c uCmߢ(!11{ ODʕ[T>gy;[,Z >Ͽp .bzeV(L~B 8yŅy6GŃllx5\d Mv)ՅEuUdXȀyxq~y:YZ39n.ob̙ld` ԣ5`-F|V'^ycn?lOb1!%\PR|r&ʜZ)`6ZhY/'mfIPH>;9N J:,;䶆n i՝LОa5v+t &НcWeWRq\:5~t`wOq=W?ln> ,bw2W%YvѲ/R0jas~G?LSd, Va[Q} 6&8 4˩F$تd%EbtyB F J#)pM4#LuJth&-X?@ncQ~cX^dTo睍*?$ ɠ;%&- n޼ͨthnq{e/>{Wrm@$p"|!Xʎ( KY9ȧ ؘcKc}Xj8Чmux<r+Ϝ#  jʲ5@.)e1Hrbr'Ȁbe$JHVH*Ÿ%'᥈ :i"8)y%DBW\ n >FC|=nq|Cu 1} yV]B|h :M%It̑O*EӱLq}vFJ`qfi=# O~d8^Ea'AɧS/IvW! M]!ξ l; "9<E,QX*n f08pzP1%CzevĵV=~ M[fR A*[qSrpn?%T-GZڭmbT8pzͨ^g-))`mWJίc(gҀEavgg|pc FNcnPp)IԹ9BCYd(rހɦmyNNwʲç- )? bҠ71\V7Chy %Dk\hhFkfnuCl=$ЦA}%HU!t?+,]tTH>vZFst9:M%]aGӣӅlOQ{H3bJg;j/+3hOywҎE~[Əдjjo!IjL&ujOmȣVPsxiR[diAf"".4ǖ,ۖf#QҩaȥC!Vl76$HI;N_jPJN 3] f9q4ك\m>`aia:Wq-@MAY$E^¢E0!DŘ8w0ls8w0K ۥl4P(呓2WV У@EĂ$0<9ǝ[:6s\+ *,TTp6ϙ yҳY$P|זxNA"FS8$+P(jd2]MBjȠ|"F!& D0W4mS6q]* iߴQ#mI֑"+!l:1څwoyM{ӞSAÀw[V =n'Xt}};}6XhaBp@^:=VN!V(DK/b#$h[-,s*g?/5G=)UG3')",S(eff`=q|αz7\l{=Ne^/:䄬M^SbMf}?}Mkx_Y#"fIUa*[0(,,f3mu̠Ӌ|{<}f卆7Q9xs#(|ll']@IY98H4kt< c;Gݼ!&Tqw1,Gڒ) 防RH1/i ZԠ$B" i4a"֫f j\[|o=fKƑ4ʝF-).2PɤHxA.!ABx*NMC>xb%;okݣ>z19 yCU<A̜)ظX k(ygy۫bt;/>EKoQ }QҒLA2eK< Μ8Wy#%/>s^Yȩdw|lּṇT3 (,P*rK38ZsΈ'zW?Loyu eϢTtb6JQd0PX!0 4uEuQRW$"y$i5K-2;i1 I J\skflj|[asg5F:Ҩ&;4bhRP=mgJKr*GX.#6xG!Uԟr`{dWPT ڵEa1))mwa0l\IMo,9h?3q_XQUN(}9c)lݰ4e6Ky*'-a:ƕ89~ ΞApWƫtE@a(8~tϜUU޻Fˉ9n;o^L笚xk) ZF>)Ʋs_|B/WNdWY_wDA }> AwHa],'p.{ + B(KCasW1hˑolK="֍P-HZu=LHR͆~noaD5)4xCi6fDkI!ɻyH]CjGI1B^ʧۛgGz$GENC5E ( eٱN˧.NZ;H;-?;O6fӮEU?*-b5hʶEarb7fy_[/\{*}s*nb㇗X?ȠW=s6۵Gj)ȉ@buX?ߙL~E%o )LGxaMWj yg둦6b5[@΁T&x,hR%PbcL52kߐQlNv|am= ٽO vapjߌ3 -n_XE3=(VT3Eg'HR4QJMy6֯юӺ 1Gg88/k/_E8{'p oaee׀<6+bҸm =yY~%|;y"֖Y /)QH">*>*.E)RLjהo1$[- R$΂X{,))ReI )J(2Hde+csJma3 y629 yAKp<{B#NӉ9 )Cv?8%5 &ĉƈڵ |Ss_"<=)يL#" IDATo28:s/+I @5Q3 mo~}*oqUi{͈ kP"DM5P4ʁK s N^h]9v)9}dF8?(5[sTC@9CB<)yHtnmloV7DQD)gJΝ]:j`x^y3:" g;nMOh'$ 6Z3*}r$̨A@j=Zg&b>|$Jb"*; ;M-5VP[b$Q` j&$=E_Sx̹[yE|Tnc7YvckĮ=UloQ\u>ԏ9Dc5ĮzL$k᧹&ٙ٬Y(Yc΀|o=;o}+ϩsfk]9EFpC.~/-Dӝɾ;: Ϸ-/; G['8|d˷ТO"(ChG<3ea˳'ꚋW4#$Һ~!6Piڈ-ֆ1619R1yؘHB$0gy"-2ؒf7^9wnf@GgY"ÖJUAUFZjih $V")944J6JLAR్ôiR{ia mhј |V%HaD>X#$[k #};g8!pE1cGS(;@):y|5:{:n;~#԰]mDvOD[MdC'U#(O(>O `ALt~{ɶGYсXV&)Ib5~/o;Pmu2 W'['}q-| ~/|Ű|:gΜS r"0/L""c9QDh$Ν<g9DhMN/̱%Qj7d]C@$d6-1}v(ChE$]q'La:9.9/~ ] ]HP&~ yDA*㠺R>nc~l@b 0nzk,;ʢĵ-ũP+ ˼S_%P&7$TT4^x楟fa4cVE7]إVr]W_gYYE3b s#&$DcI: u1\<#Ta4ܦXX<k˫Y_# @"x"y(ͣ-);jvA5 E|ʒ8{hř߹M=3'x)w 7ya37[t-ö6ǣv+&M"Q(QKC1[4ࠜDPz/8ķh[iH vPshPl91ɑbK 1:y9M Q(`@)@Iդ;` FJfBV8:00v1'O2 S]Ąlv_l9Iu~y sx|5trEz3U| 9%ϛ2l_Mw+Fw}o~ M) uIY-p7~۷ww1}CD &1f7oW7_c<=쀶|BTQ^X )04up{2uhY\X[pM677h- wn&4#Lj1@ 4{T%0)InALϑJ vir̠ϟ:# nj^4VWWxevby.15&rG%F1PH"0oXtRKҼk %C0=Զ[mChp-"bSB`Se+1&Gb0c(ӍLIWQvD,2^.K.y5/L3ː79 rf(䓫/TQIlXK"{Iy>3?S[LI1r(es{W>GOK_=F\w`(YvAѭ>sX?7hG[ [IT39.5t)k'_ӯ__S/vQ mz h"'ћפ98wCKܺLLOȍic03cH>&hp[4SC ٸN#EG;1ߡ)Ԭm+ +|i\KO5bjı9!ﮰFpDZ)<Eţx h(lW*Egd=* %J$I;"z&Q@61.s h뉵#6-iS7Ѷ!6u(|N %T"Y09ێoMIn1`,tM6Er:*#xinK&S#{M;ݴ#O `=h ##%wȅ˗iۚ^ C;ܤ5F&eCc i& =K"՚F:D"ō< >&ň(BJEtTA:h[R[.gKĦ!5nHGPĶ&$א|Kt<хlY5TB P)9j2pHBrW,;c&UF"SXiĖ]vWٍy}3zd3X'X>"r&>ս='F;bro@xQ3(MJp r1@opW_yXfw#Gi$4],PɈJ$ llsuϟa޽M W:"1S6TB0C;s<+?ȑYcacsh `Of@կP";n.FxD,ז~6Ouqr4kAb\ĴqĔO.}ķ=xr1vZk98ϑ>x󋬬I.R ^,.^ݻ8ԭg:6K~˔(#HI2&*M$МLWGGC |R T S;Fz$#A|Ml[kuԠM6zHjH-R]ږ 2! !Gdsga5o`0qQV0e}{;%ԡ^vaz3 YB]r.e wO,Q|2|BGG+~ԏpQf٢w!@H>C_!/jƐ)4FJ_` yIЩg>ý08shBq9ɒE(IQQg2;Lxf^]ChjԵۆx>_/  e )u\Ol!Kp'ǥ/vNOf\7Q(n=Jbvzzqz}5'{;)Bچȓ~ŏýХ}2^ AƮ4sǸ5nܸjU6]*hcfO>ĭ#+\'JYI( կ++>oos;oRU[bP{FuhUT4n8bceSGO8@{oI_бHfZlťHJȀaRDRB=V3Ql42+kw/&hD܀y;}c +VeB>Fkc+4%\KXHݎGD>7`PGK.!7;ݻ}T=<@N?W;{FQT]/m}_0%of& y4#4+j$6OsHť3T۔ %kX߼w N=ϗ9^̏S%7n{358eib)uܾ׸| ++h.-Sgz\YÝ Z#PX-CEf;L^r +=Stn.8I*Jo-޿-̩O |p޵96w1&uO::F3-(n#gIG`g *I;A#|~Y} ,>"Gː=GZ3>͹'ޘ|=D grڸ]%s(xly6HU R )ʎp>۹s W{_+<ok._{.^m gc!mmicnF͐^#m!|8y(n[o]XCĩؒhۈ  H$$)Д]1Se JA Ɓio{^_e6Ĵ N8tU~_e{mvsHO-'lm!.`CFBbG4mdԚ^J$h2 KI%=c( K"ҤD`! G]lG[r,E7[ҟ)D)"LMHR k4f`H\ >{D01 A3ql92y=31=nnIR9%KµO!Nx*'w1cN8!:G}^Br?0&Zs}޻tڝ t'iU!#6G?OQ޻pR|QqVERU/ȯϰb/&n8DS1j)BsJ#u]pԒ(^'xI67[1V SW}jS4ϵI9ɭPK!)(P$v">+$%En7㫚rᛩlml?&ҍW?4?s \zLٗjQ ygtC<IvO K/W_zAnZEIHO\I31Eٖ@ЖȐެep, K? ǏƝe6GrƂg -VrBR jcˮj$ECm@}>%FX۠U>}^Uq=xu~ӯrmV؍"݈K0ăxxxAHmמz4#6Ibt#SA)lAaDGdgajV9Δتf %~AU)V)b$"b zNT" 1TH* CjrwdPWv tkz }5Aua$?}/cQ<N}< #w?LՃ( Ōr3=zof<ƫd> g^{Ϟckmo}pOmEFд@$%X- ,p)c~Dr.,蠁#E @"Fe]_+7XJ]7]r?K_ؑz/3#Mw:6F _F[ ()E VJJ)snyih|C=G rf7rnXR;\Qb0KG2Te@/RDib=b"t|`(A*R*Z!ZJ;L +tz{OKeU S{FO3j'Jc}>jw }x$xauΖܸ~_s~N&") 49gZjH%ŻDp&]9 "`n{ΰr*ûw6Fۼ[Ug 04Fh B! D jFc>SS"RDT8tkXko|޻HeI:|g?X:D4 c;Ų={^INÆRՎA%waPX,=[RU%Brŵ-1ĩHM$>L;*9aEYQ@!BZNSHBf(,Ɩآc,0(+%n ;& Qb#|p߹ Wd:zdOxPWqOTɩv!=vs}{x#ֽ#+c'pxAYZ.豹vaD\ԙ3ʕ^,o{KA)MX0LrkCdxa^xU;2"7^hsvkM61KBCah2H“P9mha1VrugqefVupLk#E-a;0ODL'![cٲeY%Rp@bZ2CfuUc&@Z{=4ZR(uR`gܼy-1׿Wcu)~ ʢf}}3zqE}T ,28-)F#ʺDMEff~/㠯j̰6E,ƫJK[XGӂ&i0$!`cXP 8Tɑ*JHBks$ɲ6`czQ"i-Np$s¡L`(du59%G =q_|Gh{աQ|bߨ&"e.(H_¥Grki u)u0o}_O8w:_$+x_HҠZ >t6(f{?~)1n`@n mhAEqD{ 0B4|ޥDInﰾ œ >7Xy&ш7zxql4I!xjELmN+t^w .bD#uXi%]bI/C[0jtB6Y&ϱyR @LՌ|$;EfbV{<Is"3-=+ޒ29PB᠙ÖG4> C/e+}n;/NrW$ 2dwS\vh*Q \pcs3g³{d:W3_\7< nQcM{BU},{.|-/3*jazf4~1]P"u#(k†jp*8 O4yLԀ@5O(*Q|*f|.33Ͽd{{'M5BHA|_tb]کشj5j$ebɳ<OY(aIvt\qv[`ul< B4QxRE$CL?&|񖆹l:~~ nد=.y Vڂ>VIyv1UV̫Urĉb `|> Y'C9P2ݲɓJ\ɀ{ "bazz,šmϕpcKGl kj _-N4@͝;K uz*, _,kv^ |M(wQ_"eq5s\m"%BWO\Pypȟ)(J>sE~_bOē+q6ǐF#4o-M.cWB( ʲQ~̉&}gIdbrTK"80i lڊ5mLM 8-!xB̝B$݅AMĢQǗKTLV戒snERI~x~ŏ,b8 ^-&iL25n=:n<}T8J +k{Iԉr)=}FQ˘LȹS޼pW/'s.(i1P255wX_oeii[K_7}{Q,둉A:QP7B g j$AIڀꚢ*eS>/rY޻g}:bp~;N !a1)㸺hGm0u@<:PFJ8 !Nb"U H̚::yǘH.aX:%K>ڲ M^ >#{"qH;%6!{8ਓ|1EzKeZ@1ymsD!&8@"-Y^Rnz5:3Lsb& xַ2VV݌DJULMM333<㏟r5O([; ܺ9Ɩw'D 6p!->Ν|wK˵ӟmܻOUV>jO&/ \YF2>h%7K'Q7iVPK v ʣ+޹_?/B1%05ϿKΓ3q  jb}'F^tQۤRKNCS$딺EUQ m :1R:RbƐe&**IACg ce,\2ԁL{)QCoI&|RU(AI3̝~ln)+zXJ!u쩪ͭ-Xb<9yd{cčkx06mnvX.]{,_~T9kUL3,M(T8Jz65ɒdM'+<;K<]{B]ױUEͩϥ+zX[}5*8)R58xsM"cW|5EYRJE;U@V͊#~R*|h'OzLM 5yLal'Z%FGsAU:J;37W{>'~zā7,^I@Q /ǸK]ۡ팹6;41ruOPuĕ= 8Ƚ<n~ +AN͍/]PEhA v[[<]_cq~g1*Gߙ5zBѺB\(cɻă]kucĈ5f>gɕ+ufgOA. qbp&Q4='kWi1!ک>b6BQU]るߧ:>qU!xLj5fYt Z\Ua'ZԧǣyM(bAd|"U ~Ğ*7UӃyfpwQQ3qQUA^`rʧ_W8{֟}fIFD(>3s\xf88so^yQ1bs}c<)CJj1؄-Di ɒT\ Nj:2mDQƶvY+l3ۼuz> "*ҶʹITЧ`vmBؑgj]LWWmZ@9rUE$UbN$d^%gݹτC >si 7C:&1!{J'j6#QnDߏvTI^rq8zľ1# ? |kmy$@c= ȏ2o~Yoí7 =y\z G:=IAH ZLUycEj}(ϲ!`b+AYX\L |/q+V MmrATUH4BJ9%o^ U*娢lw1&6Pc(b%gh&)Cڿc| Dai6ME1܄Sk FE ޶0I#E;";l܍+ܻfW NLlRk"FJQ\|w[O!mS)Μ9=z1iN.Cn|Dk PH`-SD!P"51K7QK[CA([7oM])3/.pfgg >2?۔UEw;:I9F9$]vRĊC^@v'Qq'(!@]ymG+P/36q ¨KT"p8p"x`kl#,SGѸy b {mɖFƾ ǺCdi9izv^51H"C; El"dO0UclWxdddsHёrz 6?͉Y#O L*@377`8$'{=D5jlmo37~̟bέ3ay~UTBQUOa~n\pE߿W7b{gs =1,1)> oZb:qzkHDBk|\CNͳɽ;߱tOq)yU8Ii?]T sm-V.VlZ &kgH+E v**_;`-BӘjHژà㝏[>VnMCw' Ɋ$ב$i]:I{ч{I u^>rD=ԑ&x1ɖ+_0 )0ݧe˒)N@f xgy:2ǮFdY>nP՝Gֹ{ξAfΟg;`k:&{HmNM+`zz( Ƈ铧8qllfPU60 vv [;%>ƠbPŶIUC.^3r%.^ݛ߲nwEIuڌPEW yd6#j+ dLlӰMhVYdPG Ļb%b# OE<۱lc AmUD:T|$?I&KiT(b"&y3ƌ|$=a]vޥHI5q6^EIZV+|d+ɞ lImIENDB`univocity-parsers-2.9.1/pom.xml000066400000000000000000000152601400120543400165430ustar00rootroot00000000000000 4.0.0 com.univocity univocity-parsers 2.9.1 univocity-parsers jar univocity's open source parsers for processing different text formats using a consistent API http://github.com/univocity/univocity-parsers GitHub Issues https://github.com/univocity/univocity-parsers/issues Univocity Software Pty Ltd www.univocity.com Apache 2 http://www.apache.org/licenses/LICENSE-2.0.txt repo A business-friendly OSS license https://github.com/univocity/univocity-parsers scm:git:git://github.com/univocity/univocity-parsers.git scm:git:git@github.com:univocity/univocity-parsers.git jbax Jeronimo Backes jbax@univocity.com +9:30 parsers univocity parser development team parsers@univocity.com ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ org.apache.maven.plugins maven-compiler-plugin 3.1 1.6 1.6 org.apache.maven.plugins maven-enforcer-plugin 1.3.1 enforce-versions install enforce 1.6 org.apache.felix maven-bundle-plugin 2.5.4 com.googlecode.openbeans;resolution:=optional,* bundle-manifest process-classes manifest org.apache.maven.plugins maven-jar-plugin 2.5 true true ${project.build.outputDirectory}/META-INF/MANIFEST.MF test-jar org.apache.maven.plugins maven-surefire-plugin 2.17 -Dfile.encoding=UTF-8 maven-assembly-plugin 2.4 project org.sonatype.plugins nexus-staging-maven-plugin 1.6.2 true ossrh https://oss.sonatype.org/ false org.apache.maven.plugins maven-javadoc-plugin 2.9.1 attach-javadocs package jar ${JDK8_HOME}/bin/javadoc true org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources package jar UTF-8 UTF-8 org.testng testng 6.8.21 test com.univocity univocity-output-tester 2.1 test org.hsqldb hsqldb 2.3.2 test release org.apache.maven.plugins maven-gpg-plugin 1.1 sign-artifacts verify sign univocity-parsers-2.9.1/src/000077500000000000000000000000001400120543400160115ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/000077500000000000000000000000001400120543400167355ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/000077500000000000000000000000001400120543400176565ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/000077500000000000000000000000001400120543400204345ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/000077500000000000000000000000001400120543400224655ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/000077500000000000000000000000001400120543400241445ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/000077500000000000000000000000001400120543400265015ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/BooleanString.java000066400000000000000000000041271400120543400321160ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates that parsed values such as "y", "No" or "null" should be interpreted as boolean values. * If a parsed value exists in {@link BooleanString#trueStrings()}, then the field will receive true. * If a parsed value exists in {@link BooleanString#falseStrings()} then the field will receive false. *

A {@link BooleanConversion} will be assigned to this field *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface BooleanString { /** * A set of Strings that represent the boolean value {@code true} (e.g. "y", "yes", "1") * * @return Strings that represent {@code true} */ String[] trueStrings(); /** * A set of Strings that represent the boolean value {@code false} (e.g. "n", "no", "0") * * @return Strings that represent {@code false} */ String[] falseStrings(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Convert.java000066400000000000000000000041221400120543400307630ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Assigns a custom implementation of {@link Conversion} to be executed ({@link Conversion#execute(Object)}) * when writing to the field and reverted ({@link Conversion#revert(Object)}) when reading from the field. * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Convert { /** * A user provided implementation of {@link Conversion} which will be instantiated using the arguments provided by {@link Convert#args()} * @return custom class used to convert values */ @SuppressWarnings("rawtypes") Class conversionClass(); /** * The arguments to use when invoking the constructor of the class given by {@link Convert#conversionClass()}. * @return list of arguments create a new instance of the custom conversion class. */ String[] args() default {}; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Copy.java000066400000000000000000000056751400120543400302730ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import java.lang.annotation.*; /** * Allows copying values of meta-annotation properties into the properties of an annotation that composes it. For example, * consider the {@code @MyReplacement} meta-annotation defined below as: * *

 * 
 *
 * {@literal @}Replace(expression = "`", replacement = "")
 * {@literal @}Parsed
 * public {@literal @}interface MyReplacement {
 *
 *     {@literal @}Copy(to = Parsed.class)
 *     String field() default "";
 *
 *     {@literal @}Copy(to = Parsed.class, property = "index")
 *     int myIndex() default -1;
 * }
 * 
 * 
* * Values set on attributes {@code field} or {@code myIndex} in {@code @MyReplacement} will be assigned to the * attributes {@code field} and {@code index} of the {@code @Parsed} annotation. This allows you to apply the * {@code @MyReplacement} annotation to any given field of your class while configuring the field name and index * to be set for the {@code @Parsed} annotation. This eliminates the need for adding explicit, additional annotations and * their specific property values to each and every field. * * The following class can now make use of the {@code @MyReplacement} annotation to apply the the annotations * {@code @Replace} and {@code @Parsed}, configuring the properties of the "inherited" {@code @Parsed}: * *
 * 
 * public class MyBean {
 *
 *     {@literal @}MyReplacement
 *     public String id;
 *
 *     {@literal @}MyReplacementUpperCase(field = "client_name")
 *     public String name;
 *
 *     {@literal @}MyReplacementUpperCase(myIndex = 4)
 *     public String address;
 * }
 * 
 * 
*/ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target(ElementType.METHOD) public @interface Copy { /** * Target annotation class that is part of a meta-annotation. * * @return the class whose properties will be set from a given attribute of a meta-annotation */ Class to(); /** * Target property of the given annotation class that is part of a meta-annotation. * * @return the name of the property in the given annotation class that should receive the value of the * meta-annotation property. */ String property() default ""; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/EnumOptions.java000066400000000000000000000101251400120543400316230ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * The {@code EnumSelector} annotation is meant to be used in conjunction with enumeration attributes. * *

Values parsed from the input will be matched against one or more properties of the enumeration type. * By default, values read from the input will be matched against:

*
    *
  • {@link Enum#name()} - the name of the elements in the enumeration type
  • *
  • {@link Enum#ordinal()} - the ordinal of the elements in the enumeration type
  • *
  • {@link Enum#toString()} - the {@code String} representation of the elements in the enumeration type
  • *
* * You can also define a {@link #customElement()} of your enumeration type (an attribute or method), as long as it * uniquely identifies each value of your enumeration type. * *

Use the {@link #selectors()} option to choose which properties to match the parsed input against, and in what order. You will only need to * explicitly add a {@link EnumSelector#CUSTOM_FIELD} or {@link EnumSelector#CUSTOM_METHOD} to the list of {@link #selectors()} if your {@link #customElement()} name * could point to both an attribute and a method in your enumeration.

* *

This will assign an {@link EnumConversion} to this field.

* *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface EnumOptions { /** *

A list of properties of the enumeration type that will be matched against the parsed input to identify which enum element should be assigned to the annotated field. * By default, values read from the input will be matched against:

*
    *
  • {@link Enum#name()} - the name of the elements in the enumeration type
  • *
  • {@link Enum#ordinal()} - the ordinal of the elements in the enumeration type
  • *
  • {@link Enum#toString()} - the {@code String} representation of the elements in the enumeration type
  • *
* @return the sequence of properties of the enumeration type to match against the parsed input. */ EnumSelector[] selectors() default {EnumSelector.NAME, EnumSelector.ORDINAL, EnumSelector.STRING}; /** * * Defines the name of a custom element (attribute or method) of the annotated enumeration. This will be used to match the parsed input and identify an individual value of the enumeration. * The attribute value, or object returned from the method, should uniquely identify a value of the enumeration; * *

You will only need to explicitly add a {@link EnumSelector#CUSTOM_FIELD} or {@link EnumSelector#CUSTOM_METHOD} to the list of {@link #selectors()} * if your {@link #customElement()} name could point to both an attribute and a method in your enumeration.

* * @return the name of a custom element (attribute or method) of the enumeration which will match the parsed input and identify an enumeration's value. */ String customElement() default ""; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/FixedWidth.java000066400000000000000000000053051400120543400314060ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.fixed.*; import java.lang.annotation.*; /** * The {@code @FixedWidth} annotation, along with the {@link Parsed} annotation, allows users to configure the length, * alignment and padding of fields parsed/written using the {@link FixedWidthParser} and {@link FixedWidthWriter} * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @author Univocity Software Pty Ltd - dev@univocity.com * @see FixedWidthFields * @see FixedWidthParser * @see FixedWidthWriter * @see FixedWidthParserSettings * @see FixedWidthWriterSettings * @see BeanProcessor * @see BeanWriterProcessor */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface FixedWidth { /** * Sets the length of the fixed-width field * * @return length of the fixed-width field */ int value() default -1; /** * Sets the alignment of the fixed-width field * * @return alignment of the fixed-width field */ FieldAlignment alignment() default FieldAlignment.LEFT; /** * Sets the padding character of the fixed-width field * * @return padding of the fixed-width field */ char padding() default ' '; /** * Configures whether to retain the padding character when parsing values for this field * * (defaults to {@code false}) * * @return flag indicating the padding character should be kept in the parsed value */ boolean keepPadding() default false; /** * Defines the starting position of the fixed-width field * * @return Defines the starting position of the fixed-width field */ int from() default -1; /** * Defines the end position of the fixed-width field * * @return Defines the end position of the fixed-width field */ int to() default -1; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Format.java000066400000000000000000000065641400120543400306070ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.BeanProcessor; import com.univocity.parsers.common.processor.BeanWriterProcessor; import com.univocity.parsers.conversions.Conversion; import com.univocity.parsers.conversions.Conversions; import java.lang.annotation.*; /** * Indicates that a parsed value is formatted and must be parsed before being assigned. *

The {@link Conversion} type assigned to this field will depend on its type.

*

Multiple format masks can be tried for a single value.

*

When reading from this value (for writing to a given output), the first mask declared in {@link Format#formats()} will be used to produce its String representation.

*

The {@link #options()} is an optional configuration, with properties and values separated by =.. Each property will be used configure the underlying formatter. For example, * if the parsed value is a BigDecimal, and the format is '#0,00', the decimal separator must be set to ','. To specify this using the {@link #options()} annotation, use: * *

    *
  • formats="#0,00", options="decimalSeparator=,".
  • *
  • The "decimalSeparator" property will be used to identify which method in DecimalFormat to invoke. In this case, the method "setDecimalSeparator", with the value on the right hand side of the = operator
  • *
*

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor}

* * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Format { /** * Formats that define how a value can be formatted. When reading, the values parsed from the input will be parsed according to the specified format. If multiple formats * are defined, the first successful parsed value will be used. When writing, the first format defined in the sequence of formats will be used to produce the correct * String representation. * @return the sequence of formats to use. */ String[] formats(); /** * Defines a sequence of properties and their values, used to configure the underlying formatter. Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3", "locale=en"} * @return a sequence of properties available in the underlying formatter and their respective values */ String[] options() default {}; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/HeaderTransformer.java000066400000000000000000000136351400120543400327670ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import java.lang.reflect.*; /** * A transformer of headers used in {@link Nested} attributes. Used to reassign header names/indexes of * attributes of a {@link Nested} class which is useful when multiple {@link Nested} attributes of the same type are * used within a class. For example, consider the {@code Wheel} class defined as: * *

 * public class Wheel {
 *     @Parsed
 *     String brand;
 *
 *     @Parsed
 *     int miles;
 * }
 *  

* * And a {@code Car} which has four {@code Wheels}: * *

 * public static class Car {
 * 		@Nested
 * 		Wheel frontLeft;
 *
 * 		@Nested
 * 		Wheel frontRight;
 *
 * 		@Nested
 * 		Wheel rearLeft;
 *
 * 		@Nested
 * 		Wheel rearRight;
 * }
 *  

* * The {@code HeaderTransformer} allows us to "rename" the attributes of each different {@code Wheel} of the {@code Car} * so that input columns can be assigned to the appropriate places. * * Assuming an input with headers {@code frontLeftWheelBrand,frontLeftWheelMiles,frontRightWheelBrand,frontRightWheelMiles,rearLeftWheelBrand,rearLeftWheelMiles,rearRightWheelBrand,rearRightWheelMiles}, * a {@code HeaderTransformer} can be created like this to assign a prefix in front of the header names derived from {@code Wheel} (originally just "brand" and "miles"): * *

 * public static class PrefixTransformer extends HeaderTransformer {
 *
 * 		private String prefix;
 *
 * 		public PrefixTransformer(String... args) {
 * 			prefix = args[0];
 *        }
 *
 * 		@Override
 * 		public String transformName(Field field, String name) {
 * 			return prefix + Character.toUpperCase(name.charAt(0)) + name.substring(1);
 *        }
 * }
 * 

* * This allows us to to define the {@code Car} class as: * *

 * public static class Car {
 * 		@Nested(headerTransformer = PrefixTransformer.class, args = "frontLeftWheel")
 * 		Wheel frontLeft;
 *
 * 		@Nested(headerTransformer = PrefixTransformer.class, args = "frontRightWheel")
 * 		Wheel frontRight;
 *
 * 		@Nested(headerTransformer = PrefixTransformer.class, args = "rearLeftWheel")
 * 		Wheel rearLeft;
 *
 * 		@Nested(headerTransformer = PrefixTransformer.class, args = "rearRightWheel")
 * 		Wheel rearRight;
 * }
 * 

* * The above annotation will prefix the {@code frontLeft} fields ("brand" and "miles") with "frontLeftWheel", effectively * forming the header "frontLeftWheelBrand" and "frontLeftWheelMiles", which will match the input headers and assign the * correct values to the correct {@code Wheel} instance. * * IMPORTANT It is mandatory to define a constructor that takes {@code String[]} as a parameter. The actual * parameter values come from {@link Nested#args()} to allow custom configuration of the concrete {@code HeaderTransformer} instance. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public abstract class HeaderTransformer { public final String transformName(AnnotatedElement element, String name) { if (element instanceof Field) { return transformName((Field) element, name); } else { return transformName((Method) element, name); } } public final int transformIndex(AnnotatedElement element, int index) { if (element instanceof Field) { return transformIndex((Field) element, index); } else { return transformIndex((Method) element, index); } } /** * Transforms a header name * * @param field the field of a {@link Nested} class whose header will be transformed * @param name the current header name associated with the field of the {@link Nested} class * * @return the transformed header name to be used to read/write values from/to the given field. */ public String transformName(Field field, String name) { return name; } /** * Transforms a header index * * @param field the field of a {@link Nested} class whose header will be transformed * @param index the current column position associated with the field of the {@link Nested} class * * @return the transformed position to be used to read/write values from/to the given field. */ public int transformIndex(Field field, int index) { return index; } /** * Transforms a header name * * @param method the method of a {@link Nested} class whose header will be transformed * @param name the current header name associated with the method of the {@link Nested} class * * @return the transformed header name to be used to read/write values from/to the given method. */ public String transformName(Method method, String name) { return name; } /** * Transforms a header index * * @param method the method of a {@link Nested} class whose header will be transformed * @param index the current column position associated with the method of the {@link Nested} class * * @return the transformed position to be used to read/write values from/to the given method. */ public int transformIndex(Method method, int index) { return index; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Headers.java000066400000000000000000000057401400120543400307250ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import java.lang.annotation.*; /** * The {@code Headers} annotation allows java beans to configure how to handle field names in a given input/output * *

With this annotation, you can configure the sequence of headers to use when reading/writing:

* *
    *
  • when reading, the given {@link #sequence()} of header names will be used to refer to each column, irrespective of whether or not the input contains a header row. * If empty, and no headers have been defined in {@link CommonSettings#getHeaders()}, the parser will automatically use the first row in the input as the header row, * unless the fields in the bean have been annotated using {@link Parsed#index()} only. *
  • *
  • when writing, the given {@link #sequence()} of names will be used to refer to each column and will be used for writing the header row if {@link #write()} is enabled. * If empty, and no headers have been defined in {@link CommonSettings#getHeaders()}, the names given by attributes annotated with {@link Parsed#field()} will be used. *
  • *
* * *

* This annotation has no effect if {@link CommonSettings#isAutoConfigurationEnabled()} evaluates to {@code false}. *

* * @see BeanWriterProcessor * @see BeanProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) public @interface Headers { /** * Returns the sequence of header names in the input/output * * @return the sequence of header names in the input/output */ String[] sequence() default {}; /** * Indicates whether a row with headers should be written to the output. * @return a flag indicating whether to write the headers to the output when writing instances of a java bean. */ boolean write() default true; /** * Indicates whether the first row of on the input should be extracted as a header row. * @return a flag indicating whether to extract the headers from the first valid row when reading. If */ boolean extract() default false; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/LowerCase.java000066400000000000000000000027571400120543400312430ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates the String value of a field must be converted to lower case using {@link LowerCaseConversion}. * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface LowerCase { } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Nested.java000066400000000000000000000045621400120543400305750ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import java.lang.annotation.*; /** * Marks a field as a nested object to be constructed with the values of the current row. It is expected that * the annotated attribute is of a type, or provides an explicit type via the {@link #type()} option, * that contains one or more {@link Parsed} annotations. The given type and its {@link Parsed} annotations will * determine which fields from each row should be used to populate the {@code Nested} instance. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Nested { /** * Defines the concrete type of nested object to be instantiated, if it has to be a subclass of the declared attribute type. * * @return the type of nested object to be instantiated. */ Class type() default Object.class; /** * Provides a {@link HeaderTransformer} for reassigning header names/positions of the nested class. * * @return a transformation to be applied over headers/positions of the nested class. * Used for reassigning specific input columns so the correct values end up in the correct nested attributes. */ Class headerTransformer() default HeaderTransformer.class; /** * An optional sequence of arguments for creating an instance of the given {@link #headerTransformer()}. * * @return the initialization arguments passed into the constructore of the {@link HeaderTransformer} */ String[] args() default {}; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/NullString.java000066400000000000000000000034601400120543400314500ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates that parsed values such as "", "?" or "null" should be interpreted as null. If a parsed value exists in {@link NullString#nulls()}, then the field must be set to null. *

A {@link NullStringConversion} will be assigned to this field *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface NullString { /** * A set of Strings that represent a null value instead of a valid String (e.g. "?", "empty", "null" ) * @return Strings that represent {@code null} */ String[] nulls(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Parsed.java000066400000000000000000000114561400120543400305710ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates the field is parsed. If the annotated field type is not a String, it will be automatically converted using one * of the existing {@link Conversion} implementations in package {@link com.univocity.parsers.conversions}. * * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} *

Implementation note: All annotations in @Parsed fields are processed by {@link AnnotationHelper} * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * @see AnnotationHelper */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Parsed { /** * The possible field names of a record. If multiple names are provided, the parser/writer will * attempt to match the given names against the headers provided (i.e. headers found in the input when parsing with * {@link CommonParserSettings#isHeaderExtractionEnabled()}, or manually set using * {@link com.univocity.parsers.common.CommonSettings#setHeaders(String...)} for writing or parsing) * * @return the possible field names (optional if the index is provided) */ String[] field() default {}; /** * Field position in a parsed record * * @return the position of this field (optional if the field name is provided) */ int index() default -1; /** * The default value to assign to this field in the parsed value is null *

The String literal "null" will be interpreted as a regular null. *

Use "'null"' if you want the default value to be the string "null" * *

this value will have different effects depending on the field type: *

    *
  • on fields of type {@link java.util.Date} or {@link java.util.Calendar}: if the null value is "now", the result of new Date() or Calendar.getInstance() will be used. *
  • on numeric fields (primitives, wrappers and {@link java.math.BigDecimal} and {@link java.math.BigInteger}): if the null value contains a number, e.g. "50.01", it will be parsed and assigned to the field. *
  • on boolean and Boolean fields: if the null value contains a String, the result of Boolean.valueOf(defaultNullRead()) will assigned to the field. *
  • on char and Character fields: if the null value contains a String, the result of defaultNullRead().charAt(0) will assigned to the field. * An exception will be thrown if the length of this String is different than 1 *
* * @return the default String to return when the parsed value is null */ String defaultNullRead() default "null"; /** * The default value to read from this field if it is null. Used for writing to an output by {@link BeanWriterProcessor}. *

The String literal "null" will be interpreted as a regular {@code null}. *

Use "'null"' if you want the default value to be the string {@code "null"} * * @return default String to write when the input is null. */ String defaultNullWrite() default "null"; /** * Flag to indicate whether the parsed field should be converted automatically based on the field type. For example, * if the annotated field is a {@code BigDecimal}, then {@link BigDecimalConversion} will be used to convert Strings to BigDecimal when reading * and BigDecimal to String when writing. You may want to disable the default field conversion when using custom conversions through * {@link BeanWriterProcessor#convertFields(Conversion...)},{@link BeanWriterProcessor#convertIndexes(Conversion...)} or * {@link BeanWriterProcessor#convertAll(Conversion...)}. * * @return flag indicating whether the default conversion, based on the field type, is to be applied for this field. */ boolean applyDefaultConversion() default true; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Replace.java000066400000000000000000000034721400120543400307250ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates the String value of a field must have some of its contents replaced using {@link RegexConversion}. * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Replace { /** * The regular expression to be executed against the String value of the annotated field * @return the regular expression */ String expression(); /** * The replacement string to substitute any contents matched by the given {@link Replace#expression()} * @return the replacement string */ String replacement(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Trim.java000066400000000000000000000027571400120543400302720ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates the String value of a field must be trimmed using {@link TrimConversion}. * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Trim { int length() default -1; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/UpperCase.java000066400000000000000000000027561400120543400312450ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Indicates the String value of a field must be converted to uppercase using {@link UpperCaseConversion}. * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface UpperCase { } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/Validate.java000066400000000000000000000054131400120543400311000ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.annotations; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; /** * Performs basic validations against the String representation of the value found in the annotated field. * A validation failure will generate a {@link com.univocity.parsers.common.DataValidationException}. * * By default, nulls and blanks are not allowed. * *

Commonly used for java beans processed using {@link BeanProcessor} and/or {@link BeanWriterProcessor} * * @see Conversion * @see Conversions * @see BeanProcessor * @see BeanWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Validate { /** * Indicates whether this field can be {@code null} * @return {@code true} true if nulls are allowed, {@code false} otherwise */ boolean nullable() default false; /** * Indicates whether this field can be blank (i.e. contain 0 or more whitespaces, where * a whitespace is any character {@code <= ' '} * @return {@code true} true if blanks are allowed, {@code false} otherwise */ boolean allowBlanks() default false; /** * Ensures that the value of this field matches a given regular expression. * @return the regular expression that determines an expected format for the given value */ String matches() default ""; /** * Ensures that the value of this field is one of a given set of alternatives * * @return the sequence of allowed values */ String[] oneOf() default {}; /** * Ensures that the value of this field does is not an unwanted value. * * @return the sequence of disallowed values */ String[] noneOf() default {}; /** * User provided implementations of {@link Validator} which will be executed * in sequence after the validations specified in this annotation execute. * * @return custom classes to be used to validate any value associated with this field. */ Class[] validators() default {}; } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/000077500000000000000000000000001400120543400301435ustar00rootroot00000000000000AnnotationHelper.java000066400000000000000000001346621400120543400342150ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.helpers; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.Format; import com.univocity.parsers.common.*; import com.univocity.parsers.common.beans.*; import com.univocity.parsers.common.input.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.math.*; import java.text.*; import java.util.*; /** * Helper class to process fields annotated with {@link Parsed} * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class AnnotationHelper { private static AnnotatedElement lastProcessedElement; private static Class lastProcessedAnnotationType; private static Annotation lastAnnotationFound; private AnnotationHelper() { } /** * Converts the special "null" strings that might be provided by {@link Parsed#defaultNullRead() and Parsed#defaultNullWrite()} * * @param defaultValue The string returned by {@link Parsed#defaultNullRead() and Parsed#defaultNullWrite()} * * @return the default value if it is not the String literal "null" or "'null'". *

If "null" was provided, then null will be returned. *

If "'null'" was provided, then "null" will be returned. */ private static String getNullValue(String defaultValue) { if ("null".equals(defaultValue)) { return null; } if ("'null'".equals(defaultValue)) { return "null"; } return defaultValue; } public static String getNullWriteValue(AnnotatedElement target, Parsed parsed) { if (parsed == null) { return null; } return getNullValue(AnnotationRegistry.getValue(target, parsed, "defaultNullWrite", parsed.defaultNullWrite())); } public static String getNullReadValue(AnnotatedElement target, Parsed parsed) { if (parsed == null) { return null; } return getNullValue(AnnotationRegistry.getValue(target, parsed, "defaultNullRead", parsed.defaultNullRead())); } /** * Identifies the proper conversion for a given type and an annotation from the package {@link com.univocity.parsers.annotations} * * @param classType the type to have conversions applied to * @param annotation the annotation from {@link com.univocity.parsers.annotations} that identifies a {@link Conversion} instance. * * @return The {@link Conversion} that should be applied to the type */ @SuppressWarnings("rawtypes") public static Conversion getConversion(Class classType, Annotation annotation) { return getConversion(classType, null, annotation, null, null); } public static EnumConversion createDefaultEnumConversion(Class fieldType, String nullRead, String nullWrite){ Enum nullReadValue = nullRead == null ? null : Enum.valueOf(fieldType, nullRead); return new EnumConversion(fieldType, nullReadValue, nullWrite, null, EnumSelector.NAME, EnumSelector.ORDINAL, EnumSelector.STRING); } @SuppressWarnings({"rawtypes", "unchecked"}) public static Conversion getConversion(Class fieldType, AnnotatedElement target, Annotation annotation, String nullRead, String nullWrite) { try { Class annType = annotation.annotationType(); if (annType == NullString.class) { NullString nullString = (NullString) annotation; String[] nulls = AnnotationRegistry.getValue(target, nullString, "nulls", nullString.nulls()); return Conversions.toNull(nulls); } else if (annType == Validate.class) { Validate validate = (Validate) annotation; boolean nullable = AnnotationRegistry.getValue(target, validate, "nullable", validate.nullable()); boolean allowBlanks = AnnotationRegistry.getValue(target, validate, "allowBlanks", validate.allowBlanks()); String[] oneOf = AnnotationRegistry.getValue(target, validate, "oneOf", validate.oneOf()); String[] noneOf = AnnotationRegistry.getValue(target, validate, "noneOf", validate.noneOf()); String matches = AnnotationRegistry.getValue(target, validate, "matches", validate.matches()); Class[] validators = AnnotationRegistry.getValue(target, validate, "validators", validate.validators()); return new ValidatedConversion(nullable, allowBlanks, oneOf, noneOf, matches, validators); } else if (annType == EnumOptions.class) { if (!fieldType.isEnum()) { if (target == null) { throw new IllegalStateException("Invalid " + EnumOptions.class.getName() + " instance for converting class " + fieldType.getName() + ". Not an enum type."); } else { throw new IllegalStateException("Invalid " + EnumOptions.class.getName() + " annotation on " + describeElement(target) + ". Attribute must be an enum type."); } } EnumOptions enumOptions = ((EnumOptions) annotation); String customElement = AnnotationRegistry.getValue(target, enumOptions, "customElement", enumOptions.customElement()); String element = customElement.trim(); if (element.isEmpty()) { element = null; } Enum nullReadValue = nullRead == null ? null : Enum.valueOf(fieldType, nullRead); EnumSelector[] selectors = AnnotationRegistry.getValue(target, enumOptions, "selectors", enumOptions.selectors()); return new EnumConversion(fieldType, nullReadValue, nullWrite, element, selectors); } else if (annType == Trim.class) { Trim trim = ((Trim) annotation); int length = AnnotationRegistry.getValue(target, trim, "length", trim.length()); if (length == -1) { return Conversions.trim(); } else { return Conversions.trim(length); } } else if (annType == LowerCase.class) { return Conversions.toLowerCase(); } else if (annType == UpperCase.class) { return Conversions.toUpperCase(); } else if (annType == Replace.class) { Replace replace = ((Replace) annotation); String expression = AnnotationRegistry.getValue(target, replace, "expression", replace.expression()); String replacement = AnnotationRegistry.getValue(target, replace, "replacement", replace.replacement()); return Conversions.replace(expression, replacement); } else if (annType == BooleanString.class) { if (fieldType != boolean.class && fieldType != Boolean.class) { if (target == null) { throw new DataProcessingException("Invalid usage of " + BooleanString.class.getName() + ". Got type " + fieldType.getName() + " instead of boolean."); } else { throw new DataProcessingException("Invalid annotation: " + describeElement(target) + " has type " + fieldType.getName() + " instead of boolean."); } } BooleanString boolString = ((BooleanString) annotation); String[] falseStrings = AnnotationRegistry.getValue(target, boolString, "falseStrings", boolString.falseStrings()); String[] trueStrings = AnnotationRegistry.getValue(target, boolString, "trueStrings", boolString.trueStrings()); Boolean valueForNull = nullRead == null ? null : BooleanConversion.getBoolean(nullRead, trueStrings, falseStrings); if (valueForNull == null && fieldType == boolean.class) { valueForNull = Boolean.FALSE; } return Conversions.toBoolean(valueForNull, nullWrite, trueStrings, falseStrings); } else if (annType == Format.class) { Format format = ((Format) annotation); String[] formats = AnnotationRegistry.getValue(target, format, "formats", format.formats()); String[] options = AnnotationRegistry.getValue(target, format, "options", format.options()); Locale locale = extractLocale(options); TimeZone timezone = extractTimeZone(options); Conversion conversion = null; if (fieldType == BigDecimal.class) { BigDecimal defaultForNull = nullRead == null ? null : new BigDecimal(nullRead); conversion = Conversions.formatToBigDecimal(defaultForNull, nullWrite, formats); } else if (Number.class.isAssignableFrom(fieldType) || (fieldType.isPrimitive()) && fieldType != boolean.class && fieldType != char.class) { conversion = Conversions.formatToNumber(formats); ((NumericConversion) conversion).setNumberType(fieldType); } else { Date dateIfNull = null; if (nullRead != null) { if ("now".equalsIgnoreCase(nullRead)) { dateIfNull = new Date(); } else { if (formats.length == 0) { throw new DataProcessingException("No format defined"); } SimpleDateFormat sdf = new SimpleDateFormat(formats[0], locale); sdf.setTimeZone(timezone); dateIfNull = sdf.parse(nullRead); } } if (Date.class == fieldType) { conversion = Conversions.toDate(timezone, locale, dateIfNull, nullWrite, formats); } else if (Calendar.class == fieldType) { Calendar calendarIfNull = null; if (dateIfNull != null) { calendarIfNull = Calendar.getInstance(); calendarIfNull.setTime(dateIfNull); calendarIfNull.setTimeZone(timezone); } conversion = Conversions.toCalendar(timezone, locale, calendarIfNull, nullWrite, formats); } } if (conversion != null) { if (options.length > 0) { if (conversion instanceof FormattedConversion) { Object[] formatters = ((FormattedConversion) conversion).getFormatterObjects(); for (Object formatter : formatters) { applyFormatSettings(formatter, options); } } else { throw new DataProcessingException("Options '" + Arrays.toString(options) + "' not supported by conversion of type '" + conversion.getClass() + "'. It must implement " + FormattedConversion.class); } } return conversion; } } else if (annType == Convert.class) { Convert convert = ((Convert) annotation); String[] args = AnnotationRegistry.getValue(target, convert, "args", convert.args()); Class conversionClass = AnnotationRegistry.getValue(target, convert, "conversionClass", convert.conversionClass()); return (Conversion) newInstance(Conversion.class, conversionClass, args); } if (fieldType == String.class && (nullRead != null || nullWrite != null)) { return new ToStringConversion(nullRead, nullWrite); } return null; } catch (DataProcessingException ex) { throw ex; } catch (Throwable ex) { if (target == null) { throw new DataProcessingException("Unexpected error identifying conversions to apply over type " + fieldType, ex); } else { throw new DataProcessingException("Unexpected error identifying conversions to apply over " + describeElement(target), ex); } } } private static String extractOption(String[] options, String key) { for (int i = 0; i < options.length; i++) { if (options[i] != null && options[i].trim().toLowerCase().startsWith(key)) { String out = options[i].split("=")[1].trim(); options[i] = null; return out; } } return null; } private static TimeZone extractTimeZone(String[] options) { String code = extractOption(options, "timezone="); if (code != null) { return TimeZone.getTimeZone(code); } return TimeZone.getDefault(); } private static Locale extractLocale(String[] options) { String locale = extractOption(options, "locale="); if (locale != null) { String languageCode; String countryCode; String variant; CharAppender appender = new DefaultCharAppender(100, "", 0); int j = 0; char ch; for (; j < locale.length() && Character.isLetterOrDigit((ch = locale.charAt(j))); j++, appender.append(ch)) ; languageCode = appender.getAndReset(); for (++j; j < locale.length() && Character.isLetterOrDigit((ch = locale.charAt(j))); j++, appender.append(ch)) ; countryCode = appender.getAndReset(); for (++j; j < locale.length() && Character.isLetterOrDigit((ch = locale.charAt(j))); j++, appender.append(ch)) ; variant = appender.getAndReset(); return new Locale(languageCode, countryCode, variant); } return Locale.getDefault(); } public static T newInstance(Class parent, Class type, String[] args) { if (!parent.isAssignableFrom(type)) { throw new DataProcessingException("Not a valid " + parent.getSimpleName() + " class: '" + type.getSimpleName() + "' (" + type.getName() + ')'); } try { Constructor constructor = type.getConstructor(String[].class); return (T) constructor.newInstance((Object) args); } catch (NoSuchMethodException e) { if (args.length == 0) { try { return type.newInstance(); } catch (Exception ex) { throw new DataProcessingException("Unexpected error instantiating custom " + parent.getSimpleName() + " class '" + type.getSimpleName() + "' (" + type.getName() + ')', e); } } throw new DataProcessingException("Could not find a public constructor with a String[] parameter in custom " + parent.getSimpleName() + " class '" + type.getSimpleName() + "' (" + type.getName() + ')', e); } catch (Exception e) { throw new DataProcessingException("Unexpected error instantiating custom " + parent.getSimpleName() + " class '" + type.getSimpleName() + "' (" + type.getName() + ')', e); } } /** * Identifies the proper conversion for a given type * * @param fieldType The type of field to have conversions applied to. * @param target the annotated attribute of method that has the annotation * @param parsed the {@link Parsed} annotation from {@link com.univocity.parsers.annotations}. * * @return The {@link Conversion} that should be applied to the field type */ @SuppressWarnings({"rawtypes", "unchecked"}) public static Conversion getDefaultConversion(Class fieldType, AnnotatedElement target, Parsed parsed) { String nullRead = getNullReadValue(target, parsed); Object valueIfStringIsNull = null; ObjectConversion conversion = null; if (fieldType == Boolean.class || fieldType == boolean.class) { conversion = Conversions.toBoolean(); valueIfStringIsNull = nullRead == null ? null : Boolean.valueOf(nullRead); } else if (fieldType == Character.class || fieldType == char.class) { conversion = Conversions.toChar(); if (nullRead != null && nullRead.length() > 1) { throw new DataProcessingException("Invalid default value for character '" + nullRead + "'. It should contain one character only."); } valueIfStringIsNull = nullRead == null ? null : nullRead.charAt(0); } else if (fieldType == Byte.class || fieldType == byte.class) { conversion = Conversions.toByte(); valueIfStringIsNull = nullRead == null ? null : Byte.valueOf(nullRead); } else if (fieldType == Short.class || fieldType == short.class) { conversion = Conversions.toShort(); valueIfStringIsNull = nullRead == null ? null : Short.valueOf(nullRead); } else if (fieldType == Integer.class || fieldType == int.class) { conversion = Conversions.toInteger(); valueIfStringIsNull = nullRead == null ? null : Integer.valueOf(nullRead); } else if (fieldType == Long.class || fieldType == long.class) { conversion = Conversions.toLong(); valueIfStringIsNull = nullRead == null ? null : Long.valueOf(nullRead); } else if (fieldType == Float.class || fieldType == float.class) { conversion = Conversions.toFloat(); valueIfStringIsNull = nullRead == null ? null : Float.valueOf(nullRead); } else if (fieldType == Double.class || fieldType == double.class) { conversion = Conversions.toDouble(); valueIfStringIsNull = nullRead == null ? null : Double.valueOf(nullRead); } else if (fieldType == BigInteger.class) { conversion = Conversions.toBigInteger(); valueIfStringIsNull = nullRead == null ? null : new BigInteger(nullRead); } else if (fieldType == BigDecimal.class) { conversion = Conversions.toBigDecimal(); valueIfStringIsNull = nullRead == null ? null : new BigDecimal(nullRead); } else if (Enum.class.isAssignableFrom(fieldType)) { conversion = Conversions.toEnum(fieldType); } if (conversion != null) { conversion.setValueIfStringIsNull(valueIfStringIsNull); conversion.setValueIfObjectIsNull(getNullWriteValue(target, parsed)); } return conversion; } /** * Returns the default {@link Conversion} that should be applied to the field based on its type. * * @param target The field or method whose values must be converted from a given parsed String. * * @return The default {@link Conversion} applied to the given field. */ @SuppressWarnings("rawtypes") public static Conversion getDefaultConversion(AnnotatedElement target) { Parsed parsed = findAnnotation(target, Parsed.class); return getDefaultConversion(getType(target), target, parsed); } /** * Applied the configuration of a formatter object ({@link SimpleDateFormat}, {@link NumberFormat} and others). * * @param formatter the formatter instance * @param propertiesAndValues a sequence of key-value pairs, where the key is a property of the formatter * object to be set to the following value via reflection */ public static void applyFormatSettings(Object formatter, String[] propertiesAndValues) { if (propertiesAndValues.length == 0) { return; } Map values = new HashMap(); for (String setting : propertiesAndValues) { if (setting == null) { continue; } String[] pair = setting.split("="); if (pair.length != 2) { throw new DataProcessingException("Illegal format setting '" + setting + "' among: " + Arrays.toString(propertiesAndValues)); } values.put(pair[0], pair[1]); } try { for (PropertyWrapper property : BeanHelper.getPropertyDescriptors(formatter.getClass())) { String name = property.getName(); String value = values.remove(name); if (value != null) { invokeSetter(formatter, property, value); } if ("decimalFormatSymbols".equals(property.getName())) { DecimalFormatSymbols modifiedDecimalSymbols = new DecimalFormatSymbols(); boolean modified = false; try { for (PropertyWrapper prop : BeanHelper.getPropertyDescriptors(modifiedDecimalSymbols.getClass())) { value = values.remove(prop.getName()); if (value != null) { invokeSetter(modifiedDecimalSymbols, prop, value); modified = true; } } if (modified) { Method writeMethod = property.getWriteMethod(); if (writeMethod != null) { writeMethod.invoke(formatter, modifiedDecimalSymbols); } else { throw new IllegalStateException("No write method defined for property " + property.getName()); } } } catch (Throwable ex) { throw new DataProcessingException("Error trying to configure decimal symbols of formatter '" + formatter.getClass() + '.', ex); } } } } catch (Exception e) { //ignore and proceed } if (!values.isEmpty()) { throw new DataProcessingException("Cannot find properties in formatter of type '" + formatter.getClass() + "': " + values); } } private static void invokeSetter(Object formatter, PropertyWrapper property, final String value) { Method writeMethod = property.getWriteMethod(); if (writeMethod == null) { DataProcessingException exception = new DataProcessingException("Cannot set property '" + property.getName() + "' of formatter '" + formatter.getClass() + "' to '{value}'. No setter defined"); exception.setValue(value); throw exception; } Class parameterType = writeMethod.getParameterTypes()[0]; Object parameterValue = null; if (parameterType == String.class) { parameterValue = value; } else if (parameterType == Integer.class || parameterType == int.class) { parameterValue = Integer.parseInt(value); } else if (parameterType == Character.class || parameterType == char.class) { parameterValue = value.charAt(0); } else if (parameterType == Currency.class) { parameterValue = Currency.getInstance(value); } else if (parameterType == Boolean.class || parameterType == boolean.class) { parameterValue = Boolean.valueOf(value); } else if (parameterType == TimeZone.class) { parameterValue = TimeZone.getTimeZone(value); } else if (parameterType == DateFormatSymbols.class) { parameterValue = DateFormatSymbols.getInstance(new Locale(value)); } if (parameterValue == null) { DataProcessingException exception = new DataProcessingException("Cannot set property '" + property.getName() + "' of formatter '" + formatter.getClass() + ". Cannot convert '{value}' to instance of " + parameterType); exception.setValue(value); throw exception; } try { writeMethod.invoke(formatter, parameterValue); } catch (Throwable e) { DataProcessingException exception = new DataProcessingException("Error setting property '" + property.getName() + "' of formatter '" + formatter.getClass() + ", with '{parameterValue}' (converted from '{value}')", e); exception.setValue("parameterValue", parameterValue); exception.setValue(value); throw exception; } } private static boolean allFieldsIndexOrNameBased(boolean searchName, Class beanClass, MethodFilter filter) { boolean hasAnnotation = false; for (TransformedHeader header : getFieldSequence(beanClass, true, null, filter)) { if (header == null || header.getTarget() == null) { continue; } AnnotatedElement element = header.getTarget(); if (element instanceof Method && filter.reject((Method) element)) { continue; } Parsed annotation = findAnnotation(element, Parsed.class); if (annotation != null) { hasAnnotation = true; int index = AnnotationRegistry.getValue(element, annotation, "index", annotation.index()); if ((index != -1 && searchName) || (index == -1 && !searchName)) { return false; } } } return hasAnnotation; } /** * Runs through all annotations of a given class to identify whether all annotated fields and methods * (with the {@link Parsed} annotation) are mapped to a column by index. * * @param beanClass a class whose {@link Parsed} annotations will be processed. * * @return {@code true} if every field and method annotated with {@link Parsed} in the given class maps to an index, otherwise {@code false}. */ public static boolean allFieldsIndexBasedForParsing(Class beanClass) { return allFieldsIndexOrNameBased(false, beanClass, MethodFilter.ONLY_SETTERS); } /** * Runs through all annotations of a given class to identify whether all annotated fields and methods * (with the {@link Parsed} annotation) are mapped to a column by name. * * @param beanClass a class whose {@link Parsed} annotations will be processed. * * @return {@code true} if every field and method annotated with {@link Parsed} in the given class maps to a header name, otherwise {@code false}. */ public static boolean allFieldsNameBasedForParsing(Class beanClass) { return allFieldsIndexOrNameBased(true, beanClass, MethodFilter.ONLY_SETTERS); } /** * Runs through all annotations of a given class to identify whether all annotated fields and methods * (with the {@link Parsed} annotation) are mapped to a column by index. * * @param beanClass a class whose {@link Parsed} annotations will be processed. * * @return {@code true} if every field and method annotated with {@link Parsed} in the given class maps to an index, otherwise {@code false}. */ public static boolean allFieldsIndexBasedForWriting(Class beanClass) { return allFieldsIndexOrNameBased(false, beanClass, MethodFilter.ONLY_GETTERS); } /** * Runs through all annotations of a given class to identify whether all annotated fields and methods * (with the {@link Parsed} annotation) are mapped to a column by name. * * @param beanClass a class whose {@link Parsed} annotations will be processed. * * @return {@code true} if every field and method annotated with {@link Parsed} in the given class maps to a header name, otherwise {@code false}. */ public static boolean allFieldsNameBasedForWriting(Class beanClass) { return allFieldsIndexOrNameBased(true, beanClass, MethodFilter.ONLY_GETTERS); } /** * Runs through all {@link Parsed} annotations of a given class to identify all indexes associated with its fields * * @param beanClass a class whose {@link Parsed} annotations will be processed. * @param filter filter to apply over annotated methods when the class is being used for reading data from beans (to write values to an output) * or when writing values into beans (while parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. * * @return an array of column indexes used by the given class */ public static Integer[] getSelectedIndexes(Class beanClass, MethodFilter filter) { List indexes = new ArrayList(); for (TransformedHeader header : getFieldSequence(beanClass, true, null, filter)) { if (header == null) { continue; } int index = header.getHeaderIndex(); if (index != -1) { if (filter == MethodFilter.ONLY_GETTERS && indexes.contains(index)) { //allows the same column to be mapped to multiple fields when parsing, but not when writing. throw new IllegalArgumentException("Duplicate field index '" + index + "' found in attribute '" + header.getTargetName() + "' of class " + beanClass.getName()); } indexes.add(index); } } return indexes.toArray(new Integer[indexes.size()]); } /** * Runs through all {@link Parsed} annotations of a given class to identify all header names associated with its fields * * @param beanClass a class whose {@link Parsed} annotations will be processed. * @param filter a filter to exclude annotated methods that won't be used for parsing or writing * * @return an array of column names used by the given class */ public static String[] deriveHeaderNamesFromFields(Class beanClass, MethodFilter filter) { List sequence = getFieldSequence(beanClass, true, null, filter); List out = new ArrayList(sequence.size()); for (TransformedHeader field : sequence) { if (field == null) { return ArgumentUtils.EMPTY_STRING_ARRAY; // some field has an index that goes beyond list of header names, can't derive. } out.add(field.getHeaderName()); } return out.toArray(new String[out.size()]); } /** * Searches for a given annotation in the hierarchy of a class * * @param beanClass the class whose hierarchy will be searched * @param annotation the annotation to locate * @param the type of the annotation to return * @return the annotation of the given class or its most immediate parent, or {@code null} if not found. */ public static T findAnnotationInClass(Class beanClass, Class annotation) { T out; Class parent = beanClass; do { out = parent.getAnnotation(annotation); if (out != null) { return out; } else { for (Class iface : parent.getInterfaces()) { out = findAnnotationInClass(iface, annotation); if (out != null) { return out; } } } parent = parent.getSuperclass(); } while (parent != null); return null; } /** * Searches for the {@link Headers} annotation in the hierarchy of a class * * @param beanClass the class whose hierarchy will be searched * * @return the {@link Headers} annotation of the given class or its most immediate parent, or {@code null} if not found. */ public static Headers findHeadersAnnotation(Class beanClass) { return findAnnotationInClass(beanClass, Headers.class); } public static Class getType(AnnotatedElement element) { if (element instanceof Field) { return ((Field) element).getType(); } Method method = (Method) element; Class[] params = method.getParameterTypes(); if (params.length == 1) { return params[0]; } else if (params.length > 1) { throw new IllegalArgumentException("Method " + describeElement(element) + " cannot have multiple parameters"); } Class returnType = method.getReturnType(); if (returnType != void.class) { return returnType; } throw new IllegalArgumentException("Method " + describeElement(element) + " must return a value if it has no input parameter"); } public static Class getDeclaringClass(AnnotatedElement element) { if (element instanceof Field) { return ((Field) element).getDeclaringClass(); } else { return ((Method) element).getDeclaringClass(); } } public static String getName(AnnotatedElement element) { if (element instanceof Field) { return ((Field) element).getName(); } else { return ((Method) element).getName(); } } static String describeElement(AnnotatedElement element) { String description; if (element instanceof Field) { description = "attribute '" + ((Field) element).getName() + "'"; } else { description = "method '" + ((Method) element).getName() + "'"; } return description + " of class " + getDeclaringClass(element).getName(); } private static void processAnnotations(AnnotatedElement element, boolean processNested, List indexes, List tmp, Map> nestedReplacements, HeaderTransformer transformer, MethodFilter filter) { Parsed annotation = findAnnotation(element, Parsed.class); if (annotation != null) { TransformedHeader header = new TransformedHeader(element, transformer); if (filter == MethodFilter.ONLY_GETTERS && header.getHeaderIndex() >= 0 && indexes.contains(header.getHeaderIndex())) { //allows the same column to be mapped to multiple fields when parsing, but not when writing. throw new IllegalArgumentException("Duplicate field index '" + header.getHeaderIndex() + "' found in " + describeElement(element)); } tmp.add(header); indexes.add(header.getHeaderIndex()); } if (processNested) { Nested nested = findAnnotation(element, Nested.class); if (nested != null) { tmp.add(new TransformedHeader(element, null)); Class nestedBeanType = AnnotationRegistry.getValue(element, nested, "type", nested.type()); if (nestedBeanType == Object.class) { nestedBeanType = getType(element); } Class transformerType = AnnotationRegistry.getValue(element, nested, "headerTransformer", nested.headerTransformer()); if (transformerType != HeaderTransformer.class) { String[] args = AnnotationRegistry.getValue(element, nested, "args", nested.args()); HeaderTransformer innerTransformer = AnnotationHelper.newInstance(HeaderTransformer.class, transformerType, args); nestedReplacements.put(element, getFieldSequence(nestedBeanType, true, indexes, innerTransformer, filter)); } else { nestedReplacements.put(element, getFieldSequence(nestedBeanType, true, indexes, transformer, filter)); } } } } /** * Returns a list of fields with {@link Parsed} annotations in the sequence they should be processed for parsing * or writing. The sequence is ordered taking into account their original order in the annotated class, unless * {@link Parsed#index()} is set to a non-negative number. * * @param beanClass the class whose field sequence will be returned. * @param processNested flag indicating whether {@link Nested} annotations should be processed * @param transformer a {@link HeaderTransformer} instance to be used for transforming headers of a given {@link Nested} attribute. * @param filter filter to apply over annotated methods when the class is being used for reading data from beans (to write values to an output) * or when writing values into beans (while parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. * * @return a list of fields ordered by their processing sequence */ public static List getFieldSequence(Class beanClass, boolean processNested, HeaderTransformer transformer, MethodFilter filter) { List indexes = new ArrayList(); List tmp = getFieldSequence(beanClass, processNested, indexes, transformer, filter); Collections.sort(tmp, new Comparator() { @Override public int compare(TransformedHeader t1, TransformedHeader t2) { int i1 = t1.getHeaderIndex(); int i2 = t2.getHeaderIndex(); return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1); } }); Collections.sort(indexes); int col = -1; for (int i : indexes) { col++; if (i < 0) { continue; } if (i != col) { while (i >= tmp.size()) { tmp.add(null); } Collections.swap(tmp, i, col); } } return tmp; } private static List getFieldSequence(Class beanClass, boolean processNested, List indexes, HeaderTransformer transformer, MethodFilter filter) { List tmp = new ArrayList(); Map> nestedReplacements = new LinkedHashMap>(); for (Field field : getAllFields(beanClass).keySet()) { processAnnotations(field, processNested, indexes, tmp, nestedReplacements, transformer, filter); } for (Method method : getAnnotatedMethods(beanClass, filter)) { processAnnotations(method, processNested, indexes, tmp, nestedReplacements, transformer, filter); } if (!nestedReplacements.isEmpty()) { int size = tmp.size(); for (int i = size - 1; i >= 0; i--) { TransformedHeader field = tmp.get(i); List nestedFields = nestedReplacements.remove(field.getTarget()); if (nestedFields != null) { tmp.remove(i); tmp.addAll(i, nestedFields); if (nestedReplacements.isEmpty()) { break; } } } } return tmp; } /** * Returns all fields available from a given class. * * @param beanClass a class whose fields will be returned. * * @return a map of {@link Field} and the corresponding {@link PropertyWrapper} */ public static Map getAllFields(Class beanClass) { Map properties = new LinkedHashMap(); try { for (PropertyWrapper property : BeanHelper.getPropertyDescriptors(beanClass)) { String name = property.getName(); if (name != null) { properties.put(name, property); } } } catch (Exception e) { //ignore and proceed to get fields directly } Set used = new HashSet(); Class clazz = beanClass; Map out = new LinkedHashMap(); do { Field[] declared = clazz.getDeclaredFields(); for (Field field : declared) { if (used.contains(field.getName())) { continue; } used.add(field.getName()); out.put(field, properties.get(field.getName())); } clazz = clazz.getSuperclass(); } while (clazz != null && clazz != Object.class); return out; } /** * Returns all methods available from a given class that have a specific annotation. * * @param beanClass a class whose methods will be returned. * @param filter filter to apply over annotated methods when the class is being used for reading data from beans (to write values to an output) * or when writing values into beans (while parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. * @param annotationType the annotation type to consider * * @param the expected annotation type * * @return a list of {@link Method} with at least one annotation */ public static List getAnnotatedMethods(Class beanClass, MethodFilter filter, Class annotationType) { List out = new ArrayList(); Class clazz = beanClass; do { Method[] declared = clazz.getDeclaredMethods(); outer: for (Method method : declared) { if (!method.isSynthetic() && annotationType == NO_ANNOTATIONS.class) { if (!filter.reject(method)) { out.add(method); } } else { Annotation[] annotations = method.getDeclaredAnnotations(); for (Annotation annotation : annotations) { if ((annotationType == null && isCustomAnnotation(annotation)) || annotationType == annotation.annotationType()) { if (filter.reject(method)) { continue outer; } out.add(method); continue outer; } } } } clazz = clazz.getSuperclass(); } while (clazz != null && clazz != Object.class); return out; } private static final class NO_ANNOTATIONS implements Annotation { @Override public Class annotationType() { return NO_ANNOTATIONS.class; } } /** * Returns all methods available from a given class * * @param beanClass a class whose methods will be returned. * @param filter filter to apply over annotated methods when the class is being used for reading data from beans (to write values to an output) * or when writing values into beans (while parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. * * @return a list of {@link Method}s that conform to the given filter. */ public static List getAllMethods(Class beanClass, MethodFilter filter) { return getAnnotatedMethods(beanClass, filter, NO_ANNOTATIONS.class); } /** * Returns all methods available from a given class that have an annotation. * * @param beanClass a class whose methods will be returned. * @param filter filter to apply over annotated methods when the class is being used for reading data from beans (to write values to an output) * or when writing values into beans (while parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. * * @return a list of {@link Method} with at least one annotation */ public static List getAnnotatedMethods(Class beanClass, MethodFilter filter) { return getAnnotatedMethods(beanClass, filter, null); } /** * Returns all attributes available from a given class that have an annotation. * * @param beanClass a class whose methods will be returned. * * @return a list of {@link Field} with at least one annotation */ public static List getAnnotatedFields(Class beanClass) { return getAnnotatedFields(beanClass, null); } /** * Returns all attributes available from a given class that have an annotation. * * @param beanClass a class whose methods will be returned. * @param annotationType the type of annotation to consider * @param the expected annotation type * * @return a list of {@link Field} with at least one annotation */ public static List getAnnotatedFields(Class beanClass, Class annotationType) { List out = new ArrayList(); Class clazz = beanClass; do { Field[] declared = clazz.getDeclaredFields(); outer: for (Field field : declared) { Annotation[] annotations = field.getDeclaredAnnotations(); for (Annotation annotation : annotations) { if ((annotationType == null && isCustomAnnotation(annotation)) || annotationType == annotation.annotationType()) { out.add(field); continue outer; } } } clazz = clazz.getSuperclass(); } while (clazz != null && clazz != Object.class); return out; } /** * Searches for an annotation of a given type that's been applied to an element either directly (as a regular annotation) * or indirectly (as a meta-annotations, i.e. an annotation that has annotations). * * @param annotatedElement the element whose annotations will be searched * @param annotationType the type of annotation to search for * @param the type of the annotation being searched for * * @return the annotation associated with the given element, or {@code null} if not found. */ public synchronized static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { if (annotatedElement == null || annotationType == null) { return null; } if (annotatedElement.equals(lastProcessedElement) && annotationType == lastProcessedAnnotationType) { return (A) lastAnnotationFound; } lastProcessedElement = annotatedElement; lastProcessedAnnotationType = annotationType; Stack path = new Stack(); A annotation = findAnnotation(annotatedElement, annotationType, new HashSet(), path); if (annotation == null || path.isEmpty()) { lastAnnotationFound = annotation; return annotation; } while (!path.isEmpty()) { Annotation parent = path.pop(); Annotation target = path.isEmpty() ? annotation : path.peek(); for (Method method : parent.annotationType().getDeclaredMethods()) { Copy copy = method.getAnnotation(Copy.class); if (copy != null) { Class targetClass = copy.to(); String targetProperty = copy.property(); if (targetProperty.trim().isEmpty()) { targetProperty = method.getName(); } Object value; Object existingValue = AnnotationRegistry.getValue(annotatedElement, target, method.getName()); if (existingValue != null) { value = existingValue; } else { value = invoke(parent, method); } Class sourceValueType = method.getReturnType(); Class targetPropertyType = findAnnotationMethodType(targetClass, targetProperty); if (targetPropertyType != null && targetPropertyType.isArray() && !value.getClass().isArray()) { Object array = Array.newInstance(sourceValueType, 1); Array.set(array, 0, value); value = array; } if (targetClass == target.annotationType()) { AnnotationRegistry.setValue(annotatedElement, annotation, targetProperty, value); } else { A ann = (A) findAnnotation(annotatedElement, targetClass, new HashSet(), new Stack()); if (ann != null) { AnnotationRegistry.setValue(annotatedElement, ann, targetProperty, value); } else { throw new IllegalStateException("Can't process @Copy annotation on '" + method + "'. " + "Annotation '" + targetClass.getName() + "' not used in " + parent.annotationType().getName() + ". Unable to process field " + annotatedElement + "'"); } } } } } lastAnnotationFound = annotation; return annotation; } private static Class findAnnotationMethodType(Class type, String methodName) { for (Method method : type.getDeclaredMethods()) { if (method.getName().equals(methodName)) { return method.getReturnType(); } } return null; } private static Object invoke(Annotation annotation, Method method) { try { return method.invoke(annotation, (Object[]) null); } catch (Exception e) { throw new IllegalStateException("Can't read value from annotation " + annotation, e); } } private static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType, Set visited, Stack path) { Annotation[] declaredAnnotations = annotatedElement.getDeclaredAnnotations(); for (int i = 0; i < declaredAnnotations.length; i++) { Annotation ann = declaredAnnotations[i]; if (ann.annotationType() == annotationType) { return (A) ann; } } for (int i = 0; i < declaredAnnotations.length; i++) { Annotation ann = declaredAnnotations[i]; if (isCustomAnnotation(ann) && visited.add(ann)) { A annotation = findAnnotation(ann.annotationType(), annotationType, visited, path); path.push(ann); if (annotation != null) { return annotation; } } } return null; } private static final Set javaLangAnnotationTypes = new HashSet(); private static final Set customAnnotationTypes = new HashSet(); private static boolean isCustomAnnotation(Annotation annotation) { Class annotationType = annotation.annotationType(); if (customAnnotationTypes.contains(annotationType)) { return true; } if (javaLangAnnotationTypes.contains(annotationType)) { return false; } if (annotationType.getName().startsWith("java.lang.annotation")) { javaLangAnnotationTypes.add(annotationType); return false; } else { customAnnotationTypes.add(annotationType); return true; } } /** * Returns all annotations applied to an element, excluding the ones not in a given package. * * @param annotatedElement the element (method, field, etc) whose annotations will be extracted * @param aPackage the package of the annotations that should be returned * * @return the list of annotation elements applied to the given element, that are also members of the given package. */ public static List findAllAnnotationsInPackage(AnnotatedElement annotatedElement, Package aPackage) { final ArrayList found = new ArrayList(); findAllAnnotationsInPackage(annotatedElement, aPackage, found, new HashSet()); return found; } private static void findAllAnnotationsInPackage(AnnotatedElement annotatedElement, Package aPackage, ArrayList found, Set visited) { Annotation[] declaredAnnotations = annotatedElement.getDeclaredAnnotations(); for (int i = 0; i < declaredAnnotations.length; i++) { Annotation ann = declaredAnnotations[i]; if (aPackage.equals(ann.annotationType().getPackage())) { found.add(ann); } if (isCustomAnnotation(ann) && visited.add(ann)) { findAllAnnotationsInPackage(ann.annotationType(), aPackage, found, visited); } } } /** * Returns Java's default value for a given type, in a primitive type wrapper. * * @param type the primitive type whose default value will be returned. * * @return the default value for the given primitive type, or {@code null} if the type is not primitive. */ public static final Object getDefaultPrimitiveValue(Class type) { if (type == int.class) { return Integer.valueOf(0); } else if (type == double.class) { return 0.0D; } else if (type == boolean.class) { return Boolean.FALSE; } else if (type == long.class) { return Long.valueOf(0L); } else if (type == float.class) { return 0.0F; } else if (type == byte.class) { return Byte.valueOf((byte) 0); } else if (type == char.class) { return Character.valueOf('\0'); } else if (type == short.class) { return Short.valueOf((short) 0); } return null; } } AnnotationRegistry.java000066400000000000000000000134241400120543400345760ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.helpers; import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; /** * An internal registry of annotated elements and their properties that have been set via a {@link com.univocity.parsers.annotations.Copy} annotation. */ public class AnnotationRegistry { private static final Map modifiedAnnotations = new HashMap(); /** * Associates a value to a given annotation attribute * @param annotatedElement a method or field that has an annotation whose properties are changed by a {@link com.univocity.parsers.annotations.Copy} annotation * @param annotation the altered annotation of the given annotatedElement * @param attribute the attribute of the altered annotation * @param newValue the value of the given attribute of the altered annotation. */ static synchronized final void setValue(AnnotatedElement annotatedElement, Annotation annotation, String attribute, Object newValue) { FieldAnnotations attributes = modifiedAnnotations.get(annotatedElement); if (attributes == null) { attributes = new FieldAnnotations(); modifiedAnnotations.put(annotatedElement, attributes); } attributes.setValue(annotation, attribute, newValue); } /** * Returns the a value to a given annotation attribute that might have been modified by a {@link com.univocity.parsers.annotations.Copy} annotation * * @param annotatedElement a method or field that has an annotation whose properties might have been changed by a {@link com.univocity.parsers.annotations.Copy} annotation * @param annotation the possibly altered annotation of the given annotatedElement * @param attribute the attribute of the possibly altered annotation * @param valueIfNull the value to return from the unmodified annotation, if it has not been changed by a {@link com.univocity.parsers.annotations.Copy} * * @param the expected value type to be returned by this method. * * @return the value associated with the given annotation property. */ public static synchronized final T getValue(AnnotatedElement annotatedElement, Annotation annotation, String attribute, T valueIfNull) { if (annotatedElement == null) { return valueIfNull; } Object value = getValue(annotatedElement, annotation, attribute); if (value == null) { return valueIfNull; } return (T) value; } /** * Returns the a value to a given annotation attribute that might have been modified by a {@link com.univocity.parsers.annotations.Copy} annotation * * @param annotatedElement a method or field that has an annotation whose properties might have been changed by a {@link com.univocity.parsers.annotations.Copy} annotation * @param annotation the possibly altered annotation of the given annotatedElement * @param attribute the attribute of the possibly altered annotation * * @return the value associated with the given annotation property, or {@code null} if it has not been modified by a {@link com.univocity.parsers.annotations.Copy} */ static synchronized final Object getValue(AnnotatedElement annotatedElement, Annotation annotation, String attribute) { FieldAnnotations attributes = modifiedAnnotations.get(annotatedElement); if (attributes == null) { return null; } return attributes.getValue(annotation, attribute); } public static final void reset() { modifiedAnnotations.clear(); } private static class FieldAnnotations { private Map annotations = new HashMap(); private void setValue(Annotation annotation, String attribute, Object newValue) { AnnotationAttributes attributes = annotations.get(annotation); if (attributes == null) { attributes = new AnnotationAttributes(); annotations.put(annotation, attributes); } attributes.setAttribute(attribute, newValue); } private Object getValue(Annotation annotation, String attribute) { AnnotationAttributes attributes = annotations.get(annotation); if (attributes == null) { return null; } return attributes.getAttribute(attribute); } } private static class AnnotationAttributes { private Map attributes = new HashMap(); private void setAttribute(String attribute, Object newValue) { if (!attributes.containsKey(attribute)) { attributes.put(attribute, newValue); } else { Object existingValue = attributes.get(attribute); if (existingValue == null || newValue == null) { return; } //handles single values copied to a parent annotation that accepts an array Class originalClass = existingValue.getClass(); Class newClass = newValue.getClass(); if (originalClass != newClass && newClass.isArray() && newClass.getComponentType() == existingValue.getClass()) { Object array = Array.newInstance(originalClass, 1); Array.set(array, 0, existingValue); attributes.put(attribute, array); } } } private Object getAttribute(String attribute) { return attributes.get(attribute); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/FieldMapping.java000066400000000000000000000307071400120543400333540ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.helpers; import com.univocity.parsers.annotations.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.beans.*; import java.lang.reflect.*; import static com.univocity.parsers.annotations.helpers.AnnotationHelper.*; /** * A helper class with information about the location of an field annotated with {@link Parsed} in a record. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class FieldMapping { private final Class parentClass; private final AnnotatedElement target; private int index; private NormalizedString fieldName; private final Class beanClass; private final Method readMethod; private final Method writeMethod; private boolean accessible; private final boolean primitive; private final Object defaultPrimitiveValue; private Boolean applyDefault = null; private Class fieldType; private boolean primitiveNumber; /** * Creates the mapping and identifies how it is mapped (by name or by index) * * @param beanClass the class that contains a the given field. * @param target a {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method} annotated with {@link Parsed} * @param property the property descriptor of this field, if any. If this bean does not have getters/setters, it will be accessed directly. * @param transformer an optional {@link HeaderTransformer} to modify header names/positions in attributes of {@link Nested} classes. * @param headers list of headers parsed from the input or manually set with {@link CommonSettings#setHeaders(String...)} */ public FieldMapping(Class beanClass, AnnotatedElement target, PropertyWrapper property, HeaderTransformer transformer, NormalizedString[] headers) { this.beanClass = beanClass; this.target = target; if (target instanceof Field) { this.readMethod = property != null ? property.getReadMethod() : null; this.writeMethod = property != null ? property.getWriteMethod() : null; } else { Method method = (Method) target; this.readMethod = method.getReturnType() != Void.class ? method : null; this.writeMethod = method.getParameterTypes().length != 0 ? method : null; } Class typeToSet; if (target != null) { typeToSet = getType(target); parentClass = getDeclaringClass(target); } else if (writeMethod != null && writeMethod.getParameterTypes().length == 1) { typeToSet = writeMethod.getParameterTypes()[0]; parentClass = writeMethod.getDeclaringClass(); } else { typeToSet = Object.class; if (readMethod != null) { parentClass = readMethod.getDeclaringClass(); } else { parentClass = beanClass; } } primitive = typeToSet.isPrimitive(); defaultPrimitiveValue = getDefaultPrimitiveValue(typeToSet); primitiveNumber = (defaultPrimitiveValue instanceof Number); fieldType = typeToSet; determineFieldMapping(transformer, headers); } private void determineFieldMapping(HeaderTransformer transformer, NormalizedString[] headers) { Parsed parsed = findAnnotation(target, Parsed.class); String name = ""; if (parsed != null) { //field can be annotated with @Nested only. In this case we get the original field name index = AnnotationRegistry.getValue(target, parsed, "index", parsed.index()); if (index >= 0) { fieldName = null; if (transformer != null) { index = transformer.transformIndex(target, index); } return; } String[] fields = AnnotationRegistry.getValue(target, parsed, "field", parsed.field()); if (fields.length > 1 && headers != null) { for (int i = 0; i < headers.length; i++) { NormalizedString header = headers[i]; if (header == null) { continue; } for (int j = 0; j < fields.length; j++) { String field = fields[j]; if (header.equals(field)) { name = field; break; } } } } if (name.isEmpty()) { name = fields.length == 0 ? "" : fields[0]; } } if (name.isEmpty()) { name = getName(target); } fieldName = NormalizedString.valueOf(name); //Not a @Nested field if (parsed != null && transformer != null) { if (index >= 0) { index = transformer.transformIndex(target, index); } else if (fieldName != null) { fieldName = NormalizedString.valueOf(transformer.transformName(target, fieldName.toString())); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FieldMapping that = (FieldMapping) o; if (index != that.index) { return false; } if (!target.equals(that.target)) { return false; } if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null) { return false; } return beanClass.equals(that.beanClass); } @Override public int hashCode() { int result = target.hashCode(); result = 31 * result + index; result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0); result = 31 * result + beanClass.hashCode(); return result; } /** * Returns {@code true} if the field is mapped to a column index, otherwise {@code false} * * @return {@code true} if the field is mapped to a column index, otherwise {@code false} */ public boolean isMappedToIndex() { return index >= 0; } /** * Returns {@code true} if the field is mapped to a column name, otherwise {@code false} * * @return {@code true} if the field is mapped to a column name, otherwise {@code false} */ public boolean isMappedToField() { return index < 0; } /** * Returns the column index against which this field is mapped. * * @return the column index associated with this field, or -1 if there's no such association. */ public int getIndex() { return index; } /** * Defines the column index against which this field is mapped, overriding any current position derived from * annotations. * * @param index the column index associated with this field */ public void setIndex(int index) { this.index = index; } /** * Defines the column name against which this field is mapped, overriding any current name derived from * annotations or from the attribute name itself. * * @param fieldName the column name associated with this field */ public void setFieldName(String fieldName) { this.fieldName = NormalizedString.valueOf(fieldName); } /** * Defines the column name against which this field is mapped, overriding any current name derived from * annotations or from the attribute name itself. * * @param fieldName the column name associated with this field */ public void setFieldName(NormalizedString fieldName) { this.fieldName = fieldName; } /** * Returns the column name against which this field is mapped. * * @return the column name associated with this field, or {@code null} if there's no such association. */ public NormalizedString getFieldName() { return fieldName; } /** * Returns the {@link Field} mapped to a column * * @return the {@link Field} mapped to a column */ public AnnotatedElement getTarget() { return target; } private void setAccessible() { if (!accessible) { if (target instanceof Field) { final Field field = ((Field) target); if (!field.isAccessible()) { field.setAccessible(true); } } else if (target instanceof Method) { final Method method = (Method) target; if (!method.isAccessible()) { method.setAccessible(true); } } accessible = true; } } /** * Returns the parent class that contains the mapped field. * * @return the field's parent class */ public Class getFieldParent() { return parentClass; } /** * Returns the type of the mapped field * * @return the field type. */ public Class getFieldType() { return fieldType; } /** * Queries whether this field mapping can be applied over a given object instance. * * @param instance the object whose type will be verified in order to identify if it contains the mapped field * * @return {@code true} if the given instance contains the field/accessor method and can use this field mapping to modify its internal state; otherwise {@code false} */ public boolean canWrite(Object instance) { if (!primitive) { if (instance == null) { return true; } return fieldType.isAssignableFrom(instance.getClass()); } else if (instance instanceof Number) { return primitiveNumber; } else if (instance instanceof Boolean) { return fieldType == boolean.class; } else if (instance instanceof Character) { return fieldType == char.class; } return false; } /** * Reads the value accessible by this field mapping from a given object * * @param instance the object whose field, mapped by this field mapping, will be read * * @return the value contained in the given instance's field */ public Object read(Object instance) { return read(instance, false); } private Object read(Object instance, boolean ignoreErrors) { setAccessible(); try { if (readMethod != null) { return readMethod.invoke(instance); } else { return ((Field) target).get(instance); } } catch (Throwable e) { if (e instanceof InvocationTargetException) { e = e.getCause(); } if (!ignoreErrors) { String msg = "Unable to get value from field: " + toString(); if (e instanceof DataProcessingException) { DataProcessingException ex = (DataProcessingException) e; ex.setDetails(msg); throw ex; } throw new DataProcessingException(msg, e); } } return null; } /** * Writes a value to the field of a given object instance, whose field is accessible through this field mapping. * * @param instance the object whose field will be set * @param value the value to set on the given object's field. */ public void write(Object instance, Object value) { setAccessible(); try { if (primitive) { if (value == null) { if (applyDefault == null) { Object currentValue = read(instance, true); applyDefault = defaultPrimitiveValue.equals(currentValue); } if (applyDefault == Boolean.TRUE) { value = defaultPrimitiveValue; } else { return; } } else if (defaultPrimitiveValue.getClass() != value.getClass() && value instanceof Number) { Number number = ((Number) value); if (fieldType == int.class) { value = number.intValue(); } else if (fieldType == long.class) { value = number.longValue(); } else if (fieldType == double.class) { value = number.doubleValue(); } else if (fieldType == float.class) { value = number.floatValue(); } else if (fieldType == byte.class) { value = number.byteValue(); } else if (fieldType == short.class) { value = number.shortValue(); } } } if (writeMethod != null) { writeMethod.invoke(instance, value); } else { ((Field) target).set(instance, value); } } catch (Throwable e) { String valueTypeName = value == null ? null : value.getClass().getName(); String msg; String details = null; if (valueTypeName != null) { msg = "Unable to set value '{value}' of type '" + valueTypeName + "' to " + toString(); } else { msg = "Unable to set value 'null' to " + toString(); } if (e instanceof InvocationTargetException) { e = e.getCause(); details = msg; } if (e instanceof DataProcessingException) { DataProcessingException ex = (DataProcessingException) e; ex.markAsNonFatal(); ex.setValue(value); ex.setDetails(details); throw (DataProcessingException) e; } DataProcessingException ex = new DataProcessingException(msg, e); ex.markAsNonFatal(); ex.setValue(value); throw ex; } } @Override public String toString() { return AnnotationHelper.describeElement(target); } } MethodDescriptor.java000066400000000000000000000121511400120543400342060ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.helpers; import java.lang.reflect.*; /** * A very basic descriptor or getter/setter methods */ public final class MethodDescriptor { private final String prefixedName; private final String name; private final String prefix; private final Class parameterType; private final Class returnType; private final String string; private MethodDescriptor(String name, Class parameterType, Class returnType) { prefixedName = name; int lastDot = name.lastIndexOf('.'); if (lastDot == -1) { this.name = name; this.prefix = ""; } else { this.name = name.substring(lastDot + 1); this.prefix = name.substring(0, lastDot); } this.parameterType = parameterType; this.returnType = returnType; this.string = generateString(); } private MethodDescriptor(String prefix, String name, Class parameterType, Class returnType) { this.prefixedName = prefix + '.' + name; this.name = name; this.prefix = prefix; this.parameterType = parameterType; this.returnType = returnType; this.string = generateString(); } private String generateString() { StringBuilder out = new StringBuilder("method "); if (returnType != null) { out.append(returnType.getName()); out.append(' '); } if (prefix.isEmpty()) { out.append(name); } else { out.append(prefix); out.append('.'); out.append(name); } if (parameterType != null) { out.append('('); out.append(parameterType.getName()); out.append(')'); } else { out.append("()"); } return out.toString(); } /** * Creates a descriptor for a setter method * @param name name of the setter method * @param parameterType the parameter type accepted by the given setter method * @return a "setter" method descriptor */ public static MethodDescriptor setter(String name, Class parameterType) { return new MethodDescriptor(name, parameterType, null); } /** * Creates a descriptor for a getter method * @param name name of the getter method * @param returnType the return type of the given getter method * @return a "getter" method descriptor */ public static MethodDescriptor getter(String name, Class returnType) { return new MethodDescriptor(name, null, returnType); } /** * Creates a descriptor for a setter method * @param prefix a dot separated string denoting a path of nested object names * @param method a actual class method to be associated with this prefix * @return a "setter" method descriptor */ static MethodDescriptor setter(String prefix, Method method) { return new MethodDescriptor(prefix, method.getName(), method.getParameterTypes()[0], null); } /** * Creates a descriptor for a getter method * @param prefix a dot separated string denoting a path of nested object names * @param method a actual class method to be associated with this prefix * @return a "getter" method descriptor */ static MethodDescriptor getter(String prefix, Method method) { return new MethodDescriptor(prefix, method.getName(), null, method.getReturnType()); } /** * Returns the method name, without the prefix * @return the method name */ public String getName() { return name; } /** * Returns the prefix: a dot separated string denoting a path of nested object names (e.g. customer.contact). * * @return the object nesting path associated with a method. */ public String getPrefix() { return prefix; } /** * Returns the parameter type associated with a method, if available * @return the type of parameter accepted by this method if it is a setter, or {@code null} if a getter is being represented. */ public Class getParameterType() { return parameterType; } /** * Returns the return type associated with a method, if available * @return the return type of this method if it is a getter, or {@code null} if a setter is being represented. */ public Class getReturnType() { return returnType; } /** * Returns full path to a method, (e.g. {@code getName} or {@code person.getName} * @return the path to the given method. */ public String getPrefixedName() { return prefixedName; } public String toString() { return string; } @Override public boolean equals(Object o) { if (o == null || o.getClass() != this.getClass()) { return false; } return string.equals(o.toString()); } @Override public int hashCode() { return string.hashCode(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/MethodFilter.java000066400000000000000000000053141400120543400333770ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.helpers; import java.lang.reflect.*; /** * A filter for annotated methods. Used internally to exclude setters or getters from the list of fields to be processed, * accordingly to the use case: when parsing into beans, only setter methods are to be considered. When writing values * in beans to an output, only the getter methods should be used. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public enum MethodFilter { /** * Rejects any method that returns {@code void} or has a parameter list. */ ONLY_GETTERS(new Filter() { @Override public boolean reject(Method method) { return method.getReturnType() == void.class || method.getParameterTypes().length != 0; } }), /** * Rejects any method that doesn't accept a single parameter. */ ONLY_SETTERS(new Filter() { @Override public boolean reject(Method method) { return method.getParameterTypes().length != 1; } }); private Filter filter; MethodFilter(Filter filter) { this.filter = filter; } /** * Tests whether a method is not a getter or setter and should be rejected. * * @param method the method to be tested * * @return {@code true} if the given method should be rejected, {@code false} otherwise */ public boolean reject(Method method) { return filter.reject(method); } private interface Filter { boolean reject(Method method); } /** * Creates a descriptor for a getter or setter method * * @param prefix a dot separated string denoting a path of nested object names * @param method a actual class method to be associated with this prefix * @return a descriptor for the given method */ public MethodDescriptor toDescriptor(String prefix, Method method) { if (reject(method)) { return null; } if (this == MethodFilter.ONLY_SETTERS) { return MethodDescriptor.setter(prefix, method); } else { return MethodDescriptor.getter(prefix, method); } } } TransformedHeader.java000066400000000000000000000112301400120543400343210ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/annotations/helpers/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.helpers; import com.univocity.parsers.annotations.*; import java.lang.reflect.*; import static com.univocity.parsers.annotations.helpers.AnnotationHelper.*; /** * A pair associating a Field of an annotated class to an optional {@link HeaderTransformer} obtained from * {@link Nested#headerTransformer()} when nested classes are used to process beans. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public class TransformedHeader { private final AnnotatedElement target; private final Field field; private final Method method; private final HeaderTransformer transformer; private int index = -2; public TransformedHeader(AnnotatedElement target, HeaderTransformer transformer) { if (target instanceof Field) { field = (Field) target; method = null; } else { method = (Method) target; field = null; } this.target = target; this.transformer = transformer; } /** * Returns the name to be used as a header based on a given field and its {@link Parsed} annotation. * * @return the header name to be used for the given field. */ public String getHeaderName() { if (target == null) { return null; } String name = null; Parsed annotation = findAnnotation(target, Parsed.class); if (annotation != null) { String[] field = AnnotationRegistry.getValue(target, annotation, "field", annotation.field()); if (field.length == 0) { name = getTargetName(); } else { name = field[0]; } if (name.length() == 0) { name = getTargetName(); } } if (transformer != null) { if (field != null) { return transformer.transformName(field, name); } else { return transformer.transformName(method, name); } } return name; } /** * Returns the index that determines which column the current field represents, as specified by {@link Parsed#index()} * * @return the current header index. */ public int getHeaderIndex() { if (index == -2) { Parsed annotation = findAnnotation(target, Parsed.class); if (annotation != null) { index = AnnotationRegistry.getValue(target, annotation, "index", annotation.index()); if (index != -1) { if (transformer != null) { if (field != null) { index = transformer.transformIndex(field, index); } else { index = transformer.transformIndex(method, index); } } } } else { index = -1; } } return index; } /** * Returns the original attribute name of the field in its containing class. * * @return the original attribute name of the field */ public String getTargetName() { if (target == null) { return null; } if (field != null) { return field.getName(); } else { return method.getName(); } } /** * Returns the {@link AnnotatedElement} used to read/write values from/to. * * @return the field or method being manipulated by the parser/writer when processing java beans */ public AnnotatedElement getTarget() { return target; } /** * Returns {@code true} if this {@link AnnotatedElement} is a {@link Method} with parameters and can only be used * for writing values into the java bean. * * @return a flag indicating whether this is a method that allows writing values only. */ public boolean isWriteOnly() { if (method != null) { return method.getParameterTypes().length != 0; } return false; } /** * Returns {@code true} if this {@link AnnotatedElement} is a {@link Method} with no parameters and a return type which can only be used * for reading values from the java bean. * * @return a flag indicating whether this is a method that allows reading values only. */ public boolean isReadOly() { if (method != null) { return method.getParameterTypes().length == 0 && method.getReturnType() != void.class; } return false; } public String describe() { return AnnotationHelper.describeElement(target); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/000077500000000000000000000000001400120543400254345ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/AbstractException.java000066400000000000000000000111311400120543400317160ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.util.*; /** * Parent class of the Exception classes thrown by univocity-parsers. This class provides utility methods to print out the internal state of the parser/writer * at the time an error occurred. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ abstract class AbstractException extends RuntimeException { private static final long serialVersionUID = -2993096896413328423L; protected int errorContentLength = -1; protected AbstractException(String message, Throwable cause) { super(message, cause); } /** * Returns a detailed message describing the error, and the internal state of the parser/writer. * * @return a detailed message describing the error */ @Override public final String getMessage() { String msg = super.getMessage(); msg = msg == null ? getErrorDescription() + ": " : msg; String details = getDetails(); if (details != null && !details.isEmpty()) { msg = msg + "\nInternal state when error was thrown: " + details; } msg = updateMessage(msg); return msg; } /** * Allows subclasses to alter the exception message that should be displayed to end users. * By default the original message is kept unchanged. * * @param msg the original message * @return the updated message. */ protected String updateMessage(String msg){ return msg; } /** * Subclasses must implement this method to return as much information as possible about the internal state of the parser/writer. * Use {@link #printIfNotEmpty(String, String, Object)} to create a comma-separated list of relevant properties and their (non null) values. * * The result of this method is used by the {@link #getMessage()} method to print out these details after the error message. * * @return a String describing the internal state of the parser/writer. */ protected abstract String getDetails(); /** * Returns a generic description of the error. The result of this method is used by {@link #getMessage()} to print out a general description of the error before a detailed message of the root cause. * * @return a generic description of the error. */ protected abstract String getErrorDescription(); protected static String printIfNotEmpty(String previous, String description, Object o) { String value; if (o == null || o.toString().isEmpty()) { return previous; } else if (o instanceof Number && ((Number) o).intValue() < 0) { return previous; } else if (o.getClass().isArray()) { //noinspection ConstantConditions value = Arrays.toString((Object[]) o); } else { value = String.valueOf(o); } String out = description + '=' + value; if (!previous.isEmpty()) { out = previous + ", " + out; } return out; } public static String restrictContent(int errorContentLength, CharSequence content) { return ArgumentUtils.restrictContent(errorContentLength,content); } public static Object[] restrictContent(int errorContentLength, Object[] content) { if (content == null || errorContentLength == 0) { return null; } return content; } public void setErrorContentLength(int errorContentLength) { this.errorContentLength = errorContentLength; Throwable cause = this.getCause(); if(cause != null && cause instanceof AbstractException){ AbstractException e = ((AbstractException) cause); if(e.errorContentLength != errorContentLength) { //prevents an unintended recursion cycle. e.setErrorContentLength(errorContentLength); } } } protected String restrictContent(CharSequence content) { return restrictContent(errorContentLength, content); } protected String restrictContent(Object content) { return ArgumentUtils.restrictContent(errorContentLength, content); } protected Object[] restrictContent(Object[] content) { return restrictContent(errorContentLength, content); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/AbstractParser.java000066400000000000000000001522601400120543400312250ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.input.EOFException; import com.univocity.parsers.common.input.*; import com.univocity.parsers.common.iterators.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.common.record.*; import java.io.*; import java.nio.charset.*; import java.util.*; import static com.univocity.parsers.common.ArgumentUtils.*; /** * The AbstractParser class provides a common ground for all parsers in univocity-parsers. *

It handles all settings defined by {@link CommonParserSettings}, and delegates the parsing algorithm implementation to its subclasses through the * abstract method {@link AbstractParser#parseRecord()} *

The following (absolutely required) attributes are exposed to subclasses: *

    *
  • input ({@link CharInputReader}): the character input provider that reads characters from a given input into an internal buffer
  • *
  • output ({@link ParserOutput}): the output handler for every record parsed from the input. Implementors must use this object to handle the input (such as appending characters and notifying of values parsed)
  • *
  • ch (char): the current character read from the input
  • *
* * @param The specific parser settings configuration class, which can potentially provide additional configuration options supported by the parser * implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.csv.CsvParser * @see com.univocity.parsers.csv.CsvParserSettings * @see com.univocity.parsers.fixed.FixedWidthParser * @see com.univocity.parsers.fixed.FixedWidthParserSettings * @see com.univocity.parsers.common.input.CharInputReader * @see com.univocity.parsers.common.ParserOutput */ public abstract class AbstractParser> { protected final T settings; protected final ParserOutput output; private final long recordsToRead; protected final char comment; private final LineReader lineReader = new LineReader(); protected ParsingContext context; protected Processor processor; protected CharInputReader input; protected char ch; private final ProcessorErrorHandler errorHandler; private final long rowsToSkip; protected final Map comments; protected String lastComment; private final boolean collectComments; private final int errorContentLength; private boolean extractingHeaders = false; private final boolean extractHeaders; protected final int whitespaceRangeStart; protected boolean ignoreTrailingWhitespace; protected boolean ignoreLeadingWhitespace; private final boolean processComments; /** * All parsers must support, at the very least, the settings provided by {@link CommonParserSettings}. The AbstractParser requires its configuration to be * properly initialized. * * @param settings the parser configuration */ public AbstractParser(T settings) { settings.autoConfigure(); this.settings = settings; this.errorContentLength = settings.getErrorContentLength(); this.ignoreTrailingWhitespace = settings.getIgnoreTrailingWhitespaces(); this.ignoreLeadingWhitespace = settings.getIgnoreLeadingWhitespaces(); this.output = new ParserOutput(this, settings); this.processor = settings.getProcessor(); this.recordsToRead = settings.getNumberOfRecordsToRead(); this.comment = settings.getFormat().getComment(); this.errorHandler = settings.getProcessorErrorHandler(); this.rowsToSkip = settings.getNumberOfRowsToSkip(); this.collectComments = settings.isCommentCollectionEnabled(); this.comments = collectComments ? new TreeMap() : Collections.emptyMap(); this.extractHeaders = settings.isHeaderExtractionEnabled(); this.whitespaceRangeStart = settings.getWhitespaceRangeStart(); this.processComments = settings.isCommentProcessingEnabled(); } protected void processComment() { if (collectComments) { long line = input.lineCount(); String comment = input.readComment(); if (comment != null) { lastComment = comment; comments.put(line, lastComment); } } else { try { input.skipLines(1); } catch (IllegalArgumentException e) { //end of input reached, ignore. } } } /** * Parses the entirety of a given input and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param reader The input to be parsed. */ public final void parse(Reader reader) { beginParsing(reader); try { while (!context.isStopped()) { input.markRecordStart(); ch = input.nextChar(); if (processComments && inComment()) { processComment(); continue; } if (output.pendingRecords.isEmpty()) { parseRecord(); } String[] row = output.rowParsed(); if (row != null) { if (recordsToRead >= 0 && context.currentRecord() >= recordsToRead) { context.stop(); if (recordsToRead == 0) { stopParsing(); return; } } if (processor != NoopProcessor.instance) { rowProcessed(row); } } } stopParsing(); } catch (EOFException ex) { try { handleEOF(); while (!output.pendingRecords.isEmpty()) { handleEOF(); } } finally { stopParsing(); } } catch (Throwable ex) { try { ex = handleException(ex); } finally { stopParsing(ex); } } } /** * Parser-specific implementation for reading a single record from the input. *

The AbstractParser handles the initialization and processing of the input until it is ready to be parsed. *

It then delegates the input to the parser-specific implementation defined by {@link #parseRecord()}. In general, an implementation of * {@link AbstractParser#parseRecord()} will perform the following steps: *

    *
  • Test the character stored in ch and take some action on it (e.g. is while (ch != '\n'){doSomething()})
  • *
  • Request more characters by calling ch = input.nextChar();
  • *
  • Append the desired characters to the output by executing, for example, output.appender.append(ch)
  • *
  • Notify a value of the record has been fully read by executing output.valueParsed(). This will clear the output appender ({@link CharAppender}) so the next call to output.appender.append(ch) will be store the character of the next parsed value
  • *
  • Rinse and repeat until all values of the record are parsed
  • *
*

Once the {@link #parseRecord()} returns, the AbstractParser takes over and handles the information (generally, reorganizing it and passing it on to a {@link RowProcessor}). *

After the record processing, the AbstractParser reads the next characters from the input, delegating control again to the parseRecord() implementation for processing of the next record. *

This cycle repeats until the reading process is stopped by the user, the input is exhausted, or an error happens. *

In case of errors, the unchecked exception {@link TextParsingException} will be thrown and all resources in use will be closed automatically * unless {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false}. The exception should contain the cause and more information about where in the input the error happened. * * @see com.univocity.parsers.common.input.CharInputReader * @see com.univocity.parsers.common.input.CharAppender * @see com.univocity.parsers.common.ParserOutput * @see com.univocity.parsers.common.TextParsingException * @see com.univocity.parsers.common.processor.RowProcessor */ protected abstract void parseRecord(); /** * Allows the parser implementation to handle any value that was being consumed when the end of the input was reached * * @return a flag indicating whether the parser was processing a value when the end of the input was reached. */ protected boolean consumeValueOnEOF() { return false; } private String[] handleEOF() { String[] row = null; try { boolean consumeValueOnEOF = consumeValueOnEOF(); if (output.column != 0 || (consumeValueOnEOF && !context.isStopped())) { if (output.appender.length() > 0 || consumeValueOnEOF) { output.valueParsed(); } else if (input.currentParsedContentLength() > 0) { output.emptyParsed(); } row = output.rowParsed(); } else if (output.appender.length() > 0 || input.currentParsedContentLength() > 0) { if (output.appender.length() == 0) { output.emptyParsed(); } else { output.valueParsed(); } row = output.rowParsed(); } else if (!output.pendingRecords.isEmpty()) { row = output.pendingRecords.poll(); } } catch (Throwable e) { throw handleException(e); } if (row != null && processor != NoopProcessor.instance) { rowProcessed(row); } return row; } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param reader The input to be parsed. */ public final void beginParsing(Reader reader) { output.reset(); if (reader instanceof LineReader) { input = new DefaultCharInputReader(settings.getFormat().getLineSeparator(), settings.getFormat().getNormalizedNewline(), settings.getInputBufferSize(), whitespaceRangeStart, true); } else { input = settings.newCharInputReader(whitespaceRangeStart); } input.enableNormalizeLineEndings(true); context = createParsingContext(); if (processor instanceof DefaultConversionProcessor) { DefaultConversionProcessor conversionProcessor = ((DefaultConversionProcessor) processor); conversionProcessor.errorHandler = errorHandler; conversionProcessor.context = context; } if (input instanceof AbstractCharInputReader) { AbstractCharInputReader inputReader = ((AbstractCharInputReader) input); inputReader.addInputAnalysisProcess(getInputAnalysisProcess()); for(InputAnalysisProcess p : settings.getInputAnalysisProcesses()){ inputReader.addInputAnalysisProcess(p); } } try { input.start(reader); } catch (Throwable t) { throw handleException(t); } input.skipLines(rowsToSkip); initialize(); processor.processStarted(context); } void extractHeadersIfRequired() { while (extractHeaders && output.parsedHeaders == null && !context.isStopped() && !extractingHeaders) { Processor userProvidedProcessor = processor; try { processor = NoopProcessor.instance; //disables any users provided processors to capture headers extractingHeaders = true; parseNext(); } finally { extractingHeaders = false; processor = userProvidedProcessor; } } } protected ParsingContext createParsingContext() { DefaultParsingContext out = new DefaultParsingContext(this, errorContentLength); out.stopped = false; return out; } protected void initialize() { } /** * Allows the parser implementation to traverse the input buffer before the parsing process starts, in order to enable automatic configuration and discovery * of data formats. * * @return a custom implementation of {@link InputAnalysisProcess}. By default, {@code null} is returned and no special input analysis will be performed. */ protected InputAnalysisProcess getInputAnalysisProcess() { return null; } private String getParsedContent(CharSequence tmp) { return "Parsed content: " + AbstractException.restrictContent(errorContentLength, tmp); } private TextParsingException handleException(Throwable ex) { if (context != null) { context.stop(); } if (ex instanceof DataProcessingException) { DataProcessingException error = (DataProcessingException) ex; error.restrictContent(errorContentLength); error.setContext(this.context); throw error; } String message = ex.getClass().getName() + " - " + ex.getMessage(); char[] chars = output.appender.getChars(); if (chars != null) { int length = output.appender.length(); if (length > chars.length) { message = "Length of parsed input (" + length + ") exceeds the maximum number of characters defined in" + " your parser settings (" + settings.getMaxCharsPerColumn() + "). "; length = chars.length; } String tmp = new String(chars); if (tmp.contains("\n") || tmp.contains("\r")) { tmp = displayLineSeparators(tmp, true); String lineSeparator = displayLineSeparators(settings.getFormat().getLineSeparatorString(), false); message += "\nIdentified line separator characters in the parsed content. This may be the cause of the error. " + "The line separator in your parser settings is set to '" + lineSeparator + "'. " + getParsedContent(tmp); } int nullCharacterCount = 0; //ensuring the StringBuilder won't grow over Integer.MAX_VALUE to avoid OutOfMemoryError int maxLength = length > Integer.MAX_VALUE / 2 ? Integer.MAX_VALUE / 2 - 1 : length; StringBuilder s = new StringBuilder(maxLength); for (int i = 0; i < maxLength; i++) { if (chars[i] == '\0') { s.append('\\'); s.append('0'); nullCharacterCount++; } else { s.append(chars[i]); } } tmp = s.toString(); if (nullCharacterCount > 0) { message += "\nIdentified " + nullCharacterCount + " null characters ('\0') on parsed content. This may " + "indicate the data is corrupt or its encoding is invalid. Parsed content:\n\t" + getParsedContent(tmp); } } if (ex instanceof ArrayIndexOutOfBoundsException) { try { int index = Integer.parseInt(ex.getMessage()); if (index == settings.getMaxCharsPerColumn()) { message += "\nHint: Number of characters processed may have exceeded limit of " + index + " characters per column. Use settings.setMaxCharsPerColumn(int) to define the maximum number of characters a column can have"; } if (index == settings.getMaxColumns()) { message += "\nHint: Number of columns processed may have exceeded limit of " + index + " columns. Use settings.setMaxColumns(int) to define the maximum number of columns your input can have"; } message += "\nEnsure your configuration is correct, with delimiters, quotes and escape sequences that match the input format you are trying to parse"; } catch (Throwable t) { //ignore; } } try { if (!message.isEmpty()) { message += "\n"; } message += "Parser Configuration: " + settings.toString(); } catch (Exception t) { //ignore } if (errorContentLength == 0) { output.appender.reset(); } TextParsingException out = new TextParsingException(context, message, ex); out.setErrorContentLength(errorContentLength); return out; } /** * In case of errors, stops parsing and closes all open resources. Avoids hiding the original exception in case another error occurs when stopping. */ private void stopParsing(Throwable error) { if (error != null) { try { stopParsing(); } catch (Throwable ex) { // ignore and throw original error. } if (error instanceof DataProcessingException) { DataProcessingException ex = (DataProcessingException) error; ex.setContext(context); throw ex; } else if (error instanceof RuntimeException) { throw (RuntimeException) error; } else if (error instanceof Error) { throw (Error) error; } else { throw new IllegalStateException(error.getMessage(), error); } } else { stopParsing(); } } /** * Stops parsing and closes all open resources. */ public final void stopParsing() { try { ch = '\0'; try { if (context != null) { context.stop(); } } finally { try { if (processor != null) { processor.processEnded(context); } } finally { if (output != null) { output.appender.reset(); } if (input != null) { input.stop(); } } } } catch (Throwable error) { throw handleException(error); } } private List beginParseAll(boolean validateReader, Reader reader, int expectedRowCount) { if (reader == null) { if (validateReader) { throw new IllegalStateException("Input reader must not be null"); } else { if (context == null) { throw new IllegalStateException("Input not defined. Please call method 'beginParsing()' with a valid input."); } else if (context.isStopped()) { return Collections.emptyList(); } } } List out = new ArrayList(expectedRowCount <= 0 ? 10000 : expectedRowCount); if (reader != null) { beginParsing(reader); } return out; } /** * Parses all remaining rows from the input and returns them in a list. * * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all remaining records parsed from the input. */ public List parseAll(int expectedRowCount) { return internalParseAll(false, null, expectedRowCount); } /** * Parses all remaining rows from the input and returns them in a list. * * @return the list of all remaining records parsed from the input. */ public List parseAll() { return internalParseAll(false, null, -1); } /** * Parses all remaining {@link Record}s from the input and returns them in a list. * * @param expectedRowCount expected number of {@link Record}s to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all remaining records parsed from the input. */ public List parseAllRecords(int expectedRowCount) { return internalParseAllRecords(false, null, expectedRowCount); } /** * Parses all remaining {@link Record}s from the input and returns them in a list. * * @return the list of all remaining {@link Record}s parsed from the input. */ public List parseAllRecords() { return internalParseAllRecords(false, null, -1); } /** * Parses all records from the input and returns them in a list. * * @param reader the input to be parsed * * @return the list of all records parsed from the input. */ public final List parseAll(Reader reader) { return parseAll(reader, 0); } /** * Parses all records from the input and returns them in a list. * * @param reader the input to be parsed * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAll(Reader reader, int expectedRowCount) { return internalParseAll(true, reader, expectedRowCount); } private final List internalParseAll(boolean validateReader, Reader reader, int expectedRowCount) { List out = beginParseAll(validateReader, reader, expectedRowCount); String[] row; while ((row = parseNext()) != null) { out.add(row); } return out; } protected boolean inComment() { return ch == comment; } /** * Parses the next record from the input. Note that {@link AbstractParser#beginParsing(Reader)} must have been invoked once before calling this method. * If the end of the input is reached, then this method will return null. Additionally, all resources will be closed automatically at the end of the input * or if any error happens while parsing, * unless {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false}. * * @return The record parsed from the input or null if there's no more characters to read. */ public final String[] parseNext() { try { while (!context.isStopped()) { input.markRecordStart(); ch = input.nextChar(); if (processComments && inComment()) { processComment(); continue; } if (output.pendingRecords.isEmpty()) { parseRecord(); } String[] row = output.rowParsed(); if (row != null) { if (recordsToRead >= 0 && context.currentRecord() >= recordsToRead) { context.stop(); if (recordsToRead == 0L) { stopParsing(); return null; } } if (processor != NoopProcessor.instance) { rowProcessed(row); } return row; } else if (extractingHeaders) { return null; } } if (output.column != 0) { return output.rowParsed(); } stopParsing(); return null; } catch (EOFException ex) { String[] row = handleEOF(); if (output.pendingRecords.isEmpty()) { stopParsing(); } return row; } catch (NullPointerException ex) { if (context == null) { throw new IllegalStateException("Cannot parse without invoking method beginParsing(Reader) first"); } else { if (input != null) { stopParsing(); } throw new IllegalStateException("Error parsing next record.", ex); } } catch (Throwable ex) { try { ex = handleException(ex); } finally { stopParsing(ex); } } return null; } /** * Reloads headers from settings. */ protected final void reloadHeaders() { this.output.initializeHeaders(); if (context instanceof DefaultParsingContext) { ((DefaultParsingContext) context).reset(); } } /** * Parses a single line from a String in the format supported by the parser implementation. * * @param line a line of text to be parsed * * @return the {@link Record} containing the values parsed from the input line */ public final Record parseRecord(String line) { String[] values = parseLine(line); if (values == null) { return null; } return context.toRecord(values); } /** * Parses a single line from a String in the format supported by the parser implementation. * * @param line a line of text to be parsed * * @return the values parsed from the input line */ public final String[] parseLine(String line) { if (line == null || line.isEmpty()) { return null; } lineReader.setLine(line); if (context == null || context.isStopped()) { beginParsing(lineReader); } else { if (input instanceof DefaultCharInputReader) { ((DefaultCharInputReader) input).reloadBuffer(); } else if (input instanceof LookaheadCharInputReader) { ((LookaheadCharInputReader) input).reloadBuffer(); } } try { while (!context.isStopped()) { input.markRecordStart(); ch = input.nextChar(); if (processComments && inComment()) { processComment(); return null; } if (output.pendingRecords.isEmpty()) { parseRecord(); } String[] row = output.rowParsed(); if (row != null) { if (processor != NoopProcessor.instance) { rowProcessed(row); } return row; } } return null; } catch (EOFException ex) { return handleEOF(); } catch (NullPointerException ex) { if (input != null) { stopParsing(null); } throw new IllegalStateException("Error parsing next record.", ex); } catch (Throwable ex) { try { ex = handleException(ex); } finally { stopParsing(ex); } } return null; } private void rowProcessed(String[] row) { Internal.process(row, processor, context, errorHandler); } /** * Parses the entirety of a given file and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param file The file to be parsed. */ public final void parse(File file) { parse(ArgumentUtils.newReader(file)); } /** * Parses the entirety of a given file and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param file The file to be parsed. * @param encoding the encoding of the file */ public final void parse(File file, String encoding) { parse(ArgumentUtils.newReader(file, encoding)); } /** * Parses the entirety of a given file and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param file The file to be parsed. * @param encoding the encoding of the file */ public final void parse(File file, Charset encoding) { parse(ArgumentUtils.newReader(file, encoding)); } /** * Parses the entirety of a given input and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param input The input to be parsed. The input stream will be closed automatically, unless {@link CommonParserSettings#isAutoClosingEnabled()} evaluates * to {@code false}. */ public final void parse(InputStream input) { parse(ArgumentUtils.newReader(input)); } /** * Parses the entirety of a given input and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param input The input to be parsed. The input stream will be closed automatically, unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false}. * @param encoding the encoding of the input stream */ public final void parse(InputStream input, String encoding) { parse(ArgumentUtils.newReader(input, encoding)); } /** * Parses the entirety of a given input and delegates each parsed row to an instance of {@link RowProcessor}, defined by * {@link CommonParserSettings#getRowProcessor()}. * * @param input The input to be parsed. The input stream will be closed automatically, unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false}. * @param encoding the encoding of the input stream */ public final void parse(InputStream input, Charset encoding) { parse(ArgumentUtils.newReader(input, encoding)); } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param file The file to be parsed. */ public final void beginParsing(File file) { beginParsing(ArgumentUtils.newReader(file)); } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param file The file to be parsed. * @param encoding the encoding of the file */ public final void beginParsing(File file, String encoding) { beginParsing(ArgumentUtils.newReader(file, encoding)); } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param file The file to be parsed. * @param encoding the encoding of the file */ public final void beginParsing(File file, Charset encoding) { beginParsing(ArgumentUtils.newReader(file, encoding)); } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param input The input to be parsed. The input stream will be closed automatically in case of errors unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false}. */ public final void beginParsing(InputStream input) { beginParsing(ArgumentUtils.newReader(input)); } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param input The input to be parsed. The input stream will be closed automatically in case of errors unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false}. * @param encoding the encoding of the input stream */ public final void beginParsing(InputStream input, String encoding) { beginParsing(ArgumentUtils.newReader(input, encoding)); } /** * Starts an iterator-style parsing cycle. If a {@link RowProcessor} is provided in the configuration, it will be used to perform additional processing. * The parsed records must be read one by one with the invocation of {@link AbstractParser#parseNext()}. * The user may invoke @link {@link AbstractParser#stopParsing()} to stop reading from the input. * * @param input The input to be parsed. The input stream will be closed automatically in case of errors unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false}. * @param encoding the encoding of the input stream */ public final void beginParsing(InputStream input, Charset encoding) { beginParsing(ArgumentUtils.newReader(input, encoding)); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the file. */ public final List parseAll(File file, int expectedRowCount) { return parseAll(ArgumentUtils.newReader(file), expectedRowCount); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the file. */ public final List parseAll(File file, String encoding, int expectedRowCount) { return parseAll(ArgumentUtils.newReader(file, encoding), expectedRowCount); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the file. */ public final List parseAll(File file, Charset encoding, int expectedRowCount) { return parseAll(ArgumentUtils.newReader(file, encoding), expectedRowCount); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false} * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAll(InputStream input, int expectedRowCount) { return parseAll(ArgumentUtils.newReader(input), expectedRowCount); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false} * @param encoding the encoding of the input stream * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAll(InputStream input, String encoding, int expectedRowCount) { return parseAll(ArgumentUtils.newReader(input, encoding), expectedRowCount); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false} * @param encoding the encoding of the input stream * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAll(InputStream input, Charset encoding, int expectedRowCount) { return parseAll(ArgumentUtils.newReader(input, encoding), expectedRowCount); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * * @return the list of all records parsed from the file. */ public final List parseAll(File file) { return parseAll(ArgumentUtils.newReader(file)); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * * @return the list of all records parsed from the file. */ public final List parseAll(File file, String encoding) { return parseAll(ArgumentUtils.newReader(file, encoding)); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * * @return the list of all records parsed from the file. */ public final List parseAll(File file, Charset encoding) { return parseAll(ArgumentUtils.newReader(file, encoding)); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false} * * @return the list of all records parsed from the input. */ public final List parseAll(InputStream input) { return parseAll(ArgumentUtils.newReader(input)); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false} * @param encoding the encoding of the input stream * * @return the list of all records parsed from the input. */ public final List parseAll(InputStream input, String encoding) { return parseAll(ArgumentUtils.newReader(input, encoding)); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false} * @param encoding the encoding of the input stream * * @return the list of all records parsed from the input. */ public final List parseAll(InputStream input, Charset encoding) { return parseAll(ArgumentUtils.newReader(input, encoding)); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the file. */ public final List parseAllRecords(File file, int expectedRowCount) { return parseAllRecords(ArgumentUtils.newReader(file), expectedRowCount); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the file. */ public final List parseAllRecords(File file, String encoding, int expectedRowCount) { return parseAllRecords(ArgumentUtils.newReader(file, encoding), expectedRowCount); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the file. */ public final List parseAllRecords(File file, Charset encoding, int expectedRowCount) { return parseAllRecords(ArgumentUtils.newReader(file, encoding), expectedRowCount); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false} * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAllRecords(InputStream input, int expectedRowCount) { return parseAllRecords(ArgumentUtils.newReader(input), expectedRowCount); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false} * @param encoding the encoding of the input stream * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAllRecords(InputStream input, String encoding, int expectedRowCount) { return parseAllRecords(ArgumentUtils.newReader(input, encoding), expectedRowCount); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless * {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false} * @param encoding the encoding of the input stream * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAllRecords(InputStream input, Charset encoding, int expectedRowCount) { return parseAllRecords(ArgumentUtils.newReader(input, encoding), expectedRowCount); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * * @return the list of all records parsed from the file. */ public final List parseAllRecords(File file) { return parseAllRecords(ArgumentUtils.newReader(file)); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * * @return the list of all records parsed from the file. */ public final List parseAllRecords(File file, String encoding) { return parseAllRecords(ArgumentUtils.newReader(file, encoding)); } /** * Parses all records from a file and returns them in a list. * * @param file the input file to be parsed * @param encoding the encoding of the file * * @return the list of all records parsed from the file. */ public final List parseAllRecords(File file, Charset encoding) { return parseAllRecords(ArgumentUtils.newReader(file, encoding)); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false} * * @return the list of all records parsed from the input. */ public final List parseAllRecords(InputStream input) { return parseAllRecords(ArgumentUtils.newReader(input)); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false} * @param encoding the encoding of the input stream * * @return the list of all records parsed from the input. */ public final List parseAllRecords(InputStream input, String encoding) { return parseAllRecords(ArgumentUtils.newReader(input, encoding)); } /** * Parses all records from an input stream and returns them in a list. * * @param input the input stream to be parsed. The input stream will be closed automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} * evaluates to {@code false} * @param encoding the encoding of the input stream * * @return the list of all records parsed from the input. */ public final List parseAllRecords(InputStream input, Charset encoding) { return parseAllRecords(ArgumentUtils.newReader(input, encoding)); } /** * Parses all records from the input and returns them in a list. * * @param reader the input to be parsed * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} * * @return the list of all records parsed from the input. */ public final List parseAllRecords(Reader reader, int expectedRowCount) { return internalParseAllRecords(true, reader, expectedRowCount); } private List internalParseAllRecords(boolean validateReader, Reader reader, int expectedRowCount) { List out = beginParseAll(validateReader, reader, expectedRowCount); if (context.isStopped()) { return out; } Record record; while ((record = parseNextRecord()) != null) { out.add(record); } return out; } /** * Parses all records from the input and returns them in a list. * * @param reader the input to be parsed * * @return the list of all records parsed from the input. */ public final List parseAllRecords(Reader reader) { return parseAllRecords(reader, 0); } /** * Parses the next record from the input. Note that {@link AbstractParser#beginParsing(Reader)} must have been invoked once before calling this method. * If the end of the input is reached, then this method will return null. Additionally, all resources will be closed automatically at the end of the input * or if any error happens while parsing. * * @return The record parsed from the input or null if there's no more characters to read. */ public final Record parseNextRecord() { String[] row = this.parseNext(); if (row != null) { return context.toRecord(row); } return null; } /** * Returns all comments collected by the parser so far. * An empty map will be returned if {@link CommonParserSettings#isCommentCollectionEnabled()} evaluates to {@code false}. * * @return a map containing the line numbers and comments found in each. */ final Map getComments() { return comments; } /** * Returns the last comment found in the input. * {@code null} will be returned if {@link CommonParserSettings#isCommentCollectionEnabled()} is evaluated to {@code false}. * * @return the last comment found in the input. */ final String getLastComment() { return lastComment; } /** * Returns the headers parsed from the input, if and only if {@link CommonParserSettings#headerExtractionEnabled} is {@code true}. * The result of this method won't return the list of headers manually set by the user in {@link CommonParserSettings#getHeaders()}. * * @return the headers parsed from the input, when {@link CommonParserSettings#headerExtractionEnabled} is {@code true}. */ final String[] getParsedHeaders() { extractHeadersIfRequired(); return output.parsedHeaders; } /** * Returns the current parsing context with information about the status of the parser at any given time. * * @return the parsing context */ public final ParsingContext getContext() { return context; } /** * Returns the metadata associated with {@link Record}s parsed from the input using {@link AbstractParser#parseAllRecords(File)} or * {@link AbstractParser#parseNextRecord()}. * * @return the metadata of {@link Record}s generated with the current input. */ public final RecordMetaData getRecordMetadata() { if (context == null) { throw new IllegalStateException("Record metadata not available. The parser has not been started."); } return context.recordMetaData(); } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the input {@code File} * @param encoding the encoding of the input {@code File} * * @return an iterator for rows parsed from the input. */ public final IterableResult iterate(final File input, String encoding) { return iterate(input, Charset.forName(encoding)); } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the input {@code File} * @param encoding the encoding of the input {@code File} * * @return an iterator for rows parsed from the input. */ public final IterableResult iterate(final File input, final Charset encoding) { return new RowIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input, encoding); } }; } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the input {@code File} * * @return an iterator for rows parsed from the input. */ public final IterableResult iterate(final File input) { return new RowIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input); } }; } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the input {@code Reader} * * @return an {@code iterable} over the results of parsing the {@code Reader} */ public final IterableResult iterate(final Reader input) { return new RowIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input); } }; } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the the {@code InputStream} with contents to be parsed * @param encoding the character encoding to be used for processing the given input. * * @return an iterator for rows parsed from the input. */ public final IterableResult iterate(final InputStream input, String encoding) { return iterate(input, Charset.forName(encoding)); } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the the {@code InputStream} with contents to be parsed * @param encoding the character encoding to be used for processing the given input. * * @return an iterator for rows parsed from the input. */ public final IterableResult iterate(final InputStream input, final Charset encoding) { return new RowIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input, encoding); } }; } /** * Provides an {@link IterableResult} for iterating rows parsed from the input. * * @param input the the {@code InputStream} with contents to be parsed * * @return an iterator for rows parsed from the input. */ public final IterableResult iterate(final InputStream input) { return new RowIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input); } }; } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the input {@code File} * @param encoding the encoding of the input {@code File} * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final File input, String encoding) { return iterateRecords(input, Charset.forName(encoding)); } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the input {@code File} * @param encoding the encoding of the input {@code File} * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final File input, final Charset encoding) { return new RecordIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input, encoding); } }; } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the input {@code File} * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final File input) { return new RecordIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input); } }; } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the input {@code Reader} * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final Reader input) { return new RecordIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input); } }; } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the the {@code InputStream} with contents to be parsed * @param encoding the character encoding to be used for processing the given input. * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final InputStream input, String encoding) { return iterateRecords(input, Charset.forName(encoding)); } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the the {@code InputStream} with contents to be parsed * @param encoding the character encoding to be used for processing the given input. * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final InputStream input, final Charset encoding) { return new RecordIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input, encoding); } }; } /** * Provides an {@link IterableResult} for iterating records parsed from the input. * * @param input the the {@code InputStream} with contents to be parsed * * @return an iterator for records parsed from the input. */ public final IterableResult iterateRecords(final InputStream input) { return new RecordIterator(this) { @Override protected void beginParsing() { parser.beginParsing(input); } }; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/AbstractWriter.java000066400000000000000000003220461400120543400312460ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.input.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.common.record.*; import com.univocity.parsers.fixed.*; import java.io.*; import java.nio.charset.*; import java.util.*; /** * The AbstractWriter class provides a common ground for all writers in univocity-parsers. * * It handles all settings defined by {@link CommonWriterSettings}, and delegates the writing algorithm implementation to its subclasses through the abstract * method {@link AbstractWriter#processRow(Object[])} * * The following (absolutely required) attributes are exposed to subclasses: *

    *
  • appender ({@link WriterCharAppender}): the character writer that appends characters from a given input into an internal buffer
  • *
* * @param The specific writer settings configuration class, which can potentially provide additional configuration options supported by the writer * implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.csv.CsvWriter * @see com.univocity.parsers.csv.CsvWriterSettings * @see com.univocity.parsers.fixed.FixedWidthWriter * @see com.univocity.parsers.fixed.FixedWidthWriterSettings * @see com.univocity.parsers.tsv.TsvWriter * @see com.univocity.parsers.tsv.TsvWriterSettings * @see com.univocity.parsers.common.input.WriterCharAppender * @see com.univocity.parsers.common.processor.RowWriterProcessor */ public abstract class AbstractWriter> { @SuppressWarnings("rawtypes") private final RowWriterProcessor writerProcessor; private Writer writer; private final boolean skipEmptyLines; protected final char comment; private final WriterCharAppender rowAppender; private final boolean isHeaderWritingEnabled; private Object[] outputRow; private int[] indexesToWrite; private final char[] lineSeparator; protected NormalizedString[] headers; protected long recordCount = 0; protected final String nullValue; protected final String emptyValue; protected final WriterCharAppender appender; private final Object[] partialLine; private int partialLineIndex = 0; private Map> headerIndexes; private int largestRowLength = -1; protected boolean writingHeaders = false; protected boolean[] headerTrimFlags; private NormalizedString[] dummyHeaderRow; protected boolean expandRows; private boolean usingSwitch; private boolean enableNewlineAfterRecord = true; protected boolean usingNullOrEmptyValue; protected final int whitespaceRangeStart; private final boolean columnReorderingEnabled; protected boolean ignoreLeading; protected boolean ignoreTrailing; private final CommonSettings internalSettings = new CommonSettings() { @Override protected DummyFormat createDefaultFormat() { return DummyFormat.instance; } }; private final int errorContentLength; /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * Important: by not providing an instance of {@link java.io.Writer} to this constructor, only the operations that write to Strings are * available. * * @param settings the writer configuration */ public AbstractWriter(S settings) { this((Writer) null, settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param file the output file that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param settings the writer configuration */ public AbstractWriter(File file, S settings) { this(ArgumentUtils.newWriter(file), settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param file the output file that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param encoding the encoding of the file * @param settings the writer configuration */ public AbstractWriter(File file, String encoding, S settings) { this(ArgumentUtils.newWriter(file, encoding), settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param file the output file that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param encoding the encoding of the file * @param settings the writer configuration */ public AbstractWriter(File file, Charset encoding, S settings) { this(ArgumentUtils.newWriter(file, encoding), settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param output the output stream that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param settings the writer configuration */ public AbstractWriter(OutputStream output, S settings) { this(ArgumentUtils.newWriter(output), settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param output the output stream that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param encoding the encoding of the stream * @param settings the writer configuration */ public AbstractWriter(OutputStream output, String encoding, S settings) { this(ArgumentUtils.newWriter(output, encoding), settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param output the output stream that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param encoding the encoding of the stream * @param settings the writer configuration */ public AbstractWriter(OutputStream output, Charset encoding, S settings) { this(ArgumentUtils.newWriter(output, encoding), settings); } /** * All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be * properly initialized. * * @param writer the output resource that will receive the format-specific records as defined by subclasses of {@link AbstractWriter}. * @param settings the writer configuration */ public AbstractWriter(Writer writer, S settings) { settings.autoConfigure(); this.ignoreLeading = settings.getIgnoreLeadingWhitespaces(); this.ignoreTrailing = settings.getIgnoreTrailingWhitespaces(); internalSettings.setMaxColumns(settings.getMaxColumns()); this.errorContentLength = settings.getErrorContentLength(); this.nullValue = settings.getNullValue(); this.emptyValue = settings.getEmptyValue(); this.lineSeparator = settings.getFormat().getLineSeparator(); this.comment = settings.getFormat().getComment(); this.skipEmptyLines = settings.getSkipEmptyLines(); this.writerProcessor = settings.getRowWriterProcessor(); this.usingSwitch = writerProcessor instanceof RowWriterProcessorSwitch; this.expandRows = settings.getExpandIncompleteRows(); this.columnReorderingEnabled = settings.isColumnReorderingEnabled(); this.whitespaceRangeStart = settings.getWhitespaceRangeStart(); this.appender = new WriterCharAppender(settings.getMaxCharsPerColumn(), "", whitespaceRangeStart, settings.getFormat()); this.rowAppender = new WriterCharAppender(settings.getMaxCharsPerColumn(), "", whitespaceRangeStart, settings.getFormat()); this.writer = writer; this.headers = NormalizedString.toIdentifierGroupArray(settings.getHeaders()); updateIndexesToWrite(settings); this.partialLine = new Object[settings.getMaxColumns()]; this.isHeaderWritingEnabled = settings.isHeaderWritingEnabled(); if (writerProcessor instanceof DefaultConversionProcessor) { DefaultConversionProcessor conversionProcessor = (DefaultConversionProcessor) writerProcessor; conversionProcessor.context = null; conversionProcessor.errorHandler = settings.getProcessorErrorHandler(); } initialize(settings); } protected void enableNewlineAfterRecord(boolean enableNewlineAfterRecord) { this.enableNewlineAfterRecord = enableNewlineAfterRecord; } /** * Initializes the concrete implementation of this class with format-specific settings. * * @param settings the settings object specific to the format being written. */ protected abstract void initialize(S settings); /** * Update indexes to write based on the field selection provided by the user. */ private void updateIndexesToWrite(CommonSettings settings) { FieldSelector selector = settings.getFieldSelector(); if (selector != null) { if (headers != null && headers.length > 0) { indexesToWrite = selector.getFieldIndexes(headers); if (columnReorderingEnabled) { //column reordering enabled? int size = ArgumentUtils.removeAll(indexesToWrite, -1).length; outputRow = new Object[size]; } else { outputRow = new Object[headers.length]; } } else if (!(selector instanceof FieldNameSelector) && !(selector instanceof ExcludeFieldNameSelector)) { int rowLength = largestRowLength; if ((selector instanceof FieldIndexSelector)) { boolean gotLengthFromSelection = false; for (Integer index : ((FieldIndexSelector) selector).get()) { if (rowLength <= index) { rowLength = index; gotLengthFromSelection = true; } } if (gotLengthFromSelection) { rowLength++; } if (rowLength < largestRowLength) { rowLength = largestRowLength; } } else { rowLength = settings.getMaxColumns(); } indexesToWrite = selector.getFieldIndexes(new NormalizedString[rowLength]); //generates a dummy header array - only the indexes matter so we are good if (columnReorderingEnabled) { //column reordering enabled? int size = ArgumentUtils.removeAll(indexesToWrite, -1).length; outputRow = new Object[size]; } else { outputRow = new Object[rowLength]; } } else { throw new IllegalStateException("Cannot select fields by name with no headers defined"); } } else { outputRow = null; indexesToWrite = null; } } /** * Updates the selection of fields to write. This is useful if the input rows * change during the writing process and their values need be allocated to specific columns. * * @param newFieldSelection the new selection of fields to write. */ public void updateFieldSelection(String... newFieldSelection) { if (headers == null) { throw new IllegalStateException("Cannot select fields by name. Headers not defined."); } internalSettings.selectFields(newFieldSelection); updateIndexesToWrite(internalSettings); } /** * Updates the selection of fields to write. This is useful if the input rows * change during the writing process and their values need be allocated to specific columns. * * @param newFieldSelectionByIndex the new selection of fields to write. */ public void updateFieldSelection(Integer... newFieldSelectionByIndex) { internalSettings.selectIndexes(newFieldSelectionByIndex); updateIndexesToWrite(internalSettings); } /** * Updates the selection of fields to exclude when writing. This is useful if the input rows * change during the writing process and their values need be allocated to specific columns. * * @param fieldsToExclude the selection of fields to exclude from the output. */ public void updateFieldExclusion(String... fieldsToExclude) { if (headers == null) { throw new IllegalStateException("Cannot de-select fields by name. Headers not defined."); } internalSettings.excludeFields(fieldsToExclude); updateIndexesToWrite(internalSettings); } /** * Updates the selection of fields to exclude when writing. This is useful if the input rows * change during the writing process and their values need be allocated to specific columns. * * @param fieldIndexesToExclude the selection of fields to exclude from the output. */ public void updateFieldExclusion(Integer... fieldIndexesToExclude) { internalSettings.excludeIndexes(fieldIndexesToExclude); updateIndexesToWrite(internalSettings); } /** * Submits a row for processing by the format-specific implementation. * * @param row the data to be written for a single record in the output. */ private void submitRow(Object[] row) { if (largestRowLength < row.length) { largestRowLength = row.length; } if (writingHeaders) { headerTrimFlags = new boolean[headers.length]; for (int i = 0; i < headers.length; i++) { headerTrimFlags[i] = !headers[i].isLiteral(); } } processRow(row); } /** * Format-specific implementation for writing a single record into the output. * * The AbstractWriter handles the initialization and processing of the output until it is ready to be written (generally, reorganizing it and passing it on * to a {@link RowWriterProcessor}). * It then delegates the record to the writer-specific implementation defined by {@link #processRow(Object[])}. In general, an implementation of {@link * AbstractWriter#processRow(Object[])} will perform the following steps: *
    *
  • Iterate over each object in the given input and convert it to the expected String representation.
  • *
  • The conversion must happen using the provided {@link AbstractWriter#appender} object. The an individual value is processed, the {@link AbstractWriter#appendValueToRow()} method must be called. * This will clear the accumulated value in {@link AbstractWriter#appender} and add it to the output row.
  • *
  • Format specific separators and other characters must be introduced to the output row using {@link AbstractWriter#appendToRow(char)}
  • *
* Once the {@link #processRow(Object[])} method returns, a row will be written to the output with the processed information, and a newline will be automatically written after the given contents, unless this is a * {@link com.univocity.parsers.fixed.FixedWidthWriter} whose {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}. The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()} * This cycle repeats until the writing process is stopped by the user or an error happens. * In case of errors, the unchecked exception {@link TextWritingException} will be thrown and all resources in use will be closed automatically. The exception should contain the cause and more information about the output state when the error happened. * * @param row the data to be written to the output in the expected format. * * @see com.univocity.parsers.common.input.CharAppender * @see com.univocity.parsers.common.CommonWriterSettings */ protected abstract void processRow(Object[] row); /** * Appends the processed sequence of characters in {@link AbstractWriter#appender} to the output row. */ protected final void appendValueToRow() { rowAppender.append(appender); } /** * Appends the given character to the output row. * * @param ch the character to append to the output row */ protected final void appendToRow(char ch) { rowAppender.append(ch); } /** * Appends the given character sequence to the output row * * @param chars the sequence of characters to append to the output row */ protected final void appendToRow(char[] chars) { rowAppender.append(chars); } /** * Writes the headers defined in {@link CommonSettings#getHeaders()} * A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to the output. */ public final void writeHeaders() { writeHeaders(NormalizedString.toArray(this.headers)); } /** * Writes the given collection of headers to the output. * A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to the output. * * @param headers the headers to write to the output. */ public final void writeHeaders(Collection headers) { if (headers != null && headers.size() > 0) { writeHeaders(headers.toArray(new String[headers.size()])); } else { throw throwExceptionAndClose("No headers defined."); } } /** * Writes the given collection of headers to the output. * A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to the output. * * @param headers the headers to write to the output. */ public final void writeHeaders(String... headers) { if (recordCount > 0) { throw throwExceptionAndClose("Cannot write headers after records have been written.", headers, null); } if (headers != null && headers.length > 0) { writingHeaders = true; if (columnReorderingEnabled && outputRow != null) { fillOutputRow(headers); headers = Arrays.copyOf(outputRow, outputRow.length, String[].class); } this.headers = NormalizedString.toIdentifierGroupArray(headers); submitRow(headers); internalWriteRow(); writingHeaders = false; } else { throw throwExceptionAndClose("No headers defined.", headers, null); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them, then finally and closes the output * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param allRecords the records to be transformed by a {@link RowWriterProcessor} and then written to the output */ public final void processRecordsAndClose(Iterable allRecords) { try { processRecords(allRecords); } finally { close(); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them, then finally and closes the output * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param allRecords the records to be transformed by a {@link RowWriterProcessor} and then written to the output */ public final void processRecordsAndClose(Object[] allRecords) { try { processRecords(allRecords); } finally { close(); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them. * The output will remain open for further writing. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param records the records to be transformed by a {@link RowWriterProcessor} and then written to the output */ public final void processRecords(Iterable records) { for (Object record : records) { processRecord(record); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them. * The output will remain open for further writing. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param records the records to transformed by a {@link RowWriterProcessor} and then written to the output */ public final void processRecords(Object[] records) { for (Object record : records) { processRecord(record); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them. * The output will remain open for further writing. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param records the records to transformed by a {@link RowWriterProcessor} and then written to the output * @param the concrete Record type */ public final void processRecords(T[] records) { for (T record : records) { processRecord(record); } } /** * Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes it. * The output will remain open for further writing. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to the output */ public final void processRecord(Object... record) { processRecord((Object) record); } public final void processRecord(T record) { processRecord((Object) (record == null ? null : record.getValues())); } /** * Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes it. * The output will remain open for further writing. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to the output */ @SuppressWarnings("unchecked") public final void processRecord(Object record) { if (this.writerProcessor == null) { String recordDescription; if (record instanceof Object[]) { recordDescription = Arrays.toString((Object[]) record); } else { recordDescription = String.valueOf(record); } String message = "Cannot process record '" + recordDescription + "' without a writer processor. Please define a writer processor instance in the settings or use the 'writeRow' methods."; this.throwExceptionAndClose(message); } Object[] row; try { if (usingSwitch) { dummyHeaderRow = ((RowWriterProcessorSwitch) writerProcessor).getHeaders(record); if (dummyHeaderRow == null) { dummyHeaderRow = this.headers; } row = writerProcessor.write(record, dummyHeaderRow, indexesToWrite); } else { row = writerProcessor.write(record, getRowProcessorHeaders(), indexesToWrite); } } catch (DataProcessingException e) { e.setErrorContentLength(errorContentLength); throw e; } if (row != null) { writeRow(row); } } private NormalizedString[] getRowProcessorHeaders() { if (headers == null && indexesToWrite == null) { return null; } return headers; } /** * Iterates over all records, writes them and closes the output. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Iterable)} for that. * * @param Collection of objects containing values of a row * @param allRows the rows to be written to the output */ public final > void writeRowsAndClose(Iterable allRows) { try { writeRows(allRows); } finally { close(); } } /** * Iterates over all records, writes them and closes the output. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Object[])} for that. * * @param allRows the rows to be written to the output */ public final void writeRowsAndClose(Collection allRows) { try { writeRows(allRows); } finally { close(); } } /** * Iterates over all records, writes them and closes the output. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Iterable)} for that. * * @param allRows the rows to be written to the output */ public final void writeStringRowsAndClose(Collection allRows) { try { writeStringRows(allRows); } finally { close(); } } /** * Iterates over all records, writes them and closes the output. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Iterable)} for that. * * @param allRows the rows to be written to the output */ public final void writeRecordsAndClose(Collection allRows) { try { writeRecords(allRows); } finally { close(); } } /** * Iterates over all records, writes them and closes the output. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Object[])} for that. * * @param allRows the rows to be written to the output */ public final void writeRowsAndClose(Object[][] allRows) { try { writeRows(allRows); } finally { close(); } } /** * Iterates over all records and writes them to the output. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Object[])} for that. * * @param rows the rows to be written to the output */ public final void writeRows(Object[][] rows) { for (Object[] row : rows) { writeRow(row); } } /** * Iterates over all records and writes them to the output. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param Collection of objects containing values of a row * @param rows the rows to be written to the output */ public final > void writeRows(Iterable rows) { for (Collection row : rows) { writeRow(row); } } /** * Iterates over all records and writes them to the output. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param rows the rows to be written to the output */ public final void writeStringRows(Collection rows) { for (String[] row : rows) { writeRow(row); } } /** * Iterates over all records and writes them to the output. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param rows the rows to be written to the output */ public final void writeRecords(Collection rows) { for (Record row : rows) { writeRecord(row); } } /** * Iterates over all records and writes them to the output. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param Collection of objects containing values of a row * @param rows the rows to be written to the output */ public final > void writeStringRows(Iterable rows) { for (Collection row : rows) { writeRow(row.toArray()); } } /** * Iterates over all records and writes them to the output. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param rows the rows to be written to the output */ public final void writeRows(Collection rows) { for (Object[] row : rows) { writeRow(row); } } /** * Writes the data given for an individual record. * The output will remain open for further writing. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to the output */ public final void writeRow(Collection row) { if (row == null) { return; } writeRow(row.toArray()); } /** * Writes the data given for an individual record. * The output will remain open for further writing. * If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, the input will be just ignored. * If {@link CommonSettings#getSkipEmptyLines()} is false, then an empty row will be written to the output (as specified by {@link * AbstractWriter#writeEmptyRow()}). * In case of any errors, a {@link TextWritingException} will be thrown and the {@link java.io.Writer} given in the constructor will be closed. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to the output */ public final void writeRow(String[] row) { writeRow((Object[]) row); } /** * Writes the data given for an individual record. * The output will remain open for further writing. * If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, the input will be just ignored. * If {@link CommonSettings#getSkipEmptyLines()} is false, then an empty row will be written to the output (as specified by {@link * AbstractWriter#writeEmptyRow()}). * In case of any errors, a {@link TextWritingException} will be thrown and the {@link java.io.Writer} given in the constructor will be closed. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to the output * @param the concrete Record type */ public final void writeRecord(T row) { if (row == null) { if (skipEmptyLines) { return; } else { writeEmptyRow(); return; } } if (recordCount == 0 && isHeaderWritingEnabled && headers == null) { String[] headers = row.getMetaData().headers(); if (headers != null) { this.headers = NormalizedString.toArray(headers); } } writeRow((Object[]) row.getValues()); } /** * Writes the data given for an individual record. * The output will remain open for further writing. * If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, the input will be just ignored. * If {@link CommonSettings#getSkipEmptyLines()} is false, then an empty row will be written to the output (as specified by {@link * AbstractWriter#writeEmptyRow()}). * In case of any errors, a {@link TextWritingException} will be thrown and the {@link java.io.Writer} given in the constructor will be closed. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to the output */ public final void writeRow(Object... row) { try { if (recordCount == 0 && isHeaderWritingEnabled && headers != null) { writeHeaders(); } if (row == null || (row.length == 0 && !expandRows)) { if (skipEmptyLines) { return; } else { writeEmptyRow(); return; } } row = adjustRowLength(row); submitRow(row); internalWriteRow(); } catch (Throwable ex) { throw throwExceptionAndClose("Error writing row.", row, ex); } } protected Object[] expand(Object[] row, int length, Integer h2) { if (row.length < length) { return Arrays.copyOf(row, length); } else if (h2 != null && row.length < h2) { return Arrays.copyOf(row, h2); } if (length == -1 && h2 == null && row.length < largestRowLength) { return Arrays.copyOf(row, largestRowLength); } return row; } /** * Writes a plain (potentially free-text) String as a line to the output. * A newline will automatically written after the given contents, unless this is a * {@link com.univocity.parsers.fixed.FixedWidthWriter} whose * {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}. * The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()} * The writer implementation has no control over the format of this content. * The output will remain open for further writing. * * @param row the line to be written to the output */ public final void writeRow(String row) { try { writer.write(row); if (enableNewlineAfterRecord) { writer.write(lineSeparator); } } catch (Throwable ex) { throw throwExceptionAndClose("Error writing row.", row, ex); } } /** * Writes an empty line to the output, unless this is a {@link com.univocity.parsers.fixed.FixedWidthWriter} whose * {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}. * The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()} * The output will remain open for further writing. */ public final void writeEmptyRow() { try { if (enableNewlineAfterRecord) { writer.write(lineSeparator); } } catch (Throwable ex) { throw throwExceptionAndClose("Error writing empty row.", Arrays.toString(lineSeparator), ex); } } /** * Writes a comment row to the output. * A newline will automatically written after the given contents, unless this is a * {@link com.univocity.parsers.fixed.FixedWidthWriter} whose * {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}. * The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()} * The output will remain open for further writing. * * @param comment the contents to be written as a comment to the output */ public final void commentRow(String comment) { writeRow(this.comment + comment); } /** * Used when fields were selected and the input rows have a different order than the output. This method * fills the internal #outputRow array with the values provided by the user in the correct order. * * @param row user-provided data which has to be rearranged to the expected record sequence before writing to the output. */ private void fillOutputRow(T[] row) { if (!columnReorderingEnabled && row.length > outputRow.length) { outputRow = Arrays.copyOf(outputRow, row.length); } if (indexesToWrite.length < row.length) { if (columnReorderingEnabled) { for (int i = 0; i < indexesToWrite.length; i++) { outputRow[i] = row[indexesToWrite[i]]; } } else { for (int i = 0; i < indexesToWrite.length; i++) { outputRow[indexesToWrite[i]] = row[indexesToWrite[i]]; } } } else { for (int i = 0; i < row.length && i < indexesToWrite.length; i++) { if (indexesToWrite[i] != -1) { outputRow[indexesToWrite[i]] = row[i]; } } } } /** * Writes the accumulated value of a record to the output, followed by a newline, and increases the record count. * The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()} * The contents of {@link AbstractWriter#rowAppender} depend on the concrete implementation of {@link AbstractWriter#processRow(Object[])} */ private void internalWriteRow() { try { if (skipEmptyLines && rowAppender.length() == 0) { return; } if (enableNewlineAfterRecord) { rowAppender.appendNewLine(); } rowAppender.writeCharsAndReset(writer); recordCount++; } catch (Throwable ex) { throw throwExceptionAndClose("Error writing row.", rowAppender.getAndReset(), ex); } } /** * Identifies the starting character index of a value being written if leading whitespaces are to be discarded. * Implementation note whitespaces are considered all characters where {@code ch <= ' '} evaluates to {@code true} * * @param whitespaceRangeStart starting range after which characters will be considered whitespace * @param element the String to be scanned for leading whitespaces. * * @return the index of the first non-whitespace character in the given element. */ protected static int skipLeadingWhitespace(int whitespaceRangeStart, String element) { if (element.isEmpty()) { return 0; } for (int i = 0; i < element.length(); i++) { char nextChar = element.charAt(i); if (!(nextChar <= ' ' && whitespaceRangeStart < nextChar)) { return i; } } return element.length(); } /** * Flushes the {@link java.io.Writer} given in this class constructor. * An IllegalStateException will be thrown in case of any errors, and the writer will be closed. */ public final void flush() { try { writer.flush(); } catch (Throwable ex) { throw throwExceptionAndClose("Error flushing output.", rowAppender.getAndReset(), ex); } } /** * Closes the {@link java.io.Writer} given in this class constructor. * An IllegalStateException will be thrown in case of any errors. */ public final void close() { try { this.headerIndexes = null; if (writer != null) { writer.close(); writer = null; } } catch (Throwable ex) { throw new IllegalStateException("Error closing the output.", ex); } if (this.partialLineIndex != 0) { throw new TextWritingException("Not all values associated with the last record have been written to the output. " + "\n\tHint: use 'writeValuesToRow()' or 'writeValuesToString()' to flush the partially written values to a row.", recordCount, getContent(Arrays.copyOf(partialLine, partialLineIndex))); } } /** * In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed. * * @param message Description of the error */ private TextWritingException throwExceptionAndClose(String message) { return throwExceptionAndClose(message, (Object[]) null, null); } /** * In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed. * * @param message Description of the error * @param cause the exception to be wrapped by a {@link TextWritingException} */ private TextWritingException throwExceptionAndClose(String message, Throwable cause) { return throwExceptionAndClose(message, (Object[]) null, cause); } /** * In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed. * * @param message Description of the error * @param recordCharacters characters used to write to the output at the time the exception happened * @param cause the exception to be wrapped by a {@link TextWritingException} */ private TextWritingException throwExceptionAndClose(String message, String recordCharacters, Throwable cause) { try { if (cause instanceof NullPointerException && writer == null) { message = message + " No writer provided in the constructor of " + getClass().getName() + ". You can only use operations that write to Strings."; } throw new TextWritingException(message, recordCount, getContent(recordCharacters), cause); } finally { close(); } } /** * In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed. * * @param message Description of the error * @param recordValues values used to write to the output at the time the exception happened * @param cause the exception to be wrapped by a {@link TextWritingException} */ private TextWritingException throwExceptionAndClose(String message, Object[] recordValues, Throwable cause) { try { throw new TextWritingException(message, recordCount, getContent(recordValues), cause); } finally { try { close(); } catch (Throwable t) { //ignore and let original error go. } } } /** * Converts a given object to its String representation for writing to a {@code String} *
    *
  • If the object is null, then {@link AbstractWriter#nullValue} is returned.
  • *
  • If the String representation of this object is an empty String, then {@link AbstractWriter#emptyValue} is returned
  • *
* * @param element the object to be converted into a String. * * @return the String representation of the given object */ protected String getStringValue(Object element) { usingNullOrEmptyValue = false; if (element == null) { usingNullOrEmptyValue = true; return nullValue; } String string = String.valueOf(element); if (string.isEmpty()) { usingNullOrEmptyValue = true; return emptyValue; } if (this.ignoreLeading) { int start = skipLeadingWhitespace(whitespaceRangeStart, string); if (start == string.length()) { usingNullOrEmptyValue = true; return emptyValue; } } return string; } /** * Writes a sequence of values to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link * #writeValuesToRow} is called to flush * all values accumulated and effectively write a new record to the output * * @param values the values to be written */ public final void addValues(Object... values) { try { System.arraycopy(values, 0, partialLine, partialLineIndex, values.length); partialLineIndex += values.length; } catch (Throwable t) { throw throwExceptionAndClose("Error adding values to in-memory row", values, t); } } /** * Writes a sequence of Strings to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link * #writeValuesToRow} is called to flush * all values accumulated and effectively write a new record to the output * * @param values the values to be written */ public final void addStringValues(Collection values) { if (values != null) { try { for (String o : values) { partialLine[partialLineIndex++] = o; } } catch (Throwable t) { throw throwExceptionAndClose("Error adding values to in-memory row", values.toArray(), t); } } } /** * Writes a sequence of values to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link * #writeValuesToRow} is called to flush * all values accumulated and effectively write a new record to the output * * @param values the values to be written */ public final void addValues(Collection values) { if (values != null) { try { for (Object o : values) { partialLine[partialLineIndex++] = o; } } catch (Throwable t) { throw throwExceptionAndClose("Error adding values to in-memory row", values.toArray(), t); } } } /** * Writes a value to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link * #writeValuesToRow} is called to flush * all values accumulated and effectively write a new record to the output * * @param value the value to be written */ public final void addValue(Object value) { try { partialLine[partialLineIndex++] = value; } catch (Throwable t) { throw throwExceptionAndClose("Error adding value to in-memory row", new Object[]{value}, t); } } private void fillPartialLineToMatchHeaders() { if (headers != null && partialLineIndex < headers.length) { while (partialLineIndex < headers.length) { partialLine[partialLineIndex++] = null; } } } /** * Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #writeValue()} to a new record in the output. */ public final void writeValuesToRow() { fillPartialLineToMatchHeaders(); writeRow(Arrays.copyOf(partialLine, partialLineIndex)); discardValues(); } /** * Writes a value to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link * #writeValuesToRow} is called to flush * all values accumulated and effectively write a new record to the output * * @param index the position in the row that should receive the value. * @param value the value to be written */ public final void addValue(int index, Object value) { if (index >= partialLine.length) { throw throwExceptionAndClose("Cannot write '" + value + "' to index '" + index + "'. Maximum number of columns (" + partialLine.length + ") exceeded.", new Object[]{value}, null); } partialLine[index] = value; if (partialLineIndex <= index) { partialLineIndex = index + 1; } } /** * Writes a value to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link * #writeValuesToRow} is called to flush * all values accumulated and effectively write a new record to the output * * @param headerName the name of the column of the new row that should receive the value. * @param value the value to be written */ public final void addValue(String headerName, Object value) { addValue(getFieldIndex(headers, NormalizedString.valueOf(headerName), false), value); } private final void addValue(NormalizedString[] headersInContext, NormalizedString headerName, boolean ignoreOnMismatch, Object value) { int index = getFieldIndex(headersInContext, headerName, ignoreOnMismatch); if (index != -1) { addValue(index, value); } } /** * Calculates the index of a header name in relation to the original {@link #headers} array defined in this writer * * @param headersInContext headers currently in use (they might change). * @param headerName the name of the header whose position will be identified * @param ignoreOnMismatch flag indicating that if the header is not found, no exception is to be thrown, and -1 should be returned instead. * * @return the position of the given header, or -1 if it's not found when ignoreOnMismatch is set to {@code true} */ private int getFieldIndex(NormalizedString[] headersInContext, NormalizedString headerName, boolean ignoreOnMismatch) { if (headerIndexes == null) { headerIndexes = new HashMap>(); } Map indexes = headerIndexes.get(headersInContext); if (indexes == null) { indexes = new HashMap(); headerIndexes.put(headersInContext, indexes); } Integer index = indexes.get(headerName); if (index == null) { if (headersInContext == null) { throw throwExceptionAndClose("Cannot calculate position of header '" + headerName + "' as no headers were defined.", null); } index = ArgumentUtils.indexOf(NormalizedString.toArray(headersInContext), NormalizedString.valueOf(headerName)); if (index == -1) { if (!ignoreOnMismatch) { throw throwExceptionAndClose("Header '" + headerName + "' could not be found. Defined headers are: " + Arrays.toString(headersInContext) + '.', null); } } indexes.put(headerName, index); } return index; } /** * Discards the contents written to the internal in-memory row (using {@link #addValues(Object...) or #writeValue()}. */ public final void discardValues() { Arrays.fill(partialLine, 0, partialLineIndex, null); partialLineIndex = 0; } /** * Writes the headers defined in {@link CommonSettings#getHeaders()} to a {@code String} * * @return a formatted {@code String} containing the headers defined in {@link CommonSettings#getHeaders()} */ public final String writeHeadersToString() { return writeHeadersToString(NormalizedString.toArray(this.headers)); } /** * Writes the given collection of headers to a {@code String} * A {@link TextWritingException} will be thrown if no headers were defined. * * @param headers the headers to write to a {@code String} * * @return a formatted {@code String} containing the given headers */ public final String writeHeadersToString(Collection headers) { if (headers != null && headers.size() > 0) { return writeHeadersToString(headers.toArray(new String[headers.size()])); } else { throw throwExceptionAndClose("No headers defined"); } } /** * Writes the given collection of headers to a {@code String} * A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to a {@code String} * * @param headers the headers to write to a {@code String} * * @return a formatted {@code String} containing the given headers */ public final String writeHeadersToString(String... headers) { if (headers != null && headers.length > 0) { writingHeaders = true; submitRow(headers); writingHeaders = false; this.headers = NormalizedString.toIdentifierGroupArray(headers); return internalWriteRowToString(); } else { throw throwExceptionAndClose("No headers defined."); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them to a {@code List} of {@code String}. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param records the records to be transformed by a {@link RowWriterProcessor} and then written to a {@code List} of {@code String}. * * @return a {@code List} containing the information transformed from the given records as formatted {@code String}s */ public final List processRecordsToString(Iterable records) { try { List out = new ArrayList(1000); for (Object record : records) { out.add(processRecordToString(record)); } return out; } catch (Throwable t) { throw throwExceptionAndClose("Unable process input records", t); } } /** * Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and * writes them them to a {@code List} of {@code String}. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param records the records to transformed by a {@link RowWriterProcessor} and then written a {@code String}. * * @return a {@code List} containing the information transformed from the given records as formatted {@code String}s */ public final List processRecordsToString(Object[] records) { try { List out = new ArrayList(1000); for (Object record : records) { out.add(processRecordToString(record)); } return out; } catch (Throwable t) { throw throwExceptionAndClose("Unable process input records", records, t); } } /** * Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes it to a {@code String}. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to a {@code String}. * * @return a formatted {@code String} containing the information transformed from the given record */ public final String processRecordToString(Object... record) { return processRecordToString((Object) record); } /** * Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes it to a {@code String}. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to a {@code String}. * @param the concrete Record type * * @return a formatted {@code String} containing the information transformed from the given record */ public final String processRecordToString(T record) { return processRecordToString((Object) (record == null ? null : record.getValues())); } /** * Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes it. * The output will remain open for further writing. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to a {@code String}. * * @return a formatted {@code String} containing the information transformed from the given record */ @SuppressWarnings("unchecked") public final String processRecordToString(Object record) { if (this.writerProcessor == null) { throw throwExceptionAndClose("Cannot process record '" + record + "' without a writer processor. Please define a writer processor instance in the settings or use the 'writeRow' methods."); } try { Object[] row = writerProcessor.write(record, getRowProcessorHeaders(), indexesToWrite); if (row != null) { return writeRowToString(row); } } catch (Throwable t) { throw throwExceptionAndClose("Could not process record '" + record + "'", t); } return null; } /** * Iterates over all records and writes them to a {@code List} of {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Object[])} for that. * * @param rows the rows to be written to a {@code List} of {@code String}. * * @return a {@code List} containing the given rows as formatted {@code String}s */ public final List writeRowsToString(Object[][] rows) { try { List out = new ArrayList(rows.length); for (Object[] row : rows) { String string = writeRowToString(row); if (string != null) { out.add(string); } } return out; } catch (Throwable t) { throw throwExceptionAndClose("Error writing input rows", t); } } /** * Iterates over all records and writes them to a {@code List} of {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param Collection of objects containing values of a row * @param rows the rows to be written to a {@code List} of {@code String}. * * @return a {@code List} containing the given rows as formatted {@code String}s */ public final > List writeRowsToString(Iterable rows) { try { List out = new ArrayList(1000); for (Collection row : rows) { out.add(writeRowToString(row)); } return out; } catch (Throwable t) { throw throwExceptionAndClose("Error writing input rows", t); } } /** * Iterates over all records and writes them to a {@code List} of {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param Collection of objects containing values of a row * @param rows the rows to be written to a {@code List} of {@code String}. * * @return a {@code List} containing the given rows as formatted {@code String}s */ public final > List writeStringRowsToString(Iterable rows) { try { List out = new ArrayList(1000); for (Collection row : rows) { String string = writeRowToString(row); if (string != null) { out.add(string); } } return out; } catch (Throwable t) { throw throwExceptionAndClose("Error writing input rows", t); } } /** * Iterates over all records and writes them to a {@code List} of {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param rows the rows to be written to a {@code List} of {@code String}. * * @return a {@code List} containing the given rows as formatted {@code String}s */ public final List writeRowsToString(Collection rows) { try { List out = new ArrayList(rows.size()); for (Object[] row : rows) { out.add(writeRowToString(row)); } return out; } catch (Throwable t) { throw throwExceptionAndClose("Error writing input rows", t); } } /** * Iterates over all records and writes them to a {@code List} of {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param rows the rows to be written to a {@code List} of {@code String}. * * @return a {@code List} containing the given rows as formatted {@code String}s */ public final List writeStringRowsToString(Collection rows) { try { List out = new ArrayList(rows.size()); for (String[] row : rows) { out.add(writeRowToString(row)); } return out; } catch (Throwable t) { throw throwExceptionAndClose("Error writing input rows", t); } } /** * Iterates over all records and writes them to a {@code List} of {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that. * * @param rows the rows to be written to a {@code List} of {@code String}. * * @return a {@code List} containing the given rows as formatted {@code String}s */ public final List writeRecordsToString(Collection rows) { try { List out = new ArrayList(rows.size()); for (Record row : rows) { out.add(writeRecordToString(row)); } return out; } catch (Throwable t) { throw throwExceptionAndClose("Error writing input rows", t); } } /** * Writes the data given for an individual record to a {@code String}. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to a {@code String} * * @return a formatted {@code String} containing the information of the given record */ public final String writeRowToString(Collection row) { try { if (row == null) { return null; } return writeRowToString(row.toArray()); } catch (Throwable t) { throw throwExceptionAndClose("Error writing input row ", t); } } /** * Writes the data given for an individual record to a {@code String}. * If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, {@code null} will be returned * In case of any errors, a {@link TextWritingException} will be thrown. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to a {@code String}. * * @return a formatted {@code String} containing the information of the given record */ public final String writeRowToString(String[] row) { return writeRowToString((Object[]) row); } /** * Writes the data given for an individual record to a {@code String}. * If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, {@code null} will be returned * In case of any errors, a {@link TextWritingException} will be thrown. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to a {@code String}. * * @return a formatted {@code String} containing the information of the given record */ public final String writeRecordToString(T row) { return writeRowToString((Object[]) (row == null ? null : row.getValues())); } /** * Writes the data given for an individual record to a {@code String}. * If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, {@code null} will be returned * In case of any errors, a {@link TextWritingException} will be thrown. * Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that. * * @param row the information of a single record to be written to a {@code String}. * * @return a formatted {@code String} containing the information of the given record */ public final String writeRowToString(Object... row) { try { if (row == null || (row.length == 0 && !expandRows)) { if (skipEmptyLines) { return null; } } row = adjustRowLength(row); submitRow(row); return internalWriteRowToString(); } catch (Throwable ex) { throw throwExceptionAndClose("Error writing row.", row, ex); } } private Object[] adjustRowLength(Object[] row) { if (outputRow != null) { fillOutputRow(row); row = outputRow; } else if (expandRows) { if (usingSwitch) { row = expand(row, dummyHeaderRow == null ? -1 : dummyHeaderRow.length, headers == null ? null : headers.length); dummyHeaderRow = null; } else { row = expand(row, headers == null ? -1 : headers.length, null); } } return row; } /** * Writes a comment row to a {@code String} * * @param comment the contents to be written as a comment to a {@code String}. * * @return a formatted {@code String} containing the comment. */ public final String commentRowToString(String comment) { return writeRowToString(this.comment + comment); } /** * Writes the accumulated value of a record to the output, followed by a newline, and increases the record count. * The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()} * The contents of {@link AbstractWriter#rowAppender} depend on the concrete implementation of {@link AbstractWriter#processRow(Object[])} * * @return a formatted {@code String} containing the comment. */ private String internalWriteRowToString() { if (skipEmptyLines && rowAppender.length() == 0) { return null; } String out = rowAppender.getAndReset(); recordCount++; return out; } /** * Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #addValue()} as a {@code String} * * @return a formatted {@code String} containing the information accumulated in the internal in-memory row. */ public final String writeValuesToString() { fillPartialLineToMatchHeaders(); String out = writeRowToString(Arrays.copyOf(partialLine, partialLineIndex)); discardValues(); return out; } /** * Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #addValue()} to a new record in the output. * The objects added to this row will be processed with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * The output will remain open for further writing. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. */ public final void processValuesToRow() { fillPartialLineToMatchHeaders(); processRecord(Arrays.copyOf(partialLine, partialLineIndex)); discardValues(); } /** * Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #addValue()} to a {@code String} * The objects added to this row will be processed with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * The output will remain open for further writing. * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @return a formatted {@code String} containing the result produced by the {@link RowWriterProcessor} using the values accumulated in internal in-memory row. */ public final String processValuesToString() { fillPartialLineToMatchHeaders(); String out = processRecordToString(Arrays.copyOf(partialLine, partialLineIndex)); discardValues(); return out; } /** * Returns the number of records written to the output so far * * @return the number of records written to the output so far */ public final long getRecordCount() { return recordCount; } /** * Writes values from an implementation of {@link java.util.Map} to a partial output record, ready to be * written to the output. * * Values will be stored under a column identified by the headers. If no headers are defined, * the keys of the map will be used to initialize an internal header row. * * A map of headers can be optionally provided to assign a name to the keys of the input map. This is useful when * the input map has keys will generate unwanted header names. * * @param headerMapping an optional map associating keys of the rowData map with expected header names * @param rowData the data to be written. Its keys will be used to form a header row in case no headers are available. * @param type of the key in both rowData and headerMapping maps. */ private void writeValuesFromMap(Map headerMapping, Map rowData) { try { if (rowData != null && !rowData.isEmpty()) { dummyHeaderRow = this.headers; if (usingSwitch) { dummyHeaderRow = ((RowWriterProcessorSwitch) writerProcessor).getHeaders(headerMapping, rowData); if (dummyHeaderRow == null) { dummyHeaderRow = this.headers; } } if (dummyHeaderRow != null) { if (headerMapping == null) { for (Map.Entry e : rowData.entrySet()) { addValue(dummyHeaderRow, NormalizedString.valueOf(e.getKey()), true, e.getValue()); } } else { for (Map.Entry e : rowData.entrySet()) { String header = headerMapping.get(e.getKey()); if (header != null) { addValue(dummyHeaderRow, NormalizedString.valueOf(header), true, e.getValue()); } } } } else if (headerMapping != null) { setHeadersFromMap(headerMapping, false); writeValuesFromMap(headerMapping, rowData); } else { setHeadersFromMap(rowData, true); writeValuesFromMap(null, rowData); } } } catch (Throwable t) { throw throwExceptionAndClose("Error processing data from input map", t); } } /** * Iterates over the keys of a map and builds an internal header row. * * @param map the input map whose keys will be used to generate headers for the output. * @param keys indicates whether to take the map keys or values to build the header rows. */ private void setHeadersFromMap(Map map, boolean keys) { this.headers = new NormalizedString[map.size()]; int i = 0; for (Object header : keys ? map.keySet() : map.values()) { headers[i++] = NormalizedString.valueOf(header); } } /** * Writes the values of a given map to a {@code String} formatted to according to the specified output format. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a {@code String}. * * @return a {@code String} containing the given data as a formatted {@code String} */ public final String writeRowToString(Map rowData) { return writeRowToString(null, (Map) rowData); } /** * Writes the values of a given map into new output record * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a new record */ public final void writeRow(Map rowData) { writeRow(null, (Map) rowData); } /** * Writes the values of a given map to a {@code String} formatted to according to the specified output format. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code String}. * @param the key type * * @return a {@code String} containing the given data as a formatted {@code String} */ public final String writeRowToString(Map headerMapping, Map rowData) { writeValuesFromMap(headerMapping, rowData); return writeValuesToString(); } /** * Writes the values of a given map into new output record * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a new record * @param the key type */ public final void writeRow(Map headerMapping, Map rowData) { writeValuesFromMap(headerMapping, rowData); writeValuesToRow(); } /** * Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * @param the iterable type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final > List writeRowsToString(Map rowData) { return writeRowsToString(null, rowData); } /** * Writes the values of a given map to multiple output records * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void writeRows(Map rowData) { writeRows(null, rowData, null, false); } /** * Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * @param the iterable type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final > List writeRowsToString(Map headerMapping, Map rowData) { List writtenRows = new ArrayList(); writeRows(headerMapping, rowData, writtenRows, false); return writtenRows; } /** * Writes the values of a given map to multiple output records * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void writeRows(Map headerMapping, Map rowData) { writeRows(headerMapping, rowData, null, false); } /** * Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param outputList an output {@code List} to fill with formatted {@code String}s, each {@code String} representing * one successful iteration over at least one * element of the iterators in the map. * @param the key type * @param the iterable type */ private > void writeRows(Map headerMapping, Map rowData, List outputList, boolean useRowProcessor) { try { Iterator[] iterators = new Iterator[rowData.size()]; Object[] keys = new Object[rowData.size()]; final Map rowValues = new LinkedHashMap(rowData.size()); if (outputList != null && headers == null) { if (headerMapping != null) { setHeadersFromMap(headerMapping, true); } else { setHeadersFromMap(rowData, true); } } if (recordCount == 0 && headers != null && isHeaderWritingEnabled) { outputList.add(writeHeadersToString()); } int length = 0; for (Map.Entry rowEntry : rowData.entrySet()) { iterators[length] = rowEntry.getValue() == null ? null : rowEntry.getValue().iterator(); keys[length] = rowEntry.getKey(); rowValues.put(rowEntry.getKey(), null); length++; } boolean nullsOnly; do { nullsOnly = true; for (int i = 0; i < length; i++) { Iterator iterator = iterators[i]; boolean isNull = iterator == null || !iterator.hasNext(); nullsOnly &= isNull; if (isNull) { rowValues.put(keys[i], null); } else { rowValues.put(keys[i], iterator.next()); } } if (!nullsOnly) { if (outputList == null) { if (useRowProcessor) { processRecord((Map) headerMapping, (Map) rowValues); } else { writeRow((Map) headerMapping, (Map) rowValues); } } else { if (useRowProcessor) { outputList.add(processRecordToString((Map) headerMapping, (Map) rowValues)); } else { outputList.add(writeRowToString((Map) headerMapping, (Map) rowValues)); } } } } while (!nullsOnly); } catch (Throwable t) { throw throwExceptionAndClose("Error processing input rows from map", t); } } /** * Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final List writeStringRowsToString(Map headerMapping, Map rowData) { List writtenRows = new ArrayList(); writeRows(headerMapping, wrapStringArray(rowData), writtenRows, false); return writtenRows; } /** * Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final List writeRecordsToString(Map headerMapping, Map rowData) { List writtenRows = new ArrayList(); writeRows(headerMapping, wrapRecordValues(rowData), writtenRows, false); return writtenRows; } /** * Writes the values of a given map to multiple output records * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeStringRows(Map headerMapping, Map rowData) { writeRows(headerMapping, wrapStringArray(rowData), null, false); } /** * Writes the values of a given map to multiple output records * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeRecords(Map headerMapping, Map rowData) { writeRows(headerMapping, wrapRecordValues(rowData), null, false); } /** * Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final List writeObjectRowsToString(Map headerMapping, Map rowData) { List writtenRows = new ArrayList(); writeRows(headerMapping, wrapObjectArray(rowData), writtenRows, false); return writtenRows; } /** * Writes the values of a given map to multiple output records * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeObjectRows(Map headerMapping, Map rowData) { writeRows(headerMapping, wrapObjectArray(rowData), null, false); } private Map> wrapObjectArray(Map rowData) { Map> out = new LinkedHashMap>(rowData.size()); for (Map.Entry e : rowData.entrySet()) { if (e.getValue() == null) { out.put(e.getKey(), Collections.emptyList()); } else { out.put(e.getKey(), Arrays.asList(e.getValue())); } } return out; } private Map> wrapStringArray(Map rowData) { Map> out = new LinkedHashMap>(rowData.size()); for (Map.Entry e : rowData.entrySet()) { if (e.getValue() == null) { out.put(e.getKey(), Collections.emptyList()); } else { out.put(e.getKey(), Arrays.asList(e.getValue())); } } return out; } private Map> wrapRecordValues(Map rowData) { Map> out = new LinkedHashMap>(rowData.size()); for (Map.Entry e : rowData.entrySet()) { if (e.getValue() == null) { out.put(e.getKey(), Collections.emptyList()); } else { out.put(e.getKey(), Arrays.asList(e.getValue().getValues())); } } return out; } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeObjectRowsAndClose(Map headerMapping, Map rowData) { try { writeObjectRows(headerMapping, rowData); } finally { close(); } } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeStringRowsAndClose(Map headerMapping, Map rowData) { try { writeStringRows(headerMapping, rowData); } finally { close(); } } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeRecordsAndClose(Map headerMapping, Map rowData) { try { writeRecords(headerMapping, rowData); } finally { close(); } } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeObjectRowsAndClose(Map rowData) { writeObjectRowsAndClose(null, rowData); } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeStringRowsAndClose(Map rowData) { writeStringRowsAndClose(null, rowData); } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void writeRecordsAndClose(Map rowData) { writeRecordsAndClose(null, rowData); } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void writeRowsAndClose(Map headerMapping, Map rowData) { try { writeRows(headerMapping, rowData); } finally { close(); } } /** * Writes the values of a given map to multiple output records and closes the output when finished. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * Note this method will not use the {@link RowWriterProcessor}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void writeRowsAndClose(Map rowData) { writeRowsAndClose(null, rowData); } /** * Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into {@code String} formatted according to the specified output format. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * * @return a {@code String} containing the given data as a formatted {@code String} */ public final String processRecordToString(Map rowData) { return processRecordToString(null, (Map) rowData); } /** * Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into a new output record * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. */ public final void processRecord(Map rowData) { processRecord(null, (Map) rowData); } /** * Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into {@code String} formatted according to the specified output format. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * * @return a {@code String} containing the given data as a formatted {@code String} */ public final String processRecordToString(Map headerMapping, Map rowData) { writeValuesFromMap(headerMapping, rowData); return processValuesToString(); } /** * Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into a new output record * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type */ public final void processRecord(Map headerMapping, Map rowData) { writeValuesFromMap(headerMapping, rowData); processValuesToRow(); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * @param the iterable type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final > List processRecordsToString(Map rowData) { return processRecordsToString(null, rowData); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output . * * The output will remain open for further write operations. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void processRecords(Map rowData) { writeRows(null, rowData, null, true); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * @param the iterable type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final > List processRecordsToString(Map headerMapping, Map rowData) { List writtenRows = new ArrayList(); writeRows(headerMapping, rowData, writtenRows, true); return writtenRows; } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output . * * The output will remain open for further write operations. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void processRecords(Map headerMapping, Map rowData) { writeRows(headerMapping, rowData, null, true); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final List processObjectRecordsToString(Map rowData) { return processObjectRecordsToString(null, rowData); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()} * and writes the result into a {@code List} of {@code String} formatted to according to the specified output format. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a {@code List} of {@code String}. * @param the key type * * @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one * element of the iterators in the map. */ public final List processObjectRecordsToString(Map headerMapping, Map rowData) { List writtenRows = new ArrayList(); writeRows(headerMapping, wrapObjectArray(rowData), writtenRows, true); return writtenRows; } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output . * * The output will remain open for further write operations. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void processObjectRecords(Map headerMapping, Map rowData) { writeRows(headerMapping, wrapObjectArray(rowData), null, true); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output and closes the writer. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void processObjectRecordsAndClose(Map headerMapping, Map rowData) { try { processObjectRecords(headerMapping, rowData); } finally { close(); } } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output and closes the writer. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type */ public final void processObjectRecordsAndClose(Map rowData) { processRecordsAndClose(null, wrapObjectArray(rowData)); } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output and closes the writer. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param headerMapping a mapping associating the keys of the input map to their corresponding header names. * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void processRecordsAndClose(Map headerMapping, Map rowData) { try { processRecords(headerMapping, rowData); } finally { close(); } } /** * Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, * then writes all values to the output and closes the writer. * * Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable. * * A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written * when a iterator has been fully read. * * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}. * * @param rowData the map whose values will be used to generate a number of output records * @param the key type * @param the iterable type */ public final > void processRecordsAndClose(Map rowData) { processRecordsAndClose(null, rowData); } private Object[] getContent(Object[] tmp) { return AbstractException.restrictContent(errorContentLength, tmp); } private String getContent(CharSequence tmp) { return AbstractException.restrictContent(errorContentLength, tmp); } /** * Checks whether the writer can remove trailing/leading whitespaces from a value being written. * Applies to headers where names can be conflicting if trimmed for example {@code ' a '} and * {@code 'a'} will become the same value if the spaces are removed) * * @param fieldIndex index of the field to be written. * * @return {@code true} if the value being written is not a header name, or it is a header name that won't * conflict with another header if its surrounding whitespaces are trimmed. */ protected final boolean allowTrim(int fieldIndex) { return !writingHeaders || fieldIndex >= headerTrimFlags.length || headerTrimFlags[fieldIndex]; } }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ArgumentUtils.java000066400000000000000000000467041400120543400311150ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.input.*; import java.io.*; import java.nio.charset.*; import java.util.*; import static java.lang.reflect.Array.*; /** * An utility class for validating inputs. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class ArgumentUtils { /** * An empty String array. */ public static final String[] EMPTY_STRING_ARRAY = new String[0]; public static final NormalizedString[] EMPTY_NORMALIZED_STRING_ARRAY = new NormalizedString[0]; /** * Throws an IllegalArgumentException if the given array is null or empty. * * @param argDescription the description of the elements * @param args the elements to be validated. * @param Type of arguments to be validated */ public static void notEmpty(String argDescription, T... args) { if (args == null) { throw new IllegalArgumentException(argDescription + " must not be null"); } if (args.length == 0) { throw new IllegalArgumentException(argDescription + " must not be empty"); } } /** * Throws an IllegalArgumentException if the given array is null,empty, or contains null values * * @param argDescription the description of the elements * @param args the elements to be validated. * @param Type of arguments to be validated */ public static void noNulls(String argDescription, T... args) { notEmpty(argDescription, args); for (T arg : args) { if (arg == null) { if (args.length > 0) { throw new IllegalArgumentException(argDescription + " must not contain nulls"); } else { throw new IllegalArgumentException(argDescription + " must not be null"); } } } } /** * Returns the index of a header, when headers are selected using a {@link FieldSelector}. * * @param array the element array * @param element the element to be looked for in the array. * @param fieldSelector a field selector that indicates which elements of the given array are selected. * * @return the index of the given element in the array, or -1 if the element could not be found. */ public static int indexOf(NormalizedString[] array, NormalizedString element, FieldSelector fieldSelector) { int index = indexOf(array, element); if (fieldSelector == null || index == -1) { return index; } int[] indexes = fieldSelector.getFieldIndexes(array); for (int i = 0; i < indexes.length; i++) { if (indexes[i] == index) { return i; } } return -1; } /** * Returns the indexes of an element in a given array. * * @param array the element array * @param element the element to be looked for in the array. * * @return the indexes of the given element in the array, or an empty array if no element could be found */ public static int[] indexesOf(Object[] array, Object element) { int[] tmp = new int[0]; int i = 0; int o = 0; while (i < array.length) { i = indexOf(array, element, i); if (i == -1) { break; } tmp = Arrays.copyOf(tmp, tmp.length + 1); tmp[o++] = i; i++; } return tmp; } /** * Returns the index of an element in a given array. * * @param array the element array * @param element the element to be looked for in the array. * * @return the index of the given element in the array, or -1 if the element could not be found. */ public static int indexOf(Object[] array, Object element) { return indexOf(array, element, 0); } /** * Returns the index of a character in a given array. * * @param array the character array * @param element the character to be looked for in the array. * @param from the starting position of the array from where to start the search * * @return the index of the given character in the array, or -1 if the character could not be found. */ public static int indexOf(char[] array, char element, int from) { for (int i = from; i < array.length; i++) { if (array[i] == element) { return i; } } return -1; } /** * Returns the index of an element in a given array. * * @param array the element array * @param element the element to be looked for in the array. * @param from the starting position of the array from where to start the search * * @return the index of the given element in the array, or -1 if the element could not be found. */ private static int indexOf(Object[] array, Object element, int from) { if (array == null) { throw new NullPointerException("Null array"); } if (element == null) { for (int i = from; i < array.length; i++) { if (array[i] == null) { return i; } } } else { if (element.getClass() != array.getClass().getComponentType()) { throw new IllegalStateException("a"); } if (element instanceof String && array instanceof String[]) { for (int i = from; i < array.length; i++) { String e = String.valueOf(array[i]); if (element.toString().equalsIgnoreCase(e)) { return i; } } } else { for (int i = from; i < array.length; i++) { if (element.equals(array[i])) { return i; } } } } return -1; } /** * Searches for elements in a given array and returns the elements not found. * * @param array An array with elements * @param elements the elements to be found * * @return the elements not found in the array. */ public static Object[] findMissingElements(Object[] array, Collection elements) { return findMissingElements(array, elements.toArray()); } /** * Searches for elements in a given array and returns the elements not found. * * @param array An array with elements * @param elements the elements to be found * * @return the elements not found in the array. */ public static Object[] findMissingElements(Object[] array, Object[] elements) { List out = new ArrayList(); for (Object element : elements) { if (indexOf(array, element) == -1) { out.add(element); } } return out.toArray(); } /** * Creates a {@link java.io.Writer} from an output stream * * @param output the output stream * * @return {@link java.io.Writer} wrapping the given output stream */ public static Writer newWriter(OutputStream output) { return newWriter(output, (Charset) null); } /** * Creates a {@link java.io.Writer} from an output stream * * @param output the output stream * @param encoding the encoding to use when writing to the output stream * * @return {@link java.io.Writer} wrapping the given output stream */ public static Writer newWriter(OutputStream output, String encoding) { return newWriter(output, Charset.forName(encoding)); } /** * Creates a {@link java.io.Writer} from an output stream * * @param output the output stream * @param encoding the encoding to use when writing to the output stream * * @return {@link java.io.Writer} wrapping the given output stream */ public static Writer newWriter(OutputStream output, Charset encoding) { if (encoding != null) { return new OutputStreamWriter(output, encoding); } else { return new OutputStreamWriter(output); } } /** * Creates a {@link java.io.Writer} from a file * * @param file the file to be written * * @return {@link java.io.Writer} for the given file */ public static Writer newWriter(File file) { return newWriter(file, (Charset) null); } /** * Creates a {@link java.io.Writer} from a file * * @param file the file to be written * @param encoding the encoding to use when writing to the file * * @return {@link java.io.Writer} for the given file */ public static Writer newWriter(File file, String encoding) { return newWriter(file, Charset.forName(encoding)); } /** * Creates a {@link java.io.Writer} from a file * * @param file the file to be written * @param encoding the encoding to use when writing to the file * * @return {@link java.io.Writer} for the given file */ public static Writer newWriter(File file, Charset encoding) { if (!file.exists()) { File parent = file.getParentFile(); if (parent != null && !parent.exists()) { parent.mkdirs(); } try { file.createNewFile(); } catch (IOException e) { throw new IllegalArgumentException("Unable to create file '" + file.getAbsolutePath() + "', please ensure your application has permission to create files in that path", e); } } FileOutputStream os; try { os = new FileOutputStream(file); } catch (FileNotFoundException e) { throw new IllegalArgumentException(e); } return newWriter(os, encoding); } /** * Creates a {@link java.io.Reader} from an input stream * * @param input the input stream * * @return a {@link java.io.Reader} wrapping the given input stream */ public static Reader newReader(InputStream input) { return newReader(input, (Charset) null); } /** * Creates a {@link java.io.Reader} from an input stream * * @param input the input stream * @param encoding the encoding to use when reading from the input stream * * @return a {@link java.io.Reader} wrapping the given input stream */ public static Reader newReader(InputStream input, String encoding) { return newReader(input, encoding == null ? (Charset) null : Charset.forName(encoding)); } /** * Creates a {@link java.io.Reader} from an input stream * * @param input the input stream * @param encoding the encoding to use when reading from the input stream * * @return a {@link java.io.Reader} wrapping the given input stream */ public static Reader newReader(InputStream input, Charset encoding) { if (encoding == null) { BomInput bomInput = new BomInput(input); if (bomInput.getEncoding() != null) { //charset detected. Just set the encoding and keep using the original input stream. encoding = bomInput.getCharset(); } if (bomInput.hasBytesStored()) { //there are bytes to be processed. We should use the BomInput wrapper to read the first bytes already consumed when trying to match the BOM. input = bomInput; } //else the original input can be used and the wrapper is not necessary, as a BOM has been matched and the bytes discarded. } if (encoding != null) { return new InputStreamReader(input, encoding); } else { return new InputStreamReader(input); } } /** * Creates a {@link java.io.Reader} for a given a file * * @param file the file to be read * * @return a {@link java.io.Reader} for reading the given file */ public static Reader newReader(File file) { return newReader(file, (Charset) null); } /** * Creates a {@link java.io.Reader} for a given a file * * @param file the file to be read * @param encoding the encoding to be used when reading from the file * * @return a {@link java.io.Reader} for reading the given file */ public static Reader newReader(File file, String encoding) { return newReader(file, Charset.forName(encoding)); } /** * Creates a {@link java.io.Reader} for a given a file * * @param file the file to be read * @param encoding the encoding to be used when reading from the file * * @return a {@link java.io.Reader} for reading the given file */ public static Reader newReader(File file, Charset encoding) { FileInputStream input; try { input = new FileInputStream(file); } catch (FileNotFoundException e) { throw new IllegalArgumentException(e); } return newReader(input, encoding); } /** * Converts a list of enumerations to an array of their {@link Enum#toString()} representation * * @param enums a list of enumerations to convert * * @return an array of {@code String} with the values produced by each element's {@link Enum#toString()} method. */ @SuppressWarnings("rawtypes") public static String[] toArray(List enums) { String[] out = new String[enums.size()]; for (int i = 0; i < out.length; i++) { out[i] = enums.get(i).toString(); } return out; } /** * Converts any collection of {@code Integer} into an {@code int} array. * * @param ints a collection of (boxed) integers. * * @return a primitive {@code int} array with the unboxed integer values. */ public static int[] toIntArray(Collection ints) { int[] out = new int[ints.size()]; int i = 0; for (Integer boxed : ints) { out[i++] = boxed.intValue(); } return out; } /** * Converts any collection of {@code Character} into a char array. * * @param characters a collection of (boxed) characters. * * @return a primitive {@code char} array with the unboxed character values. */ public static char[] toCharArray(Collection characters) { char[] out = new char[characters.size()]; int i = 0; for (Character boxed : characters) { out[i++] = boxed.charValue(); } return out; } /** * Restricts the length of a given content. * * @param length the maximum length to be displayed. If {@code 0}, the {@code ""} string will be returned. * @param content the content whose length should be restricted. * * @return the restricted content. */ public static String restrictContent(int length, CharSequence content) { if (content == null) { return null; } if (length == 0) { return ""; } if (length == -1) { return content.toString(); } int errorMessageStart = content.length() - length; if (length > 0 && errorMessageStart > 0) { return "..." + content.subSequence(errorMessageStart, content.length()).toString(); } return content.toString(); } /** * Restricts the length of a given content. * * @param length the maximum length to be displayed. If {@code 0}, the {@code ""} string will be returned. * @param content the content whose length should be restricted. * * @return the restricted content. */ public static String restrictContent(int length, Object content) { if (content == null) { return null; } if (content instanceof Object[]) { return restrictContent(length, Arrays.toString((Object[]) content)); } return restrictContent(length, String.valueOf(content)); } /** * Allows rethrowing a checked exception instead of wrapping it into a runtime exception. For internal use only * as this generally causes more trouble than it solves (your exception-specific catch statement may not catch this * error - make sure you are catching a Throwable) * * @param error the (potentially checked) exception to the thrown. */ public static void throwUnchecked(Throwable error) { ArgumentUtils.throwsUnchecked(error); } private static void throwsUnchecked(Throwable toThrow) throws T { throw (T) toThrow; } /** * Converts a sequence of int numbers into a byte array. * * @param ints the integers to be cast to by * * @return the resulting byte array. */ public static byte[] toByteArray(int... ints) { byte[] out = new byte[ints.length]; for (int i = 0; i < ints.length; i++) { out[i] = (byte) ints[i]; } return out; } /** * Identifies duplicate values in a given array and returns them * * @param array the search array * @param the type of elements held in the given array. * * @return all duplicate values found in the given array, or empty array if no duplicates, or {@code null} if the input is {@code null}. */ public static T[] findDuplicates(T[] array) { if (array == null || array.length == 0) { return array; } Set elements = new HashSet(array.length); ArrayList duplicates = new ArrayList(1); for (T element : array) { if (!elements.contains(element)) { elements.add(element); } else { duplicates.add(element); } } return duplicates.toArray((T[]) newInstance(array.getClass().getComponentType(), duplicates.size())); } /** * Removes surrounding spaces from a given {@code String}, from its right or left side, or both. * * @param input the content to trim * @param left flag to indicate whether spaces on the left side of the string should be removed. * @param right flag to indicate whether spaces on the right side of the string should be removed. * * @return the trimmed string. */ public static String trim(String input, boolean left, boolean right) { if (input.length() == 0 || !left && !right) { return input; } int begin = 0; while (left && begin < input.length() && input.charAt(begin) <= ' ') { begin++; } if (begin == input.length()) { return ""; } int end = begin + input.length() - 1; if (end >= input.length()) { end = input.length() - 1; } while (right && input.charAt(end) <= ' ') { end--; } if (begin == end) { return ""; } if (begin == 0 && end == input.length() - 1) { return input; } return input.substring(begin, end + 1); } /** * Displays line separators in a string by replacing all instances * of `\r` and `\n` with `[cr]` and `[lf]`. * If `\r` is followed by `\n` or vice versa, then `[crlf]` or `[lfcr]` will be printed. * * @param str the string to have its line separators displayed * @param addNewLine flag indicating whether the original `\r` or `\n` characters should be kept in the string. * if {@code true}, `\r` will be replaced by `[cr]\r` for example. * * @return the updated string with any line separators replaced by visible character sequences. */ public static String displayLineSeparators(String str, boolean addNewLine) { StringBuilder out = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); if (ch == '\r' || ch == '\n') { out.append('['); out.append(ch == '\r' ? "cr" : "lf"); char next = '\0'; if (i + 1 < str.length()) { next = str.charAt(i + 1); if (next != ch && (next == '\r' || next == '\n')) { out.append(next == '\r' ? "cr" : "lf"); i++; } else { next = '\0'; } } out.append(']'); if (addNewLine) { out.append(ch); if (next != '\0') { out.append(next); } } } else { out.append(ch); } } return out.toString(); } /** * Removes all instances of a given element from an int array. * * @param array the array to be checked * @param e the element to be removed * * @return an updated array that does not contain the given element anywhere, * or the original array if the element has not been found. */ public static int[] removeAll(int[] array, int e) { if (array == null || array.length == 0) { return array; } int removeCount = 0; for (int i = 0; i < array.length; i++) { if (array[i] == e) { removeCount++; } } if (removeCount == 0) { return array; } int[] tmp = new int[array.length - removeCount]; for (int i = 0, j = 0; i < array.length; i++) { if (array[i] != e) { tmp[j++] = array[i]; } } return tmp; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ColumnMap.java000066400000000000000000000106501400120543400301740ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.fields.*; import java.util.*; /** * Class responsible for calculating and storing the position of fields parsed from the input. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public class ColumnMap { private Map columnMap; private int[] enumMap; private int[] extractedIndexes = null; private final Context context; private final ParserOutput output; public ColumnMap(Context context, ParserOutput output) { this.context = context; this.output = output; } /** * Returns the position of a header (0 based). * * @param header the header whose position will be returned * * @return the position of the given header, or -1 if it could not be found. */ public int indexOf(String header) { if (columnMap != null && columnMap.isEmpty()) { return -1; } validateHeader(header); NormalizedString normalizedHeader = NormalizedString.valueOf(header); if (columnMap == null) { NormalizedString[] headers = NormalizedString.toIdentifierGroupArray(context.headers()); if (headers == null) { columnMap = Collections.emptyMap(); return -1; } columnMap = new HashMap(headers.length); extractedIndexes = context.extractedFieldIndexes(); if (extractedIndexes != null) { if (context.columnsReordered()) { int[] selection = ArgumentUtils.removeAll(extractedIndexes, -1); for (int i = 0; i < selection.length; i++) { int originalIndex = selection[i]; NormalizedString h = headers[originalIndex]; columnMap.put(h, i); } } else { for (int i = 0; i < extractedIndexes.length && i < headers.length; i++) { columnMap.put(headers[i], i); } } } else { for (int i = 0; i < headers.length; i++) { columnMap.put(headers[i], i); } } } Integer index = columnMap.get(normalizedHeader); if (index == null) { return -1; } return index.intValue(); } private void validateHeader(Object header) { if (header == null) { if (context.headers() == null) { throw new IllegalArgumentException("Header name cannot be null."); } throw new IllegalArgumentException("Header name cannot be null. Use one of the available column names: " + Arrays.asList(context.headers())); } } /** * Returns the position of a header (0 based). * * @param header the header whose position will be returned * * @return the position of the given header, or -1 if it could not be found. */ public int indexOf(Enum header) { if (enumMap != null && enumMap.length == 0) { return -1; } validateHeader(header); if (enumMap == null) { NormalizedString[] headers = NormalizedString.toIdentifierGroupArray(context.headers()); if (headers == null) { enumMap = new int[0]; return -1; } Enum[] constants = header.getClass().getEnumConstants(); int lastOrdinal = Integer.MIN_VALUE; for (int i = 0; i < constants.length; i++) { if (lastOrdinal < constants[i].ordinal()) { lastOrdinal = constants[i].ordinal(); } } enumMap = new int[lastOrdinal + 1]; FieldSelector selector = output == null ? null : output.getFieldSelector(); if (!context.columnsReordered()) { selector = null; } for (int i = 0; i < constants.length; i++) { Enum constant = constants[i]; String name = constant.toString(); int index = ArgumentUtils.indexOf(headers, NormalizedString.valueOf(name), selector); enumMap[constant.ordinal()] = index; } } return enumMap[header.ordinal()]; } void reset() { columnMap = null; enumMap = null; extractedIndexes = null; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/CommonParserSettings.java000066400000000000000000000625701400120543400324370ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.input.*; import com.univocity.parsers.common.input.concurrent.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * This is the parent class for all configuration classes used by parsers ({@link AbstractParser}) * *

By default, all parsers work with, at least, the following configuration options in addition to the ones provided by {@link CommonSettings}: * *

    *
  • rowProcessor: a callback implementation of the interface {@link RowProcessor} which handles the life cycle of the parsing process and processes each record extracted from the input
  • *
  • headerExtractionEnabled (defaults to false): indicates whether or not the first valid record parsed from the input should be considered as the row containing the names of each column
  • *
  • columnReorderingEnabled (defaults to true): indicates whether fields selected using the field selection methods (defined by the parent class {@link CommonSettings}) should be reordered. *

    When disabled, each parsed record will contain values for all columns, in the order they occur in the input. Fields which were not selected will not be parsed but and the record will contain empty values. *

    When enabled, each parsed record will contain values only for the selected columns. The values will be ordered according to the selection. *

  • inputBufferSize (defaults to 1024*1024 characters): The number of characters held by the parser's buffer when processing the input. *
  • readInputOnSeparateThread (defaults true if the number of available processors at runtime is greater than 1): *

    When enabled, a reading thread (in {@code input.concurrent.ConcurrentCharInputReader}) will be started and load characters from the input, while the parser is processing its input buffer. * This yields better performance, especially when reading from big input (greater than 100 mb) *

    When disabled, the parsing process will briefly pause so the buffer can be replenished every time it is exhausted (in {@link DefaultCharInputReader} it is not as bad or slow as it sounds, and can even be (slightly) more efficient if your input is small) *

  • numberOfRecordsToRead (defaults to -1): Defines how many (valid) records are to be parsed before the process is stopped. A negative value indicates there's no limit.
  • *
  • lineSeparatorDetectionEnabled (defaults to false): Attempts to identify what is the line separator being used in the input. * The first row of the input will be read until a sequence of '\r\n', or characters '\r' or '\n' is found. If a match is found, then it will be used as the line separator to use to parse the input
  • *
* * @param the format supported by this parser. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.processor.RowProcessor * @see com.univocity.parsers.csv.CsvParserSettings * @see com.univocity.parsers.fixed.FixedWidthParserSettings */ public abstract class CommonParserSettings extends CommonSettings { protected Boolean headerExtractionEnabled = null; private Processor processor; private boolean columnReorderingEnabled = true; private int inputBufferSize = 1024 * 1024; private boolean readInputOnSeparateThread = Runtime.getRuntime().availableProcessors() > 1; private long numberOfRecordsToRead = -1L; private boolean lineSeparatorDetectionEnabled = false; private long numberOfRowsToSkip = 0L; private boolean commentCollectionEnabled = false; private boolean autoClosingEnabled = true; private boolean commentProcessingEnabled = true; private List inputAnalysisProcesses = new ArrayList(); /** * Indicates whether or not a separate thread will be used to read characters from the input while parsing (defaults true if the number of available * processors at runtime is greater than 1) *

When enabled, a reading thread (in {@code com.univocity.parsers.common.input.concurrent.ConcurrentCharInputReader}) * will be started and load characters from the input, while the parser is processing its input buffer. * This yields better performance, especially when reading from big input (greater than 100 mb) *

When disabled, the parsing process will briefly pause so the buffer can be replenished every time it is exhausted * (in {@link DefaultCharInputReader} it is not as bad or slow as it sounds, and can even be (slightly) more efficient if your input is small) * * @return true if the input should be read on a separate thread, false otherwise */ public boolean getReadInputOnSeparateThread() { return readInputOnSeparateThread; } /** * Defines whether or not a separate thread will be used to read characters from the input while parsing (defaults true if the number of available * processors at runtime is greater than 1) *

When enabled, a reading thread (in {@code com.univocity.parsers.common.input.concurrent.ConcurrentCharInputReader}) will be * started and load characters from the input, while the * parser is processing its input buffer. This yields better performance, especially when reading from big input (greater than 100 mb) *

When disabled, the parsing process will briefly pause so the buffer can be replenished every time it is exhausted (in {@link DefaultCharInputReader} * it is not as bad or slow as it sounds, and can even be (slightly) more efficient if your input is small) * * @param readInputOnSeparateThread the flag indicating whether or not the input should be read on a separate thread */ public void setReadInputOnSeparateThread(boolean readInputOnSeparateThread) { this.readInputOnSeparateThread = readInputOnSeparateThread; } /** * Indicates whether or not the first valid record parsed from the input should be considered as the row containing the names of each column * * @return true if the first valid record parsed from the input should be considered as the row containing the names of each column, false otherwise */ public boolean isHeaderExtractionEnabled() { return headerExtractionEnabled == null ? false : headerExtractionEnabled; } /** * Defines whether or not the first valid record parsed from the input should be considered as the row containing the names of each column * * @param headerExtractionEnabled a flag indicating whether the first valid record parsed from the input should be considered as the row containing the * names of each column */ public void setHeaderExtractionEnabled(boolean headerExtractionEnabled) { this.headerExtractionEnabled = headerExtractionEnabled; } /** * Returns the callback implementation of the interface {@link RowProcessor} which handles the lifecycle of the parsing process and processes each record * extracted from the input * * @return Returns the RowProcessor used by the parser to handle each record * * @see com.univocity.parsers.common.processor.ObjectRowProcessor * @see com.univocity.parsers.common.processor.ObjectRowListProcessor * @see com.univocity.parsers.common.processor.MasterDetailProcessor * @see com.univocity.parsers.common.processor.MasterDetailListProcessor * @see com.univocity.parsers.common.processor.BeanProcessor * @see com.univocity.parsers.common.processor.BeanListProcessor * @deprecated Use the {@link #getProcessor()} method as it allows format-specific processors to be built to work with different implementations of {@link Context}. * Implementations based on {@link RowProcessor} allow only parsers who provide a {@link ParsingContext} to be used. */ @Deprecated public RowProcessor getRowProcessor() { if (processor == null) { return NoopRowProcessor.instance; } return (RowProcessor) processor; } /** * Defines the callback implementation of the interface {@link RowProcessor} which handles the lifecycle of the parsing process and processes each record * extracted from the input * * @param processor the RowProcessor instance which should used by the parser to handle each record * * @see com.univocity.parsers.common.processor.ObjectRowProcessor * @see com.univocity.parsers.common.processor.ObjectRowListProcessor * @see com.univocity.parsers.common.processor.MasterDetailProcessor * @see com.univocity.parsers.common.processor.MasterDetailListProcessor * @see com.univocity.parsers.common.processor.BeanProcessor * @see com.univocity.parsers.common.processor.BeanListProcessor * @deprecated Use the {@link #setProcessor(Processor)} method as it allows format-specific processors to be built to work with different implementations of * {@link Context}. * Implementations based on {@link RowProcessor} allow only parsers who provide a {@link ParsingContext} to be used. */ @Deprecated public void setRowProcessor(RowProcessor processor) { this.processor = processor; } /** * Returns the callback implementation of the interface {@link Processor} which handles the lifecycle of the parsing process and processes each record * extracted from the input * * @param the context type supported by the parser implementation. * * @return Returns the {@link Processor} used by the parser to handle each record * * @see AbstractObjectProcessor * @see AbstractObjectListProcessor * @see AbstractMasterDetailProcessor * @see AbstractMasterDetailListProcessor * @see AbstractBeanProcessor * @see AbstractBeanListProcessor */ public Processor getProcessor() { if (processor == null) { return NoopProcessor.instance; } return (Processor) processor; } /** * Defines the callback implementation of the interface {@link Processor} which handles the lifecycle of the parsing process and processes each record * extracted from the input * * @param processor the {@link Processor} instance which should used by the parser to handle each record * * @see AbstractObjectProcessor * @see AbstractObjectListProcessor * @see AbstractMasterDetailProcessor * @see AbstractMasterDetailListProcessor * @see AbstractBeanProcessor * @see AbstractBeanListProcessor * @see AbstractColumnProcessor * @see AbstractColumnProcessor */ public void setProcessor(Processor processor) { this.processor = processor; } /** * An implementation of {@link CharInputReader} which loads the parser buffer in parallel or sequentially, as defined by the readInputOnSeparateThread * property * * @param whitespaceRangeStart starting range of characters considered to be whitespace. * * @return The input reader as chosen with the readInputOnSeparateThread property. */ protected CharInputReader newCharInputReader(int whitespaceRangeStart) { if (readInputOnSeparateThread) { if (lineSeparatorDetectionEnabled) { return new ConcurrentCharInputReader(getFormat().getNormalizedNewline(), this.getInputBufferSize(), 10, whitespaceRangeStart, autoClosingEnabled); } else { return new ConcurrentCharInputReader(getFormat().getLineSeparator(), getFormat().getNormalizedNewline(), this.getInputBufferSize(), 10, whitespaceRangeStart, autoClosingEnabled); } } else { if (lineSeparatorDetectionEnabled) { return new DefaultCharInputReader(getFormat().getNormalizedNewline(), this.getInputBufferSize(), whitespaceRangeStart, autoClosingEnabled); } else { return new DefaultCharInputReader(getFormat().getLineSeparator(), getFormat().getNormalizedNewline(), this.getInputBufferSize(), whitespaceRangeStart, autoClosingEnabled); } } } /** * The number of valid records to be parsed before the process is stopped. A negative value indicates there's no limit (defaults to -1). * * @return the number of records to read before stopping the parsing process. */ public long getNumberOfRecordsToRead() { return numberOfRecordsToRead; } /** * Defines the number of valid records to be parsed before the process is stopped. A negative value indicates there's no limit (defaults to -1). * * @param numberOfRecordsToRead the number of records to read before stopping the parsing process. */ public void setNumberOfRecordsToRead(long numberOfRecordsToRead) { this.numberOfRecordsToRead = numberOfRecordsToRead; } /** * Indicates whether fields selected using the field selection methods (defined by the parent class {@link CommonSettings}) should be reordered (defaults to * true). *

When disabled, each parsed record will contain values for all columns, in the order they occur in the input. Fields which were not selected will not * be parsed but and the record will contain empty values. *

When enabled, each parsed record will contain values only for the selected columns. The values will be ordered according to the selection. * * @return true if the selected fields should be reordered and returned by the parser, false otherwise */ public boolean isColumnReorderingEnabled() { return !preventReordering() && columnReorderingEnabled; } /** * Returns the set of selected fields, if any * * @return the set of selected fields. Null if no field was selected/excluded */ @Override FieldSet getFieldSet() { return preventReordering() ? null : super.getFieldSet(); } /** * Returns the FieldSelector object, which handles selected fields. * * @return the FieldSelector object, which handles selected fields. Null if no field was selected/excluded */ @Override FieldSelector getFieldSelector() { return preventReordering() ? null : super.getFieldSelector(); } /** * Defines whether fields selected using the field selection methods (defined by the parent class {@link CommonSettings}) should be reordered (defaults to * true). *

When disabled, each parsed record will contain values for all columns, in the order they occur in the input. Fields which were not selected will not * be parsed but the record will contain empty values. *

When enabled, each parsed record will contain values only for the selected columns. The values will be ordered according to the selection. * * @param columnReorderingEnabled the flag indicating whether or not selected fields should be reordered and returned by the parser */ public void setColumnReorderingEnabled(boolean columnReorderingEnabled) { if (columnReorderingEnabled && preventReordering()) { throw new IllegalArgumentException("Cannot reorder columns when using a row processor that manipulates nested rows."); } this.columnReorderingEnabled = columnReorderingEnabled; } /** * Informs the number of characters held by the parser's buffer when processing the input (defaults to 1024*1024 characters). * * @return the number of characters held by the parser's buffer when processing the input */ public int getInputBufferSize() { return inputBufferSize; } /** * Defines the number of characters held by the parser's buffer when processing the input (defaults to 1024*1024 characters). * * @param inputBufferSize the new input buffer size (in number of characters) */ public void setInputBufferSize(int inputBufferSize) { this.inputBufferSize = inputBufferSize; } /** * Returns an instance of CharAppender with the configured limit of maximum characters per column and the default value used to represent a null value (when * the String parsed from the input is empty) * * @return an instance of CharAppender with the configured limit of maximum characters per column and the default value used to represent a null value (when * the String parsed from the input is empty) */ protected CharAppender newCharAppender() { int chars = getMaxCharsPerColumn(); if (chars != -1) { return new DefaultCharAppender(chars, getNullValue(), getWhitespaceRangeStart()); } else { return new ExpandingCharAppender(getNullValue(), getWhitespaceRangeStart()); } } /** * Indicates whether the parser should detect the line separator automatically. * * @return {@code true} if the first line of the input should be used to search for common line separator sequences (the matching sequence will be used as * the line separator for parsing). Otherwise {@code false}. */ public final boolean isLineSeparatorDetectionEnabled() { return lineSeparatorDetectionEnabled; } /** * Defines whether the parser should detect the line separator automatically. * * @param lineSeparatorDetectionEnabled a flag indicating whether the first line of the input should be used to search for common line separator sequences * (the matching sequence will be used as the line separator for parsing). */ public final void setLineSeparatorDetectionEnabled(boolean lineSeparatorDetectionEnabled) { this.lineSeparatorDetectionEnabled = lineSeparatorDetectionEnabled; } /** * Returns the number of rows to skip from the input before the parser can begin to execute. * * @return number of rows to skip before parsing */ public final long getNumberOfRowsToSkip() { return numberOfRowsToSkip; } /** * Defines a number of rows to skip from the input before the parser can begin to execute. * * @param numberOfRowsToSkip number of rows to skip before parsing */ public final void setNumberOfRowsToSkip(long numberOfRowsToSkip) { if (numberOfRowsToSkip < 0) { throw new IllegalArgumentException("Number of rows to skip from the input must be 0 or greater"); } this.numberOfRowsToSkip = numberOfRowsToSkip; } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); out.put("Header extraction enabled", headerExtractionEnabled); out.put("Processor", processor == null ? "none" : processor.getClass().getName()); out.put("Column reordering enabled", columnReorderingEnabled); out.put("Input buffer size", inputBufferSize); out.put("Input reading on separate thread", readInputOnSeparateThread); out.put("Number of records to read", numberOfRecordsToRead == -1 ? "all" : numberOfRecordsToRead); out.put("Line separator detection enabled", lineSeparatorDetectionEnabled); out.put("Auto-closing enabled", autoClosingEnabled); } private boolean preventReordering() { if (processor instanceof ColumnOrderDependent) { return ((ColumnOrderDependent) processor).preventColumnReordering(); } return false; } /** * Indicates that comments found in the input must be collected (disabled by default). If enabled, comment lines will be * stored by the parser and made available via {@code AbstractParser.getContext().comments()} and {@code AbstractParser.getContext().lastComment()} * * @return a flag indicating whether or not to enable collection of comments. */ public boolean isCommentCollectionEnabled() { return commentCollectionEnabled; } /** * Enables collection of comments found in the input (disabled by default). If enabled, comment lines will be * stored by the parser and made available via {@code AbstractParser.getContext().comments()} and {@code AbstractParser.getContext().lastComment()} * * @param commentCollectionEnabled flag indicating whether or not to enable collection of comments. */ public void setCommentCollectionEnabled(boolean commentCollectionEnabled) { this.commentCollectionEnabled = commentCollectionEnabled; } @Override final void runAutomaticConfiguration() { Class beanClass = null; if (processor instanceof AbstractBeanProcessor) { beanClass = ((AbstractBeanProcessor) processor).getBeanClass(); } else if (processor instanceof AbstractMultiBeanProcessor) { Class[] classes = ((AbstractMultiBeanProcessor) processor).getBeanClasses(); if (classes.length > 0) { beanClass = classes[0]; } } if (beanClass != null) { configureFromAnnotations(beanClass); } } /** * Configures the parser based on the annotations provided in a given class * * @param beanClass the classes whose annotations will be processed to derive configurations for parsing */ protected synchronized void configureFromAnnotations(Class beanClass) { if (!deriveHeadersFrom(beanClass)) { return; } Headers headerAnnotation = AnnotationHelper.findHeadersAnnotation(beanClass); String[] headersFromBean = ArgumentUtils.EMPTY_STRING_ARRAY; boolean allFieldsIndexBased = AnnotationHelper.allFieldsIndexBasedForParsing(beanClass); boolean extractHeaders = !allFieldsIndexBased; if (headerAnnotation != null) { if (headerAnnotation.sequence().length > 0) { headersFromBean = headerAnnotation.sequence(); } extractHeaders = headerAnnotation.extract(); } if (headerExtractionEnabled == null) { setHeaderExtractionEnabled(extractHeaders); } if (getHeaders() == null && headersFromBean.length > 0 && !headerExtractionEnabled) { setHeadersDerivedFromClass(beanClass, headersFromBean); } if (getFieldSet() == null) { if (allFieldsIndexBased) { selectIndexes(AnnotationHelper.getSelectedIndexes(beanClass, MethodFilter.ONLY_SETTERS)); } else if (headersFromBean.length > 0 && AnnotationHelper.allFieldsNameBasedForParsing(beanClass)) { selectFields(headersFromBean); } } } @Override protected CommonParserSettings clone(boolean clearInputSpecificSettings) { return (CommonParserSettings) super.clone(clearInputSpecificSettings); } @Override protected CommonParserSettings clone() { return (CommonParserSettings) super.clone(); } @Override protected void clearInputSpecificSettings() { super.clearInputSpecificSettings(); processor = null; numberOfRecordsToRead = -1L; numberOfRowsToSkip = 0L; } /** * Indicates whether automatic closing of the input (reader, stream, etc) * is enabled. If {@code true}, the parser will always close the input automatically * when all records have been parsed or when an error occurs. * * Defaults to {@code true} * * @return flag indicating whether automatic input closing is enabled. */ public boolean isAutoClosingEnabled() { return autoClosingEnabled; } /** * Configures whether the parser should always close the input (reader, stream, etc) automatically * when all records have been parsed or when an error occurs. * * Defaults to {@code true} * * @param autoClosingEnabled flag determining whether automatic input closing should be enabled. */ public void setAutoClosingEnabled(boolean autoClosingEnabled) { this.autoClosingEnabled = autoClosingEnabled; } /** * Indicates whether code will check for comment line in the data file * is enabled. If {@code true}, the parser will always check for the comment line * default value for comment check is # * Defaults to {@code true} * * @return flag indicating whether parser will check for the comment line * If disabled/false then parser wont treat any line as comment line including default(#) * this condition will supersede the comment character(#) */ public boolean isCommentProcessingEnabled() { return commentProcessingEnabled; } /** * Configures whether the parser will check for the comment line in the file * Defaults to {@code true} * * @param commentProcessingEnabled flag determining whether comment line check should be performed * If disabled/false then parser wont treat any line as comment line including default(#) * this condition will supersede the comment character(#) */ public void setCommentProcessingEnabled(boolean commentProcessingEnabled) { this.commentProcessingEnabled = commentProcessingEnabled; } /** * Provides a custom {@link InputAnalysisProcess} to analyze the input buffer and potentially discover configuration options such as * column separators is CSV, data formats, etc. The process will be execute only once. * * @param inputAnalysisProcess a custom process to analyze the contents of the first input buffer loaded when the parsing starts. */ public void addInputAnalysisProcess(InputAnalysisProcess inputAnalysisProcess) { if (inputAnalysisProcess == null) { return; } if (this.inputAnalysisProcesses == null) { inputAnalysisProcesses = new ArrayList(); } inputAnalysisProcesses.add(inputAnalysisProcess); } /** * Returns the sequence of {@link InputAnalysisProcess} to be used for analyzing the input buffer and potentially discover configuration options such as * column separators is CSV, data formats, etc. Each process will be execute only once. * * @return the list of custom processes to analyze the contents of the first input buffer loaded when the parsing starts. */ public List getInputAnalysisProcesses() { return inputAnalysisProcesses; } }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/CommonSettings.java000066400000000000000000001011601400120543400312470ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.annotations.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.processor.*; import java.util.*; import java.util.Map.*; /** * This is the parent class for all configuration classes used by parsers ({@link AbstractParser}) and writers ({@link AbstractWriter}) * *

By default, all parsers and writers work with, at least, the following configuration options: * *

    *
  • format (each file format provides its default): the input/output format of a given file
  • *
  • nullValue (defaults to null): *

    when reading, if the parser does not read any character from the input, the nullValue is used instead of an empty string *

    when writing, if the writer has a null object to write to the output, the nullValue is used instead of an empty string

  • *
  • maxCharsPerColumn (defaults to 4096): The maximum number of characters allowed for any given value being written/read. *

    You need this to avoid OutOfMemoryErrors in case a file does not have a valid format. In such cases the parser might just keep reading from the input * until its end or the memory is exhausted. This sets a limit which avoids unwanted JVM crashes.

  • *
  • maxColumns (defaults to 512): a hard limit on how many columns a record can have. * You need this to avoid OutOfMemory errors in case of inputs that might be inconsistent with the format you are dealing with
  • *
  • skipEmptyLines (defaults to true): *

    when reading, if the parser reads a line that is empty, it will be skipped. *

    when writing, if the writer receives an empty or null row to write to the output, it will be ignored

  • *
  • ignoreTrailingWhitespaces (defaults to true): removes trailing whitespaces from values being read/written
  • *
  • ignoreLeadingWhitespaces (defaults to true): removes leading whitespaces from values being read/written
  • *
  • headers (defaults to null): the field names in the input/output, in the sequence they occur. *

    when reading, the given header names will be used to refer to each column irrespective of whether or not the input contains a header row *

    when writing, the given header names will be used to refer to each column and can be used for writing the header row

  • *
  • field selection (defaults to none): a selection of fields for reading and writing. Fields can be selected by their name or their position. *

    when reading, the selected fields only will be parsed and the remaining fields will be discarded. *

    when writing, the selected fields only will be written and the remaining fields will be discarded

  • *
* * @param the format supported by this settings class. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.CommonParserSettings * @see com.univocity.parsers.common.CommonWriterSettings * @see com.univocity.parsers.csv.CsvParserSettings * @see com.univocity.parsers.csv.CsvWriterSettings * @see com.univocity.parsers.fixed.FixedWidthParserSettings * @see com.univocity.parsers.fixed.FixedWidthWriterSettings */ public abstract class CommonSettings implements Cloneable { private F format; private String nullValue = null; private int maxCharsPerColumn = 4096; private int maxColumns = 512; private boolean skipEmptyLines = true; private boolean ignoreTrailingWhitespaces = true; private boolean ignoreLeadingWhitespaces = true; private FieldSelector fieldSelector = null; private boolean autoConfigurationEnabled = true; private ProcessorErrorHandler errorHandler; private int errorContentLength = -1; private boolean skipBitsAsWhitespace = true; private String[] headers; Class headerSourceClass; /** * Creates a new instance of this settings object using the default format specified by the concrete class that inherits from {@code CommonSettings} */ public CommonSettings() { setFormat(createDefaultFormat()); } /** * Returns the String representation of a null value (defaults to null) *

When reading, if the parser does not read any character from the input, the nullValue is used instead of an empty string *

When writing, if the writer has a null object to write to the output, the nullValue is used instead of an empty string * * @return the String representation of a null value */ public String getNullValue() { return nullValue; } /** * Sets the String representation of a null value (defaults to null) *

When reading, if the parser does not read any character from the input, the nullValue is used instead of an empty string *

When writing, if the writer has a null object to write to the output, the nullValue is used instead of an empty string * * @param emptyValue the String representation of a null value */ public void setNullValue(String emptyValue) { this.nullValue = emptyValue; } /** * The maximum number of characters allowed for any given value being written/read. Used to avoid OutOfMemoryErrors (defaults to 4096). * *

If set to {@code -1}, then the internal internal array will expand automatically, up to the limit allowed by the JVM

* * @return The maximum number of characters allowed for any given value being written/read */ public int getMaxCharsPerColumn() { return maxCharsPerColumn; } /** * Defines the maximum number of characters allowed for any given value being written/read. Used to avoid OutOfMemoryErrors (defaults to 4096). * *

To enable auto-expansion of the internal array, set this property to -1

* * @param maxCharsPerColumn The maximum number of characters allowed for any given value being written/read */ public void setMaxCharsPerColumn(int maxCharsPerColumn) { this.maxCharsPerColumn = maxCharsPerColumn; } /** * Returns whether or not empty lines should be ignored (defaults to true) *

when reading, if the parser reads a line that is empty, it will be skipped. *

when writing, if the writer receives an empty or null row to write to the output, it will be ignored * * @return true if empty lines are configured to be ignored, false otherwise */ public boolean getSkipEmptyLines() { return skipEmptyLines; } /** * Defines whether or not empty lines should be ignored (defaults to true) *

when reading, if the parser reads a line that is empty, it will be skipped. *

when writing, if the writer receives an empty or null row to write to the output, it will be ignored * * @param skipEmptyLines true if empty lines should be ignored, false otherwise */ public void setSkipEmptyLines(boolean skipEmptyLines) { this.skipEmptyLines = skipEmptyLines; } /** * Returns whether or not trailing whitespaces from values being read/written should be skipped (defaults to true) * * @return true if trailing whitespaces from values being read/written should be skipped, false otherwise */ public boolean getIgnoreTrailingWhitespaces() { return ignoreTrailingWhitespaces; } /** * Defines whether or not trailing whitespaces from values being read/written should be skipped (defaults to true) * * @param ignoreTrailingWhitespaces true if trailing whitespaces from values being read/written should be skipped, false otherwise */ public void setIgnoreTrailingWhitespaces(boolean ignoreTrailingWhitespaces) { this.ignoreTrailingWhitespaces = ignoreTrailingWhitespaces; } /** * Returns whether or not leading whitespaces from values being read/written should be skipped (defaults to true) * * @return true if leading whitespaces from values being read/written should be skipped, false otherwise */ public boolean getIgnoreLeadingWhitespaces() { return ignoreLeadingWhitespaces; } /** * Defines whether or not leading whitespaces from values being read/written should be skipped (defaults to true) * * @param ignoreLeadingWhitespaces true if leading whitespaces from values being read/written should be skipped, false otherwise */ public void setIgnoreLeadingWhitespaces(boolean ignoreLeadingWhitespaces) { this.ignoreLeadingWhitespaces = ignoreLeadingWhitespaces; } /** * Defines the field names in the input/output, in the sequence they occur (defaults to null). *

when reading, the given header names will be used to refer to each column irrespective of whether or not the input contains a header row *

when writing, the given header names will be used to refer to each column and can be used for writing the header row * * @param headers the field name sequence associated with each column in the input/output. */ public void setHeaders(String... headers) { if (headers == null || headers.length == 0) { this.headers = null; } else { this.headers = headers; } } /** * Defines the field names in the input/output derived from a given class with {@link Parsed} annotated attributes/methods. *

when reading, the given header names will be used to refer to each column irrespective of whether or not the input contains a header row *

when writing, the given header names will be used to refer to each column and can be used for writing the header row * * @param headerSourceClass the class from which the headers have been derived. * @param headers the field name sequence associated with each column in the input/output. */ void setHeadersDerivedFromClass(Class headerSourceClass, String... headers) { this.headerSourceClass = headerSourceClass; setHeaders(headers); } /** * Indicates whether headers should be derived from a given class. * * @param beanClass the class to derive headers from * * @return {@code true} if the headers used for parsing/writing should be derived from the given class; otherwise {@code false} */ boolean deriveHeadersFrom(Class beanClass) { if (headerSourceClass != null) { if (headerSourceClass != beanClass) { setHeaders(null); } else { return false; } } return true; } /** * Returns the field names in the input/output, in the sequence they occur (defaults to null). *

when reading, the given header names will be used to refer to each column irrespective of whether or not the input contains a header row *

when writing, the given header names will be used to refer to each column and can be used for writing the header row * * @return the field name sequence associated with each column in the input/output. */ public String[] getHeaders() { return this.headers; } /** * Returns the hard limit of how many columns a record can have (defaults to 512). * You need this to avoid OutOfMemory errors in case of inputs that might be inconsistent with the format you are dealing with . * * @return The maximum number of columns a record can have. */ public int getMaxColumns() { return maxColumns; } /** * Defines a hard limit of how many columns a record can have (defaults to 512). * You need this to avoid OutOfMemory errors in case of inputs that might be inconsistent with the format you are dealing with. * * @param maxColumns The maximum number of columns a record can have. */ public void setMaxColumns(int maxColumns) { this.maxColumns = maxColumns; } /** * The format of the file to be parsed/written (returns the format's defaults). * * @return The format of the file to be parsed/written */ public F getFormat() { return format; } /** * Defines the format of the file to be parsed/written (returns the format's defaults). * * @param format The format of the file to be parsed/written */ public void setFormat(F format) { if (format == null) { throw new IllegalArgumentException("Format cannot be null"); } this.format = format; } /** * Selects a sequence of fields for reading/writing by their names. * *

When reading, only the values of the selected columns will be parsed, and the content of the other columns ignored. * The resulting rows will be returned with the selected columns only, in the order specified. If you want to * obtain the original row format, with all columns included and nulls in the fields that have not been selected, * set {@link CommonParserSettings#setColumnReorderingEnabled(boolean)} with {@code false}.

* *

When writing, the sequence provided represents the expected format of the input rows. For example, * headers can be "H1,H2,H3", but the input data is coming with values for two columns and in a different order, * such as "V_H3, V_H1". Selecting fields "H3" and "H1" will allow the writer to write values in the expected * locations. Using the given example, the output row will be generated as: "V_H1,null,V_H3"

* * @param fieldNames The field names to read/write * * @return the (modifiable) set of selected fields */ public FieldSet selectFields(String... fieldNames) { return setFieldSet(new FieldNameSelector(), fieldNames); } /** * Selects fields which will not be read/written, by their names * *

When reading, only the values of the selected columns will be parsed, and the content of the other columns ignored. * The resulting rows will be returned with the selected columns only, in the order specified. If you want to * obtain the original row format, with all columns included and nulls in the fields that have not been selected, * set {@link CommonParserSettings#setColumnReorderingEnabled(boolean)} with {@code false}.

* *

When writing, the sequence of non-excluded fields represents the expected format of the input rows. For example, * headers can be "H1,H2,H3", but the input data is coming with values for two columns and in a different order, * such as "V_H3, V_H1". Selecting fields "H3" and "H1" will allow the writer to write values in the expected * locations. Using the given example, the output row will be generated as: "V_H1,null,V_H3"

* * @param fieldNames The field names to exclude from the parsing/writing process * * @return the (modifiable) set of ignored fields */ public FieldSet excludeFields(String... fieldNames) { return setFieldSet(new ExcludeFieldNameSelector(), fieldNames); } /** * Selects a sequence of fields for reading/writing by their positions. * *

When reading, only the values of the selected columns will be parsed, and the content of the other columns ignored. * The resulting rows will be returned with the selected columns only, in the order specified. If you want to * obtain the original row format, with all columns included and nulls in the fields that have not been selected, * set {@link CommonParserSettings#setColumnReorderingEnabled(boolean)} with {@code false}.

* *

When writing, the sequence provided represents the expected format of the input rows. For example, * headers can be "H1,H2,H3", but the input data is coming with values for two columns and in a different order, * such as "V_H3, V_H1". Selecting indexes "2" and "0" will allow the writer to write values in the expected * locations. Using the given example, the output row will be generated as: "V_H1,null,V_H3"

* * @param fieldIndexes The indexes to read/write * * @return the (modifiable) set of selected fields */ public FieldSet selectIndexes(Integer... fieldIndexes) { return setFieldSet(new FieldIndexSelector(), fieldIndexes); } /** * Selects columns which will not be read/written, by their positions * *

When reading, only the values of the selected columns will be parsed, and the content of the other columns ignored. * The resulting rows will be returned with the selected columns only, in the order specified. If you want to * obtain the original row format, with all columns included and nulls in the fields that have not been selected, * set {@link CommonParserSettings#setColumnReorderingEnabled(boolean)} with {@code false}.

* *

When writing, the sequence of non-excluded fields represents the expected format of the input rows. For example, * headers can be "H1,H2,H3", but the input data is coming with values for two columns and in a different order, * such as "V_H3, V_H1". Selecting fields by index, such as "2" and "0" will allow the writer to write values in the expected * locations. Using the given example, the output row will be generated as: "V_H1,null,V_H3"

* * @param fieldIndexes indexes of columns to exclude from the parsing/writing process * * @return the (modifiable) set of ignored fields */ public FieldSet excludeIndexes(Integer... fieldIndexes) { return setFieldSet(new ExcludeFieldIndexSelector(), fieldIndexes); } /** * Selects a sequence of fields for reading/writing by their names * * *

When reading, only the values of the selected columns will be parsed, and the content of the other columns ignored. * The resulting rows will be returned with the selected columns only, in the order specified. If you want to * obtain the original row format, with all columns included and nulls in the fields that have not been selected, * set {@link CommonParserSettings#setColumnReorderingEnabled(boolean)} with {@code false}.

* *

When writing, the sequence provided represents the expected format of the input rows. For example, * headers can be "H1,H2,H3", but the input data is coming with values for two columns and in a different order, * such as "V_H3, V_H1". Selecting fields "H3" and "H1" will allow the writer to write values in the expected * locations. Using the given example, the output row will be generated as: "V_H1,null,V_H3"

* * @param columns The columns to read/write * * @return the (modifiable) set of selected fields */ @SuppressWarnings("rawtypes") public FieldSet selectFields(Enum... columns) { return setFieldSet(new FieldEnumSelector(), columns); } /** * Selects columns which will not be read/written, by their names * *

When reading, only the values of the selected columns will be parsed, and the content of the other columns ignored. * The resulting rows will be returned with the selected columns only, in the order specified. If you want to * obtain the original row format, with all columns included and nulls in the fields that have not been selected, * set {@link CommonParserSettings#setColumnReorderingEnabled(boolean)} with {@code false}.

* *

When writing, the sequence of non-excluded fields represents the expected format of the input rows. For example, * headers can be "H1,H2,H3", but the input data is coming with values for two columns and in a different order, * such as "V_H3, V_H1". Selecting fields "H3" and "H1" will allow the writer to write values in the expected * locations. Using the given example, the output row will be generated as: "V_H1,null,V_H3"

* * @param columns The columns to exclude from the parsing/writing process * * @return the (modifiable) set of ignored fields */ @SuppressWarnings("rawtypes") public FieldSet excludeFields(Enum... columns) { return setFieldSet(new ExcludeFieldEnumSelector(), columns); } /** * Replaces the current field selection * * @param fieldSet the new set of selected fields * @param values the values to include to the selection * * @return the set of selected fields given in as a parameter. */ private FieldSet setFieldSet(FieldSet fieldSet, T... values) { this.fieldSelector = (FieldSelector) fieldSet; fieldSet.add(values); return fieldSet; } /** * Returns the set of selected fields, if any * * @return the set of selected fields. Null if no field was selected/excluded */ FieldSet getFieldSet() { return (FieldSet) fieldSelector; } /** * Returns the FieldSelector object, which handles selected fields. * * @return the FieldSelector object, which handles selected fields. Null if no field was selected/excluded */ FieldSelector getFieldSelector() { return this.fieldSelector; } /** * Indicates whether this settings object can automatically derive configuration options. This is used, for example, to define the headers when the user * provides a {@link BeanWriterProcessor} where the bean class contains a {@link Headers} annotation, or to enable header extraction when the bean class of a * {@link BeanProcessor} has attributes mapping to header names. * *

Defaults to {@code true}

* * @return {@code true} if the automatic configuration feature is enabled, false otherwise */ public final boolean isAutoConfigurationEnabled() { return autoConfigurationEnabled; } /** * Indicates whether this settings object can automatically derive configuration options. This is used, for example, to define the headers when the user * provides a {@link BeanWriterProcessor} where the bean class contains a {@link Headers} annotation, or to enable header extraction when the bean class of a * {@link BeanProcessor} has attributes mapping to header names. * * @param autoConfigurationEnabled a flag to turn the automatic configuration feature on/off. */ public final void setAutoConfigurationEnabled(boolean autoConfigurationEnabled) { this.autoConfigurationEnabled = autoConfigurationEnabled; } /** * Returns the custom error handler to be used to capture and handle errors that might happen while processing records with a {@link RowProcessor} * or a {@link RowWriterProcessor} (i.e. non-fatal {@link DataProcessingException}s). * *

The parsing/writing process won't stop (unless the error handler rethrows the {@link DataProcessingException} or manually stops the process).

* * @return the callback error handler with custom code to manage occurrences of {@link DataProcessingException}. * * @deprecated Use the {@link #getProcessorErrorHandler()} method as it allows format-specific error handlers to be built to work with different implementations of {@link Context}. * Implementations based on {@link RowProcessorErrorHandler} allow only parsers who provide a {@link ParsingContext} to be used. */ @Deprecated public RowProcessorErrorHandler getRowProcessorErrorHandler() { return errorHandler == null ? NoopRowProcessorErrorHandler.instance : (RowProcessorErrorHandler) errorHandler; } /** * Defines a custom error handler to capture and handle errors that might happen while processing records with a {@link RowProcessor} * or a {@link RowWriterProcessor} (i.e. non-fatal {@link DataProcessingException}s). * *

The parsing parsing/writing won't stop (unless the error handler rethrows the {@link DataProcessingException} or manually stops the process).

* * @param rowProcessorErrorHandler the callback error handler with custom code to manage occurrences of {@link DataProcessingException}. * * @deprecated Use the {@link #setProcessorErrorHandler(ProcessorErrorHandler)} method as it allows format-specific error handlers to be built to work with different implementations of {@link Context}. * Implementations based on {@link RowProcessorErrorHandler} allow only parsers who provide a {@link ParsingContext} to be used. */ @Deprecated public void setRowProcessorErrorHandler(RowProcessorErrorHandler rowProcessorErrorHandler) { this.errorHandler = rowProcessorErrorHandler; } /** * Returns the custom error handler to be used to capture and handle errors that might happen while processing records with a {@link com.univocity.parsers.common.processor.core.Processor} * or a {@link RowWriterProcessor} (i.e. non-fatal {@link DataProcessingException}s). * *

The parsing/writing process won't stop (unless the error handler rethrows the {@link DataProcessingException} or manually stops the process).

* * @param the {@code Context} type provided by the parser implementation. * * @return the callback error handler with custom code to manage occurrences of {@link DataProcessingException}. */ public ProcessorErrorHandler getProcessorErrorHandler() { return errorHandler == null ? NoopProcessorErrorHandler.instance : (ProcessorErrorHandler) errorHandler; } /** * Defines a custom error handler to capture and handle errors that might happen while processing records with a {@link com.univocity.parsers.common.processor.core.Processor} * or a {@link RowWriterProcessor} (i.e. non-fatal {@link DataProcessingException}s). * *

The parsing parsing/writing won't stop (unless the error handler rethrows the {@link DataProcessingException} or manually stops the process).

* * @param processorErrorHandler the callback error handler with custom code to manage occurrences of {@link DataProcessingException}. */ public void setProcessorErrorHandler(ProcessorErrorHandler processorErrorHandler) { this.errorHandler = processorErrorHandler; } /** * Returns a flag indicating whether or not a {@link ProcessorErrorHandler} has been defined through the use of method {@link #setProcessorErrorHandler(ProcessorErrorHandler)} * * @return {@code true} if the parser/writer is configured to use a {@link ProcessorErrorHandler} */ public boolean isProcessorErrorHandlerDefined() { return errorHandler != null; } /** * Extending classes must implement this method to return the default format settings for their parser/writer * * @return Default format configuration for the given parser/writer settings. */ protected abstract F createDefaultFormat(); final void autoConfigure() { if (!this.autoConfigurationEnabled) { return; } runAutomaticConfiguration(); } /** * Configures the parser/writer to trim or keep leading and trailing whitespaces around values * This has the same effect as invoking both {@link #setIgnoreLeadingWhitespaces(boolean)} and {@link #setIgnoreTrailingWhitespaces(boolean)} * with the same value. * * @param trim a flag indicating whether the whitespaces should remove whitespaces around values parsed/written. */ public final void trimValues(boolean trim) { this.setIgnoreLeadingWhitespaces(trim); this.setIgnoreTrailingWhitespaces(trim); } /** * Configures the parser/writer to limit the length of displayed contents being parsed/written in the exception message when an error occurs * *

If set to {@code 0}, then no exceptions will include the content being manipulated in their attributes, * and the {@code ""} string will appear in error messages as the parsed/written content.

* *

defaults to {@code -1} (no limit)

. * * @return the maximum length of contents displayed in exception messages in case of errors while parsing/writing. */ public int getErrorContentLength() { return errorContentLength; } /** * Configures the parser/writer to limit the length of displayed contents being parsed/written in the exception message when an error occurs. * *

If set to {@code 0}, then no exceptions will include the content being manipulated in their attributes, * and the {@code ""} string will appear in error messages as the parsed/written content.

* *

defaults to {@code -1} (no limit)

. * * @param errorContentLength maximum length of contents displayed in exception messages in case of errors while parsing/writing. */ public void setErrorContentLength(int errorContentLength) { this.errorContentLength = errorContentLength; } void runAutomaticConfiguration() { } /** * Returns a flag indicating whether the parser/writer should skip bit values as whitespace. * * By default the parser/writer * removes control characters and considers a whitespace any character where {@code character <= ' '} evaluates to * {@code true}. This includes bit values, i.e. {@code 0} (the \0 character) and {@code 1} which might * be produced by database dumps. Disabling this flag will prevent the parser/writer from discarding these characters * when {@link #getIgnoreLeadingWhitespaces()} or {@link #getIgnoreTrailingWhitespaces()} evaluate to {@code true}. * *

defaults to {@code true}

* * @return a flag indicating whether bit values (0 or 1) should be considered whitespace. */ public final boolean getSkipBitsAsWhitespace() { return skipBitsAsWhitespace; } /** * Configures the parser to skip bit values as whitespace. * * By default the parser/writer removes control characters and considers a whitespace any character where {@code character <= ' '} evaluates to * {@code true}. This includes bit values, i.e. {@code 0} (the \0 character) and {@code 1} which might * be produced by database dumps. Disabling this flag will prevent the parser/writer from discarding these characters * when {@link #getIgnoreLeadingWhitespaces()} or {@link #getIgnoreTrailingWhitespaces()} evaluate to {@code true}. * *

defaults to {@code true}

* * @param skipBitsAsWhitespace a flag indicating whether bit values (0 or 1) should be considered whitespace. */ public final void setSkipBitsAsWhitespace(boolean skipBitsAsWhitespace) { this.skipBitsAsWhitespace = skipBitsAsWhitespace; } /** * Returns the starting decimal range for {@code characters <= ' '} that should be skipped as whitespace, as * determined by {@link #getSkipBitsAsWhitespace()} * * @return the starting range after which characters will be considered whitespace */ protected final int getWhitespaceRangeStart() { return skipBitsAsWhitespace ? -1 : 1; } @Override public final String toString() { StringBuilder out = new StringBuilder(); out.append(getClass().getSimpleName()).append(':'); TreeMap config = new TreeMap(); addConfiguration(config); for (Entry e : config.entrySet()) { out.append("\n\t"); out.append(e.getKey()).append('=').append(e.getValue()); } out.append("Format configuration:\n\t").append(getFormat().toString()); return out.toString(); } protected void addConfiguration(Map out) { out.put("Null value", nullValue); out.put("Maximum number of characters per column", maxCharsPerColumn); out.put("Maximum number of columns", maxColumns); out.put("Skip empty lines", skipEmptyLines); out.put("Ignore trailing whitespaces", ignoreTrailingWhitespaces); out.put("Ignore leading whitespaces", ignoreLeadingWhitespaces); out.put("Selected fields", fieldSelector == null ? "none" : fieldSelector.describe()); out.put("Headers", Arrays.toString(headers)); out.put("Auto configuration enabled", autoConfigurationEnabled); out.put("RowProcessor error handler", errorHandler); out.put("Length of content displayed on error", errorContentLength); out.put("Restricting data in exceptions", errorContentLength == 0); out.put("Skip bits as whitespace", skipBitsAsWhitespace); } /** * Clones this configuration object to reuse user-provided settings. * * Properties that are specific to a given input (such as header names and selection of fields) can be reset to their defaults * if the {@code clearInputSpecificSettings} flag is set to {@code true} * * @param clearInputSpecificSettings flag indicating whether to clear settings that are likely to be associated with a given input. * * @return a copy of the configurations applied to the current instance. */ protected CommonSettings clone(boolean clearInputSpecificSettings) { try { CommonSettings out = (CommonSettings) super.clone(); if (out.format != null) { out.format = out.format.clone(); } if (clearInputSpecificSettings) { out.clearInputSpecificSettings(); } return out; } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } /** * Clones this configuration object. Use alternative {@link #clone(boolean)} method to reset properties that are * specific to a given input, such as header names and selection of fields. * * @return a copy of all configurations applied to the current instance. */ @Override protected CommonSettings clone() { return clone(false); } /** * Clears settings that are likely to be specific to a given input. */ protected void clearInputSpecificSettings() { fieldSelector = null; headers = null; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/CommonWriterSettings.java000066400000000000000000000231341400120543400324500ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.processor.*; import java.util.*; /** * This is the parent class for all configuration classes used by writers ({@link AbstractWriter}) * *

By default, all writers work with, at least, the following configuration options in addition to the ones provided by {@link CommonSettings}: * *

    *
  • rowWriterProcessor: a implementation of the interface {@link RowWriterProcessor} which processes input objects into a manageable format for writing.
  • *
* * @param the format supported by this writer. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.processor.RowWriterProcessor * @see com.univocity.parsers.csv.CsvWriterSettings * @see com.univocity.parsers.fixed.FixedWidthWriterSettings */ public abstract class CommonWriterSettings extends CommonSettings { private RowWriterProcessor rowWriterProcessor; private Boolean headerWritingEnabled = null; private String emptyValue = ""; private boolean expandIncompleteRows = false; private boolean columnReorderingEnabled = false; /** * Returns the String representation of an empty value (defaults to null) * *

When writing, if the writer has an empty String to write to the output, the emptyValue is used instead of an empty string * * @return the String representation of an empty value */ public String getEmptyValue() { return emptyValue; } /** * Sets the String representation of an empty value (defaults to null) * *

If the writer has an empty String to write to the output, the emptyValue is used instead of an empty string * * @param emptyValue the String representation of an empty value */ public void setEmptyValue(String emptyValue) { this.emptyValue = emptyValue; } /** * Returns the implementation of the interface {@link RowWriterProcessor} which processes input objects into a manageable format for writing. * * @return the implementation of the interface {@link RowWriterProcessor} which processes input objects into a manageable format for writing. * * @see com.univocity.parsers.common.processor.ObjectRowWriterProcessor * @see com.univocity.parsers.common.processor.BeanWriterProcessor */ public RowWriterProcessor getRowWriterProcessor() { return rowWriterProcessor; } /** * Defines a processor for input objects that converts them into a manageable format for writing. * * @param rowWriterProcessor the implementation of the interface {@link RowWriterProcessor} which processes input objects into a manageable format for writing. * * @see com.univocity.parsers.common.processor.ObjectRowWriterProcessor * @see com.univocity.parsers.common.processor.BeanWriterProcessor */ public void setRowWriterProcessor(RowWriterProcessor rowWriterProcessor) { this.rowWriterProcessor = rowWriterProcessor; } /** * Returns a flag indicating whether automatic writing of headers is enabled. If enabled, and headers are defined (or derived automatically if {@link #isAutoConfigurationEnabled} evaluates to {@code true}), * the writer will invoke the {@link AbstractWriter#writeHeaders()} method automatically. In this case, attempting to explicitly write the headers will result in a {@link TextWritingException}. * *

Defaults to {@code false}

* * @return returns {@code true} if automatic header writing is enabled, otherwise false. */ public final boolean isHeaderWritingEnabled() { return headerWritingEnabled == null ? false : headerWritingEnabled; } /** * Enables automatic writing of headers when they are available. If enabled, and headers are defined (or derived automatically if {@link #isAutoConfigurationEnabled} evaluates to {@code true}), * the writer will invoke the {@link AbstractWriter#writeHeaders()} method automatically. In this case, attempting to explicitly write the headers will result in a {@link TextWritingException}. * *

Defaults to {@code false}

* * @param headerWritingEnabled a flag to enable or disable automatic header writing. */ public final void setHeaderWritingEnabled(boolean headerWritingEnabled) { this.headerWritingEnabled = headerWritingEnabled; } /** * Indicates whether the writer should expand records with less columns than the number of headers. For example, if * the writer is using "A,B,C" as the headers, and the user provides a row with "V1,V2", then {@code null} will be * introduced in column C, generating the output "V1,V2,null". * *

Defaults to {@code false}

* * @return a flag indicating whether records with less columns than the number of headers are to be expanded with nulls. */ public final boolean getExpandIncompleteRows() { return expandIncompleteRows; } /** * Defines whether the writer should expand records with less columns than the number of headers. * For example, if the writer is using "A,B,C" as the headers, and the user provides a row with "V1,V2", then {@code null} will be * introduced in column C, generating the output "V1,V2,null". * *

Defaults to {@code false}

* * @param expandIncompleteRows a flag indicating whether records with less columns than the number of headers are to be expanded with nulls. */ public final void setExpandIncompleteRows(boolean expandIncompleteRows) { this.expandIncompleteRows = expandIncompleteRows; } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); out.put("Empty value", emptyValue); out.put("Header writing enabled", headerWritingEnabled); out.put("Row processor", rowWriterProcessor == null ? "none" : rowWriterProcessor.getClass().getName()); } @Override final void runAutomaticConfiguration() { if (rowWriterProcessor instanceof BeanWriterProcessor) { Class beanClass = ((BeanWriterProcessor) rowWriterProcessor).getBeanClass(); configureFromAnnotations(beanClass); } } /** * Configures the writer based on the annotations provided in a given class * * @param beanClass the classes whose annotations will be processed to derive configurations for writing. */ protected void configureFromAnnotations(Class beanClass) { if(!deriveHeadersFrom(beanClass)){ return; } Headers headerAnnotation = AnnotationHelper.findHeadersAnnotation(beanClass); String[] headersFromBean = AnnotationHelper.deriveHeaderNamesFromFields(beanClass, MethodFilter.ONLY_GETTERS); boolean writeHeaders = false; if (headerAnnotation != null) { if (headerAnnotation.sequence().length > 0) { headersFromBean = headerAnnotation.sequence(); } writeHeaders = headerAnnotation.write(); } if (headerWritingEnabled == null) { headerWritingEnabled = writeHeaders; } if (getHeaders() == null && headersFromBean.length > 0) { setHeadersDerivedFromClass(beanClass, headersFromBean); } } @Override protected CommonWriterSettings clone(boolean clearInputSpecificSettings) { return (CommonWriterSettings) super.clone(clearInputSpecificSettings); } @Override protected CommonWriterSettings clone() { return (CommonWriterSettings) super.clone(); } @Override protected void clearInputSpecificSettings() { super.clearInputSpecificSettings(); rowWriterProcessor = null; } /** * Indicates whether fields selected using the field selection methods (defined by the parent class {@link CommonSettings}) should be reordered (defaults to false). *

When disabled, each written record will contain values for all columns, in the order they are sent to the writer. Fields which were not selected will not be written but and the record will contain empty values. *

When enabled, each written record will contain values only for the selected columns. The values will be ordered according to the selection. * * @return true if the selected fields should be reordered when writing with field selection enabled, false otherwise */ public boolean isColumnReorderingEnabled() { return columnReorderingEnabled; } /** * Defines whether fields selected using the field selection methods (defined by the parent class {@link CommonSettings}) should be reordered (defaults to false). *

When disabled, each written record will contain values for all columns, in the order they are sent to the writer. Fields which were not selected will not be written but and the record will contain empty values. *

When enabled, each written record will contain values only for the selected columns. The values will be ordered according to the selection. * * @param columnReorderingEnabled the flag indicating whether or not selected fields should be reordered and written by the writer */ public void setColumnReorderingEnabled(boolean columnReorderingEnabled) { this.columnReorderingEnabled = columnReorderingEnabled; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/Context.java000066400000000000000000000114211400120543400277220ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.record.*; /** * Basic context information used internally by instances of {@link com.univocity.parsers.common.processor.core.Processor} and {@link com.univocity.parsers.common.record.Record}. * * @author Univocity Software Pty Ltd - dev@univocity.com * @see DefaultContext * @see ParsingContext * @see DefaultParsingContext */ public interface Context { /** * Returns the file headers that identify each parsed record. * * @return the headers used to identify each record parsed from the input. */ String[] headers(); /** * Returns the sequence of headers that have been selected. If no selection has been made, all available headers * will be returned, producing the same output as a call to method {@link #headers()}. * * @return the sequence of selected headers, or all headers if no selection has been made. */ String[] selectedHeaders(); /** * Returns the indexes of each field extracted from the input when fields are selected. * *

The indexes are relative to their original position in the input. *

For example, if the input has the fields "A, B, C, D", and the selected fields are "A, D", then the extracted field indexes will return [0, 3] * *

If no fields were selected, then this method will return null. This means all fields are being parsed. * * @return The indexes of each selected field; null if no fields were selected. * * @see com.univocity.parsers.common.CommonSettings */ int[] extractedFieldIndexes(); /** * Indicates whether selected fields are being reordered. * *

If columns are reordered, each parsed record will contain values only for the selected fields, as specified by {@link #extractedFieldIndexes()} * * @return true if the parsed records are being reordered by the parser, false otherwise * * @see com.univocity.parsers.common.CommonParserSettings * @see com.univocity.parsers.common.CommonSettings */ boolean columnsReordered(); /** * Returns the position of a header (0 based). * * @param header the header whose position will be returned * * @return the position of the given header, or -1 if it could not be found. */ int indexOf(String header); /** * Returns the position of a header (0 based). * * @param header the header whose position will be returned * * @return the position of the given header, or -1 if it could not be found. */ int indexOf(Enum header); /** * Returns the column index of the record being processed. * * @return the column index of the record being processed. */ int currentColumn(); /** * Returns the index of the last valid record parsed from the input * * @return the index of the last valid record parsed from the input */ long currentRecord(); /** * Stops the parsing process. Any open resources in use by the parser are closed * automatically unless {@link CommonParserSettings#isAutoClosingEnabled()} evaluates to {@code false}. */ void stop(); /** * Identifies whether the parser is running. * * @return true if the parser is stopped, false otherwise. */ boolean isStopped(); /** * Returns the length limit of parsed contents appearing in exception messages when an error occurs * *

If {@code 0}, then no exceptions will include the content being manipulated in their attributes, * and the {@code ""} string will appear in error messages as the parsed content.

* *

defaults to {@code -1} (no limit)

. * * @return the maximum length of the data content to display in exception messages */ int errorContentLength(); /** * Converts the given parsed row to a {@link Record} * * @param row the row to be converted into a {@link Record} * * @return a {@link Record} representing the given row. */ Record toRecord(String[] row); /** * Returns the metadata information associated with records produced by the current parsing process. * * @return the record metadata. */ RecordMetaData recordMetaData(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ContextSnapshot.java000066400000000000000000000033721400120543400314500ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; /** * A snapshot of a {@link Context} which retains copies of variable attributes of a given {@link Context} to * store the state of the parsing process at a given point in time. All runtime operations such as {@link #stop()} * will still work and affect the current parsing process. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public abstract class ContextSnapshot extends ContextWrapper { private final int currentColumn; private final long currentRecord; /** * Creates a snapshot of a given {@link Context} * @param context the context object whose variable attributes will be copied over. */ public ContextSnapshot(T context) { super(context); currentColumn = context.currentColumn(); currentRecord = context.currentRecord(); } @Override public int currentColumn() { return currentColumn; } @Override public long currentRecord() { return currentRecord; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ContextWrapper.java000066400000000000000000000044621400120543400312720ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.record.*; /** * A simple a wrapper for a {@link Context}. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public abstract class ContextWrapper implements Context { protected final T context; /** * Wraps a {@link Context}. * * @param context the context object to be wrapped. */ public ContextWrapper(T context) { this.context = context; } @Override public String[] headers() { return context.headers(); } @Override public int[] extractedFieldIndexes() { return context.extractedFieldIndexes(); } @Override public boolean columnsReordered() { return context.columnsReordered(); } @Override public int indexOf(String header) { return context.indexOf(header); } @Override public int indexOf(Enum header) { return context.indexOf(header); } @Override public int currentColumn() { return context.currentColumn(); } @Override public long currentRecord() { return context.currentRecord(); } @Override public void stop() { context.stop(); } @Override public boolean isStopped() { return context.isStopped(); } @Override public String[] selectedHeaders() { return context.selectedHeaders(); } @Override public int errorContentLength() { return context.errorContentLength(); } @Override public Record toRecord(String[] row) { return context.toRecord(row); } @Override public RecordMetaData recordMetaData() { return context.recordMetaData(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ConversionProcessor.java000066400000000000000000000054701400120543400323320ustar00rootroot00000000000000/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.conversions.*; /** * A basic interface for classes that associate {@link Conversion} implementations with fields of a given input/output. */ public interface ConversionProcessor { /** * Applies a set of {@link Conversion} objects over indexes of a record. * *

The idiom to define which indexes should have these conversions applies is as follows: *


	 *
	 * processor.convertIndexes(Conversions.trim(), Conversions.toUpperCase()).add(2, 5); // applies trim and uppercase conversions to fields in indexes 2 and 5
	 * 

* * @param conversions The sequence of conversions to be executed in a set of field indexes. * * @return A {@link FieldSet} for indexes. */ @SuppressWarnings({"unchecked", "rawtypes"}) FieldSet convertIndexes(Conversion... conversions); /** * Applies a set of {@link Conversion} objects over all elements of a record * * @param conversions The sequence of conversions to be executed in all elements of a record */ @SuppressWarnings({"rawtypes", "unchecked"}) void convertAll(Conversion... conversions); /** * Applies a set of {@link Conversion} objects over fields of a record by name. * *

The idiom to define which fields should have these conversions applied is as follows: *


	 *
	 * processor.convertFields(Conversions.trim(), Conversions.toUpperCase()).add("name", "position"); // applies trim and uppercase conversions to fields with headers "name" and "position"
	 * 

* * @param conversions The sequence of conversions to be executed in a set of field indexes. * * @return A {@link FieldSet} for field names. */ @SuppressWarnings({"unchecked", "rawtypes"}) FieldSet convertFields(Conversion... conversions); /** * Applies a sequence of conversions over values of a given type. Used for writing. * @param type the type over which a sequence of conversions should be applied * @param conversions the sequence of conversions to apply over values of the given type. */ void convertType(Class type, Conversion... conversions); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/DataProcessingException.java000066400000000000000000000231321400120543400330650ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.processor.*; import java.util.*; /** * A {@code DataProcessingException} is an error thrown during the processing of a record successfully parsed. * This type of error usually indicates that the input text has been parsed correctly, but the subsequent * transformations applied over the input (generally via a {@link RowProcessor}} failed. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class DataProcessingException extends TextParsingException { private static final long serialVersionUID = 1410975527141918215L; private String columnName; private int columnIndex; private Object[] row; private Object value; private Map values = new HashMap(); private boolean fatal = true; private boolean handled = false; private String details = null; /** * Creates a new exception with an error message only. * * @param message the error message */ public DataProcessingException(String message) { this(message, -1, null, null); } /** * Creates a new exception with an error message and error cause * * @param message the error message * @param cause the cause of the error */ public DataProcessingException(String message, Throwable cause) { this(message, -1, null, cause); } /** * Creates a new exception with an error message and the row that could not be processed. * * @param message the error message * @param row the row that could not be processed. */ public DataProcessingException(String message, Object[] row) { this(message, -1, row, null); } /** * Creates a new exception with an error message, the row that could not be processed, and the error cause. * * @param message the error message * @param row the row that could not be processed. * @param cause the cause of the error */ public DataProcessingException(String message, Object[] row, Throwable cause) { this(message, -1, row, cause); } /** * Creates a new exception with an error message and the column that could not be processed. * * @param message the error message * @param columnIndex index of the column that could not be processed. */ public DataProcessingException(String message, int columnIndex) { this(message, columnIndex, null, null); } /** * Creates a new exception with an error message, the column that could not be processed * the row that could not be processed, and the error cause. * * @param message the error message * @param columnIndex index of the column that could not be processed. * @param row the row that could not be processed. * @param cause the cause of the error */ public DataProcessingException(String message, int columnIndex, Object[] row, Throwable cause) { super(null, message, cause); setColumnIndex(columnIndex); this.row = row; } @Override protected String getErrorDescription() { return "Error processing parsed input"; } @Override protected String getDetails() { String details = (this.details == null ? "" : this.details + '\n') + super.getDetails(); Object[] row = getRow(); if (row != null) { row = row.clone(); for (int i = 0; i < row.length; i++) { row[i] = restrictContent(row[i]); } } details = printIfNotEmpty(details, "row", row); details = printIfNotEmpty(details, "value", restrictContent(getValue())); details = printIfNotEmpty(details, "columnName", getColumnName()); details = printIfNotEmpty(details, "columnIndex", getColumnIndex()); return details; } /** * Returns the name of the column from where the error occurred, if available. * * @return the name of the column from where the error occurred. */ public String getColumnName() { if (columnName != null) { return columnName; } String[] headers = getHeaders(); if (headers != null && getExtractedColumnIndex() != -1 && getExtractedColumnIndex() < headers.length) { return headers[getExtractedColumnIndex()]; } return null; } /** * Returns the index of the column from where the error occurred, if available. * * @return the index of the column from where the error occurred. */ public final int getColumnIndex() { return columnIndex; } /** * Returns the record being processed when the error occurred, if available. * * @return the record being processed when error occurred, if available. */ public final Object[] getRow() { return restrictContent(row); } /** * Defines the value being processed when the error occurred. * * @param value the value being processed when error occurred. */ public final void setValue(Object value) { if (errorContentLength == 0) { value = null; } if (value == null) { value = "null"; } this.value = value; } /** * Associates a label in the exception message (idenfied in curly braces) with a value being processed when the error occurred. * Used for formatting the exception message * * @param label a label in the exception message - any string enclosed by curly braces. * @param value the value being processed when error occurred, that should be replaced by the label in the exception message. */ public final void setValue(String label, Object value) { if (errorContentLength == 0) { value = null; } this.values.put(label, value); } /** * Returns the value being processed when the error occurred, if available. * * @return the value being processed when the error occurred, if available. */ public final Object getValue() { if (errorContentLength == 0) { return null; } if (value != null) { return value; } if (row != null && columnIndex != -1 && columnIndex < row.length) { return row[columnIndex]; } return null; } /** * Defines the column index being processed when the error occurred. * * @param columnIndex the column index being processed when error occurred. */ public final void setColumnIndex(int columnIndex) { this.columnIndex = columnIndex; } private int getExtractedColumnIndex() { if (this.extractedIndexes != null && columnIndex < extractedIndexes.length && columnIndex > -1) { return extractedIndexes[columnIndex]; } return columnIndex; } /** * Defines the name of the column being processed when the error occurred. * * @param columnName the name of the column being processed when error occurred. */ public final void setColumnName(String columnName) { this.columnName = columnName; } /** * Updates the exception with the record being processed when the error occurred. * * @param row the record data processed when the error occurred. */ public final void setRow(Object[] row) { if (errorContentLength == 0) { row = null; } this.row = row; } /** * Returns a flag indicating whether this error is fatal and the process must stop as it is impossible to proceed. * * @return a flag indicating whether this error is fatal and the process must stop as it is impossible to proceed. */ final boolean isFatal() { return fatal; } /** * Marks the error as non fatal and the parsing process might proceed. */ public final void markAsNonFatal() { this.fatal = false; } /** * Marks the error as handled so it doesn't trigger a {@link ProcessorErrorHandler} again. * @param handler the {@link ProcessorErrorHandler} used to handle this exception. */ public final void markAsHandled(ProcessorErrorHandler handler) { this.handled = handler != null && !(handler instanceof NoopProcessorErrorHandler) && !(handler instanceof NoopRowProcessorErrorHandler); } /** * Returns a flag indicating this exception has been handled by a user-provided {@link ProcessorErrorHandler} * * @return {@code true} if this exception has been handled to a user-provided {@link ProcessorErrorHandler}, * otherwise {@code false} */ public boolean isHandled() { return handled; } public void setDetails(String details) { this.details = details == null || details.trim().isEmpty() ? null : details; } @Override protected final String updateMessage(String msg) { if (errorContentLength == 0 || msg == null) { return msg; //doesn't replace labels enclosed within { and }. } StringBuilder out = new StringBuilder(msg.length()); int previous = 0; int start = 0; while (true) { start = msg.indexOf('{', start); if (start == -1) { break; } int end = msg.indexOf('}', start); if (end == -1) { break; } String label = msg.substring(start + 1, end); Object value = null; if ("value".equals(label)) { value = this.value; } else if (values.containsKey(label)) { value = values.get(label); } if (value != null) { String content = restrictContent(value); out.append(msg, previous, start); out.append(content); previous = end; } start = end; } out.append(msg, previous == 0 ? 0 : previous + 1, msg.length()); return out.toString(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/DataValidationException.java000066400000000000000000000053331400120543400330460ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; /** * A {@code DataValidationException} is an error thrown during the processing of a record successfully parsed, * but whose data failed to pass a validation defined by annotation {@link com.univocity.parsers.annotations.Validate} * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class DataValidationException extends DataProcessingException { private static final long serialVersionUID = 3110975527111918123L; /** * Creates a new validation exception with an error message only. * * @param message the error message */ public DataValidationException(String message) { super(message, -1, null, null); } /** * Creates a new validation exception with an error message and error cause * * @param message the error message * @param cause the cause of the error */ public DataValidationException(String message, Throwable cause) { super(message, -1, null, cause); } /** * Creates a new validation exception with an error message and the row that could not be validated. * * @param message the error message * @param row the row that could not be processed. */ public DataValidationException(String message, Object[] row) { super(message, -1, row, null); } /** * Creates a new validation exception with an error message, the row that could not be validated, and the error cause. * * @param message the error message * @param row the row that could not be processed. * @param cause the cause of the error */ public DataValidationException(String message, Object[] row, Throwable cause) { super(message, -1, row, cause); } /** * Creates a new validation exception with an error message and the column that could not be validated. * * @param message the error message * @param columnIndex index of the column that could not be validated. */ public DataValidationException(String message, int columnIndex) { super(message, columnIndex, null, null); } @Override protected String getErrorDescription() { return "Error validating parsed input"; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/DefaultContext.java000066400000000000000000000066571400120543400312460ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; import com.univocity.parsers.common.record.*; /** * Default implementation of the {@link Context} interface with essential information about the output being produced. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public class DefaultContext implements Context { protected boolean stopped = false; final ParserOutput output; final ColumnMap columnMap; final int errorContentLength; protected RecordFactory recordFactory; private String[] headers; public DefaultContext(int errorContentLength) { this(null, errorContentLength); } public DefaultContext(ParserOutput output, int errorContentLength) { this.output = output; this.errorContentLength = errorContentLength; this.columnMap = new ColumnMap(this, output); } @Override public String[] headers() { if(headers == null) { if (output == null) { headers = ArgumentUtils.EMPTY_STRING_ARRAY; } headers = output.getHeaderAsStringArray(); } return headers; } public String[] selectedHeaders() { if(headers == null) { headers(); } int[] extractedFieldIndexes = extractedFieldIndexes(); if (extractedFieldIndexes != null) { String[] extractedFields = new String[extractedFieldIndexes.length]; String[] headers = headers(); for (int i = 0; i < extractedFieldIndexes.length; i++) { extractedFields[i] = headers[extractedFieldIndexes[i]]; } return extractedFields; } return headers(); } @Override public int[] extractedFieldIndexes() { if (output == null) { return null; } return output.getSelectedIndexes(); } @Override public boolean columnsReordered() { if (output == null) { return false; } return output.isColumnReorderingEnabled(); } @Override public int indexOf(String header) { return columnMap.indexOf(header); } @Override public int indexOf(Enum header) { return columnMap.indexOf(header); } void reset() { if (output != null) { output.reset(); } recordFactory = null; columnMap.reset(); } @Override public int currentColumn() { if (output == null) { return -1; } return output.getCurrentColumn(); } @Override public long currentRecord() { if (output == null) { return -1; } return output.getCurrentRecord(); } @Override public void stop() { stopped = true; } @Override public boolean isStopped() { return stopped; } @Override public int errorContentLength() { return errorContentLength; } @Override public Record toRecord(String[] row) { if (recordFactory == null) { recordFactory = new RecordFactory(this); } return recordFactory.newRecord(row); } @Override public RecordMetaData recordMetaData(){ if(recordFactory == null){ recordFactory = new RecordFactory(this); } return recordFactory.getRecordMetaData(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/DefaultConversionProcessor.java000066400000000000000000000305231400120543400336340ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.util.*; /** * The base class for {@link RowProcessor} and {@link RowWriterProcessor} implementations that support value conversions provided by {@link Conversion} instances. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public abstract class DefaultConversionProcessor implements ConversionProcessor { private Map, Conversion[]> conversionsByType; protected FieldConversionMapping conversions; private boolean conversionsInitialized; private int[] fieldIndexes; private int[] reverseFieldIndexes; private boolean fieldsReordered; ProcessorErrorHandler errorHandler = NoopProcessorErrorHandler.instance; Context context; @Override @SuppressWarnings({"unchecked", "rawtypes"}) public final FieldSet convertIndexes(Conversion... conversions) { return getConversions().applyConversionsOnFieldIndexes(conversions); } @Override @SuppressWarnings({"rawtypes", "unchecked"}) public final void convertAll(Conversion... conversions) { getConversions().applyConversionsOnAllFields(conversions); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public final FieldSet convertFields(Conversion... conversions) { return getConversions().applyConversionsOnFieldNames(conversions); } private FieldConversionMapping getConversions() { if (conversions == null) { conversions = new FieldConversionMapping(); } return conversions; } protected void initializeConversions(String[] row, Context context) { conversionsInitialized = true; this.fieldIndexes = null; this.fieldsReordered = false; String[] contextHeaders = context == null ? null : context.headers(); if (contextHeaders != null && contextHeaders.length > 0) { getConversions().prepareExecution(false, contextHeaders); } else { getConversions().prepareExecution(false, row); } if (context != null) { this.fieldIndexes = context.extractedFieldIndexes(); this.fieldsReordered = context.columnsReordered(); } } /** * Executes the sequences of conversions defined using {@link DefaultConversionProcessor#convertFields(Conversion...)}, {@link DefaultConversionProcessor#convertIndexes(Conversion...)} and {@link DefaultConversionProcessor#convertAll(Conversion...)}, for every field in the given row. * *

Each field will be transformed using the {@link Conversion#execute(Object)} method. *

In general the conversions will process a String and convert it to some object value (such as booleans, dates, etc). * * @param row the parsed record with its individual records as extracted from the original input. * @param context the current state of the parsing process. * * @return an row of Object instances containing the values obtained after the execution of all conversions. *

Fields that do not have any conversion defined will just be copied to the object array into their original positions. */ public final Object[] applyConversions(String[] row, Context context) { boolean keepRow = true; Object[] objectRow = new Object[row.length]; boolean[] convertedFlags = conversionsByType != null ? new boolean[row.length] : null; System.arraycopy(row, 0, objectRow, 0, row.length); if (conversions != null) { if (!conversionsInitialized) { initializeConversions(row, context); } final int length = !fieldsReordered && fieldIndexes == null ? objectRow.length : fieldIndexes.length; for (int i = 0; i < length; i++) { try { if (!fieldsReordered) { if (fieldIndexes == null) { objectRow[i] = conversions.applyConversions(i, row[i], convertedFlags); } else { int index = fieldIndexes[i]; objectRow[index] = conversions.applyConversions(index, row[index], convertedFlags); } } else { objectRow[i] = conversions.applyConversions(fieldIndexes[i], row[i], convertedFlags); } } catch (Throwable ex) { keepRow = handleConversionError(ex, objectRow, i); } } } if (keepRow && convertedFlags != null) { keepRow = applyConversionsByType(false, objectRow, convertedFlags); } if (keepRow && validateAllValues(objectRow)) { return objectRow; } return null; } private void populateReverseFieldIndexes() { int max = 0; for (int i = 0; i < fieldIndexes.length; i++) { if (fieldIndexes[i] > max) { max = fieldIndexes[i]; } } reverseFieldIndexes = new int[max + 1]; Arrays.fill(reverseFieldIndexes, -1); for (int i = 0; i < fieldIndexes.length; i++) { reverseFieldIndexes[fieldIndexes[i]] = i; } } private boolean validateAllValues(Object[] objectRow) { if (conversions != null && conversions.validatedIndexes != null) { boolean keepRow = true; for (int i = 0; keepRow && i < conversions.validatedIndexes.length; i++) { final int index = conversions.validatedIndexes[i]; int valueIndex = index; if (fieldsReordered) { if (reverseFieldIndexes == null) { populateReverseFieldIndexes(); } valueIndex = reverseFieldIndexes[index]; } try { Object value = index < objectRow.length ? objectRow[valueIndex] : null; conversions.executeValidations(index, value); } catch (Throwable ex) { keepRow = handleConversionError(ex, objectRow, index); } } return keepRow; } return true; } /** * Executes the sequences of reverse conversions defined using {@link DefaultConversionProcessor#convertFields(Conversion...)}, {@link DefaultConversionProcessor#convertIndexes(Conversion...)} and {@link DefaultConversionProcessor#convertAll(Conversion...)}, for every field in the given row. * *

Each field will be transformed using the {@link Conversion#revert(Object)} method. *

In general the conversions will process an Object (such as a Boolean, Date, etc), and convert it to a String representation. * * @param executeInReverseOrder flag to indicate whether the conversion sequence should be executed in the reverse order of its declaration. * @param row the row of objects that will be converted * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * * @return {@code true} if the the row should be discarded */ public final boolean reverseConversions(boolean executeInReverseOrder, Object[] row, NormalizedString[] headers, int[] indexesToWrite) { boolean keepRow = true; boolean[] convertedFlags = conversionsByType != null ? new boolean[row.length] : null; if (conversions != null) { if (!conversionsInitialized) { conversionsInitialized = true; conversions.prepareExecution(true, headers != null ? NormalizedString.toArray(headers) : new String[row.length]); this.fieldIndexes = indexesToWrite; } if (executeInReverseOrder) { keepRow = validateAllValues(row); } final int last = fieldIndexes == null ? row.length : fieldIndexes.length; for (int i = 0; i < last; i++) { try { if (fieldIndexes == null || fieldIndexes[i] == -1) { row[i] = conversions.reverseConversions(executeInReverseOrder, i, row[i], convertedFlags); } else { int index = fieldIndexes[i]; row[index] = conversions.reverseConversions(executeInReverseOrder, index, row[index], convertedFlags); } } catch (Throwable ex) { keepRow = handleConversionError(ex, row, i); } } } if (keepRow && convertedFlags != null) { keepRow = applyConversionsByType(true, row, convertedFlags); } if (executeInReverseOrder) { return keepRow; } return keepRow && validateAllValues(row); } private boolean applyConversionsByType(boolean reverse, Object[] row, boolean[] convertedFlags) { boolean keepRow = true; for (int i = 0; i < row.length; i++) { if (convertedFlags[i]) { continue; //conversions already applied. Prevent default type conversion. } try { row[i] = applyTypeConversion(reverse, row[i]); } catch (Throwable ex) { keepRow = handleConversionError(ex, row, i); } } return keepRow; } /** * Handles an error that occurred when applying conversions over a value. If the user defined a * {@link ProcessorErrorHandler} the user will receive the exception and is able to determine whether or not * processing should continue, discarding the record. If the error handler is an instance of * {@link RetryableErrorHandler}, the user can provide a default value to use in place of the one that could not * be converted, and decide whether or not the record should be kept with the use of the * {@link RetryableErrorHandler#keepRecord()} method. * * @param ex the exception that occurred when applying a conversion * @param row the record being processed at the time the exception happened. * @param column the column if the given row, whose value could not be converted. If negative, it's not possible to * keep the record. * * @return {@code true} if the error has been handled by the user and the record can still be processed, otherwise * {@code false} if the record should be discarded. */ protected final boolean handleConversionError(Throwable ex, Object[] row, int column) { if (row != null && row.length < column) { //expand row so column index won't make error handlers blow up. row = Arrays.copyOf(row, column + 1); } DataProcessingException error = toDataProcessingException(ex, row, column); if (column > -1 && errorHandler instanceof RetryableErrorHandler) { ((RetryableErrorHandler) errorHandler).prepareToRun(); } error.markAsHandled(errorHandler); errorHandler.handleError(error, row, context); if (column > -1 && errorHandler instanceof RetryableErrorHandler) { RetryableErrorHandler retry = ((RetryableErrorHandler) errorHandler); Object defaultValue = retry.getDefaultValue(); row[column] = defaultValue; return !retry.isRecordSkipped(); } return false; } protected DataProcessingException toDataProcessingException(Throwable ex, Object[] row, int column) { DataProcessingException error; if (ex instanceof DataProcessingException) { error = (DataProcessingException) ex; error.setRow(row); error.setColumnIndex(column); } else { error = new DataProcessingException("Error processing data conversions", column, row, ex); } error.markAsNonFatal(); error.setContext(context); return error; } @Override public final void convertType(Class type, Conversion... conversions) { ArgumentUtils.noNulls("Type to convert", type); ArgumentUtils.noNulls("Sequence of conversions to apply over data of type " + type.getSimpleName(), conversions); if (conversionsByType == null) { conversionsByType = new HashMap, Conversion[]>(); } conversionsByType.put(type, conversions); } private Object applyTypeConversion(boolean revert, Object input) { if (conversionsByType == null || input == null) { return input; } Conversion[] conversionSequence = conversionsByType.get(input.getClass()); if (conversionSequence == null) { return input; } if (revert) { for (int i = 0; i < conversionSequence.length; i++) { input = conversionSequence[i].revert(input); } } else { for (int i = 0; i < conversionSequence.length; i++) { input = conversionSequence[i].execute(input); } } return input; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/DefaultParsingContext.java000066400000000000000000000052361400120543400325620ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; import com.univocity.parsers.common.input.*; import java.util.*; /** * The default {@link ParsingContext} implementation used internally by {@link AbstractParser} to expose information about a parsing process in execution. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.ParsingContext * @see com.univocity.parsers.common.AbstractParser * @see com.univocity.parsers.common.processor.RowProcessor */ public class DefaultParsingContext extends DefaultContext implements ParsingContext { private final CharInputReader input; private final AbstractParser parser; public DefaultParsingContext(AbstractParser parser, int errorContentLength) { super(parser == null ? null : parser.output, errorContentLength); this.parser = parser; this.input = parser == null ? null : parser.input; } @Override public long currentLine() { return input.lineCount(); } @Override public long currentChar() { return input.charCount(); } @Override public void skipLines(long lines) { input.skipLines(lines); } @Override public String fieldContentOnError() { char[] chars = output.appender.getChars(); if (chars != null) { int length = output.appender.length(); if (length > chars.length) { length = chars.length; } if (length > 0) { return new String(chars, 0, length); } } return null; } @Override public String currentParsedContent() { if (input != null) { return input.currentParsedContent(); } return null; } @Override public int currentParsedContentLength() { if (input != null) { return input.currentParsedContentLength(); } return 0; } @Override public Map comments() { return parser.getComments(); } @Override public String lastComment() { return parser.getLastComment(); } @Override public String[] parsedHeaders() { return parser.getParsedHeaders(); } @Override public char[] lineSeparator() { return input.getLineSeparator(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/DummyFormat.java000066400000000000000000000025461400120543400305520ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.util.*; /** * A concrete (dummy) implementation of {@code Format}. Used by {@link AbstractWriter} to manage its internal configuration of field selections * * @see AbstractWriter * @see CommonSettings * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ final class DummyFormat extends Format { static final DummyFormat instance = new DummyFormat(); private DummyFormat() { } @Override protected final TreeMap getConfiguration() { return new TreeMap(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/Format.java000066400000000000000000000206551400120543400275370ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.util.Map.*; import java.util.*; /** * This is the parent class for all configuration classes that define a text format. * *

By default, all parsers and writers have to handle, at least, the following format definitions: * *

    *
  • lineSeparator: the 1-2 character sequence that indicates the end of a line. Newline sequences are different across operating systems. Typically: *
      *
    • Windows uses carriage return and line feed: \r\n
    • *
    • Linux/Unix uses line feed only: \n
    • *
    • MacOS uses carriage return only: \r
    • *
    * {@link Format#lineSeparator} defaults to the system line separator *
  • *
  • normalizedNewline: a single character used to represent the end of a line uniformly in any parsed content. It has the following implications: *
      *
    • When reading a text-based input, the sequence of characters defined in {@link Format#lineSeparator} will be replaced by this character.
    • *
    • When writing to a text-based output, this character will be replaced by the sequence of characters defined in {@link Format#lineSeparator}.
    • *
    *

    {@link Format#normalizedNewline} defaults to '\n'. *

  • *
  • comment:a character that, if found in the beginning of a line of text, represents comment in any text-based input supported by univocity-parsers. *

    {@link Format#comment} defaults to '#'.

  • *
* * @see com.univocity.parsers.csv.CsvFormat * @see com.univocity.parsers.fixed.FixedWidthFormat * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public abstract class Format implements Cloneable{ private static final String systemLineSeparatorString; private static final char[] systemLineSeparator; static { String lineSeparator = System.getProperty("line.separator"); if (lineSeparator == null) { systemLineSeparatorString = "\n"; } else { systemLineSeparatorString = lineSeparator; } systemLineSeparator = systemLineSeparatorString.toCharArray(); } private String lineSeparatorString; private char[] lineSeparator; private char normalizedNewline = '\n'; private char comment = '#'; protected Format() { this.lineSeparator = systemLineSeparator.clone(); this.lineSeparatorString = systemLineSeparatorString; } /** * Returns the current line separator character sequence, which can contain 1 to 2 characters. Defaults to the system's line separator sequence (usually '\r\n' in Windows, '\r' in MacOS, and '\n' in Linux/Unix). * @return the sequence of 1 to 2 characters that identifies the end of a line */ public char[] getLineSeparator() { return lineSeparator.clone(); } /** * Returns the system's line separator sequence, which can contain 1 to 2 characters. * @return a sequence of 1 to 2 characters used as the system's line ending. */ public static char[] getSystemLineSeparator(){ return systemLineSeparator.clone(); } /** * Returns the current line separator sequence as a String of 1 to 2 characters. Defaults to the system's line separator sequence (usually "\r\n" in Windows, "\r" in MacOS, and "\n" in Linux/Unix). * @return the sequence of 1 to 2 characters that identifies the end of a line */ public String getLineSeparatorString() { return lineSeparatorString; } /** * Defines the line separator sequence that should be used for parsing and writing. * @param lineSeparator a sequence of 1 to 2 characters that identifies the end of a line */ public void setLineSeparator(String lineSeparator) { if (lineSeparator == null || lineSeparator.isEmpty()) { throw new IllegalArgumentException("Line separator cannot be empty"); } setLineSeparator(lineSeparator.toCharArray()); } /** * Defines the line separator sequence that should be used for parsing and writing. * @param lineSeparator a sequence of 1 to 2 characters that identifies the end of a line */ public void setLineSeparator(char[] lineSeparator) { if (lineSeparator == null || lineSeparator.length == 0) { throw new IllegalArgumentException("Invalid line separator. Expected 1 to 2 characters"); } if (lineSeparator.length > 2) { throw new IllegalArgumentException("Invalid line separator. Up to 2 characters are expected. Got " + lineSeparator.length + " characters."); } this.lineSeparator = lineSeparator; this.lineSeparatorString = new String(lineSeparator); if(lineSeparator.length == 1){ setNormalizedNewline(lineSeparator[0]); } } /** * Returns the normalized newline character, which is automatically replaced by {@link Format#lineSeparator} when reading/writing. Defaults to '\n'. * @return the normalized newline character */ public char getNormalizedNewline() { return normalizedNewline; } /** * Sets the normalized newline character, which is automatically replaced by {@link Format#lineSeparator} when reading/writing * @param normalizedNewline a single character used to represent a line separator. */ public void setNormalizedNewline(char normalizedNewline) { this.normalizedNewline = normalizedNewline; } /** * Compares the given character against the {@link Format#normalizedNewline} character. * @param ch the character to be verified * @return true if the given character is the normalized newline character, false otherwise */ public boolean isNewLine(char ch) { return this.normalizedNewline == ch; } /** * Returns the character that represents a line comment. Defaults to '#' *

Set it to '\0' to disable comment skipping. * @return the comment character */ public char getComment() { return comment; } /** * Defines the character that represents a line comment when found in the beginning of a line of text. Defaults to '#' *

Use '\0' to disable comment skipping. * @param comment the comment character */ public void setComment(char comment) { this.comment = comment; } /** * Identifies whether or not a given character represents a comment * @param ch the character to be verified * @return true if the given character is the comment character, false otherwise */ public boolean isComment(char ch) { return this.comment == ch; } private static String getFormattedValue(Object value) { if (value instanceof Character) { char ch = (Character) value; switch (ch) { case '\n': return "\\n"; case '\r': return "\\r"; case '\t': return "\\t"; case '\0': return "\\0"; default: return value.toString(); } } if (value instanceof String) { String s = (String) value; StringBuilder tmp = new StringBuilder(); for (int i = 0; i < s.length(); i++) { tmp.append(getFormattedValue(s.charAt(i))); } value = tmp.toString(); } if (String.valueOf(value).trim().isEmpty()) { return "'" + value + '\''; } return String.valueOf(value); } @Override public final String toString() { StringBuilder out = new StringBuilder(); out.append(getClass().getSimpleName()).append(':'); TreeMap config = getConfiguration(); config.put("Comment character", comment); config.put("Line separator sequence", lineSeparatorString); config.put("Line separator (normalized)", normalizedNewline); for (Entry e : config.entrySet()) { out.append("\n\t\t"); out.append(e.getKey()).append('=').append(getFormattedValue(e.getValue())); } return out.toString(); } protected abstract TreeMap getConfiguration(); @Override protected Format clone() { try { return (Format) super.clone(); } catch(CloneNotSupportedException e){ throw new IllegalStateException("Error cloning format object", e); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/Internal.java000066400000000000000000000050751400120543400300620ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * Internal class to keep common internal functions that are used in multiple places. * * @author Univocity Software Pty Ltd - dev@univocity.com */ class Internal { public static final void process(String[] row, Processor processor, C context, ProcessorErrorHandler errorHandler) { try { processor.rowProcessed(row, context); } catch (DataProcessingException ex) { ex.setContext(context); if (!ex.isFatal() && !ex.isHandled() && ex.getColumnIndex() > -1 && errorHandler instanceof RetryableErrorHandler) { RetryableErrorHandler retry = ((RetryableErrorHandler) errorHandler); ex.markAsHandled(errorHandler); retry.handleError(ex, row, context); if (!retry.isRecordSkipped()) { try { processor.rowProcessed(row, context); return; } catch (DataProcessingException e) { ex = e; } catch (Throwable t) { throwDataProcessingException(processor, t, row, context.errorContentLength()); } } } ex.setErrorContentLength(context.errorContentLength()); if (ex.isFatal()) { throw ex; } ex.markAsHandled(errorHandler); errorHandler.handleError(ex, row, context); } catch (Throwable t) { throwDataProcessingException(processor, t, row, context.errorContentLength()); } } private static final void throwDataProcessingException(Processor processor, Throwable t, String[] row, int errorContentLength) throws DataProcessingException { DataProcessingException ex = new DataProcessingException("Unexpected error processing input row " + AbstractException.restrictContent(errorContentLength, Arrays.toString(row)) + " using Processor " + processor.getClass().getName() + '.' , AbstractException.restrictContent(errorContentLength, row) , t); ex.restrictContent(errorContentLength); throw ex; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/IterableResult.java000066400000000000000000000024521400120543400312300ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; /** * An {@link Iterable} result that provides the current parsing context * through the {@link #getContext()} method * * @author Univocity Software Pty Ltd - dev@univocity.com */ public interface IterableResult extends Iterable { /** * Returns the current parsing {@link Context}, if available * @return the contextual object with information about an ongoing parsing process */ C getContext(); @Override ResultIterator iterator(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/LineReader.java000066400000000000000000000034131400120543400303120ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.io.*; /** * A simple Reader implementation to enable parsers to process lines on demand, via {@link AbstractParser#parseLine(String)} * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ class LineReader extends Reader { private String line; private int length; private int next = 0; public LineReader() { } public void setLine(String line) { this.line = line; this.length = line.length(); this.next = 0; } @Override public int read(char cbuf[], int off, int len) { if (len == 0) { return 0; } if (next >= length) { return -1; } int read = Math.min(length - next, len); line.getChars(next, next + read, cbuf, off); next += read; return read; } @Override public long skip(long ns) { return 0; } @Override public boolean ready() { return line != null; } @Override public boolean markSupported() { return false; } @Override public void close() { line = null; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/NoopParsingContext.java000066400000000000000000000054141400120543400321070ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.record.*; import java.util.*; /** * A {@link ParsingContext} implementation that does nothing. * * @author Univocity Software Pty Ltd - dev@univocity.com */ class NoopParsingContext implements ParsingContext { static final NoopParsingContext instance = new NoopParsingContext(); private RecordMetaData recordMetaData; private NoopParsingContext() { } @Override public void stop() { } @Override public boolean isStopped() { return false; } @Override public long currentLine() { return 0; } @Override public long currentChar() { return 0; } @Override public int currentColumn() { return 0; } @Override public long currentRecord() { return 0; } @Override public void skipLines(long lines) { } @Override public String[] parsedHeaders() { return null; } @Override public String currentParsedContent() { return null; } public int currentParsedContentLength() { return 0; } @Override public Map comments() { return Collections.emptyMap(); } @Override public String lastComment() { return null; } @Override public char[] lineSeparator() { return Format.getSystemLineSeparator(); } @Override public String[] headers() { return null; } @Override public String[] selectedHeaders() { return null; } @Override public int[] extractedFieldIndexes() { return null; } @Override public boolean columnsReordered() { return true; } @Override public int indexOf(String header) { return -1; } @Override public int indexOf(Enum header) { return -1; } @Override public String fieldContentOnError() { return null; } @Override public int errorContentLength() { return -1; } @Override public Record toRecord(String[] row) { return null; } @Override public RecordMetaData recordMetaData() { if(recordMetaData == null){ recordMetaData = new RecordFactory(this).getRecordMetaData(); } return recordMetaData; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/NoopProcessorErrorHandler.java000066400000000000000000000034641400120543400334310ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; /** * An (singleton) implementation of {@link ProcessorErrorHandler} that simply rethrows any {@link DataProcessingException} * that comes into its {@link #handleError(DataProcessingException, Object[], Context)}} method * * @see ProcessorErrorHandler * * @author Univocity Software Pty Ltd - parsers@univocity.com * @param the {@code Context} type provided by the parser implementation. * */ public final class NoopProcessorErrorHandler implements ProcessorErrorHandler { public static final ProcessorErrorHandler instance = new NoopProcessorErrorHandler(); private NoopProcessorErrorHandler() { } /** * Rethrows the {@link DataProcessingException} * * @param error the exception thrown during the processing an input record. Will be rethrown to abort the parsing process. * @param inputRow ignored * @param context ignored */ @Override public void handleError(DataProcessingException error, Object[] inputRow, T context) { throw error; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/NoopRowProcessorErrorHandler.java000066400000000000000000000030721400120543400341140ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; /** * An (singleton) implementation of {@link RowProcessorErrorHandler} that simply rethrows any {@link DataProcessingException} * that comes into its {@link #handleError(DataProcessingException, Object[], ParsingContext)} method * * @see RowProcessorErrorHandler * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ final class NoopRowProcessorErrorHandler implements RowProcessorErrorHandler { public static final RowProcessorErrorHandler instance = new NoopRowProcessorErrorHandler(); private NoopRowProcessorErrorHandler() { } /** * Rethrows the {@link DataProcessingException} */ @Override public void handleError(DataProcessingException error, Object[] inputRow, ParsingContext context) { throw error; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/NormalizedString.java000066400000000000000000000535571400120543400316110ustar00rootroot00000000000000/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.io.*; import java.util.*; import static com.univocity.parsers.common.ArgumentUtils.*; /** * A {@code NormalizedString} allows representing text in a normalized fashion. Strings * with different character case or surrounding whitespace are considered the same. * * Used to represent groups of fields, where users may refer to their names using * different character cases or whitespaces. * * Where the character case or the surrounding space is relevant, the {@code NormalizedString} * will have its {@link #isLiteral()} method return {@code true}, meaning the exact * character case and surrounding whitespaces are required for matching it. * * Invoking {@link #valueOf(String)} with a {@code String} surrounded by single quotes * will create a literal {@code NormalizedString}. Use {@link #literalValueOf(String)} * to obtain the same {@code NormalizedString} without having to introduce single quotes. * */ public final class NormalizedString implements Serializable, Comparable, CharSequence { private static final long serialVersionUID = -3904288692735859811L; private static final StringCache stringCache = new StringCache() { @Override protected NormalizedString process(String input) { if (input == null) { return null; } return new NormalizedString(input); } }; private final String original; private final String normalized; private final boolean literal; private final int hashCode; private NormalizedString(String string) { String trimmed = string.trim(); if (trimmed.length() > 2 && trimmed.charAt(0) == '\'' && trimmed.charAt(trimmed.length() - 1) == '\'') { this.original = string.substring(1, string.length() - 1); this.normalized = original; this.hashCode = normalize(original).hashCode(); this.literal = true; } else { this.original = string; this.normalized = normalize(original); this.hashCode = normalized.hashCode(); this.literal = false; } } private String normalize(Object value) { String str = String.valueOf(value); str = str.trim().toLowerCase(); return str; } public boolean isLiteral() { return literal; } @Override public boolean equals(Object anObject) { if (anObject == this) { return true; } if (anObject == null) { return false; } if (anObject instanceof NormalizedString) { NormalizedString other = (NormalizedString) anObject; if (this.literal || other.literal) { return original.equals(other.original); } return this.normalized.equals(other.normalized); } if (literal) { return original.equals(String.valueOf(anObject)); } else { return normalized.equals(normalize(anObject)); } } @Override public int hashCode() { return hashCode; } @Override public int length() { return original.length(); } @Override public char charAt(int index) { return original.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return original.subSequence(start, end); } @Override public int compareTo(NormalizedString o) { if (o == this) { return 0; } if (this.literal || o.literal) { return original.compareTo(o.original); } return this.normalized.compareTo(o.normalized); } /** * Compares a {@code NormalizedString} against a {@code String} lexicographically. * @param o a plain {@code String} * @return the result of {@link String#compareTo(String)}. If this {@code NormalizedString} * is a literal, the original argument string will be compared. If this {@code NormalizedString} * is not a literal, the result will be from the comparison of the normalized content of both strings * (i.e. surrounding whitespaces and character case differences will be ignored). */ public int compareTo(String o) { return compareTo(valueOf(o)); } @Override public String toString() { return original; } /** * Creates a literal {@code NormalizedString}, meaning it will only match with * other {@code String} or {@code NormalizedString} if they have the exact same content * including character case and surrounding whitespaces. * * @param string the input {@code String} * @return the literal {@code NormalizedString} version of the given string. */ public static NormalizedString literalValueOf(String string) { if (string == null) { return null; } return stringCache.get('\'' + string + "\'"); } /** * Creates a non-literal {@code NormalizedString}, meaning it will match with * other {@code String} or {@code NormalizedString} regardless of different * including character case and surrounding whitespaces. * * If the input value is enclosed with single quotes, a literal {@code NormalizedString} * will be returned, as described in {@link #literalValueOf(String)} * * @param o the input object whose {@code String} representation will be used * @return the {@code NormalizedString} of the given object. */ public static NormalizedString valueOf(Object o) { if (o == null) { return null; } return stringCache.get(o.toString()); } /** * Creates a non-literal {@code NormalizedString}, meaning it will match with * other {@code String} or {@code NormalizedString} regardless of different * including character case and surrounding whitespaces. * * If the input string is enclosed with single quotes, a literal {@code NormalizedString} * will be returned, as described in {@link #literalValueOf(String)} * * @param string the input string * @return the {@code NormalizedString} of the given string. */ public static NormalizedString valueOf(String string) { if (string == null) { return null; } return stringCache.get(string); } /** * Converts a {@code NormalizedString} back to its original {@code String} representation * @param string the normalized string * @return the original string used to create the given normalized representation. */ public static String valueOf(NormalizedString string) { if (string == null) { return null; } return string.original; } /** * Converts a collection of plain strings into an array of {@code NormalizedString} * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static NormalizedString[] toArray(Collection args) { if (args == null) { throw new IllegalArgumentException("String collection cannot be null"); } NormalizedString[] out = new NormalizedString[args.size()]; Iterator it = args.iterator(); for (int i = 0; i < out.length; i++) { out[i] = valueOf(it.next()); } return out; } /** * Converts a collection of normalized strings into an array of {@code String} * @param args the normalized strings to convert back to to {@code String} * @return the {@code String} representations of all normalized strings. */ public static String[] toStringArray(Collection args) { if (args == null) { throw new IllegalArgumentException("String collection cannot be null"); } String[] out = new String[args.size()]; Iterator it = args.iterator(); for (int i = 0; i < out.length; i++) { out[i] = valueOf(it.next()); } return out; } /** * Converts multiple plain strings into an array of {@code NormalizedString}, ensuring * no duplicate {@code NormalizedString} elements exist, even if their original {@code String}s * are different. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static NormalizedString[] toUniqueArray(String... args) { notEmpty("Element array", args); NormalizedString[] out = toArray(args); NormalizedString[] duplicates = findDuplicates(out); if (duplicates.length > 0) { throw new IllegalArgumentException("Duplicate elements found: " + Arrays.toString(duplicates)); } return out; } /** * Converts multiple plain strings into an array of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static NormalizedString[] toArray(String... args) { if (args == null) { return null; } else if (args.length == 0) { return EMPTY_NORMALIZED_STRING_ARRAY; } NormalizedString[] out = new NormalizedString[args.length]; for (int i = 0; i < args.length; i++) { out[i] = valueOf(args[i]); } return out; } /** * Converts multiple normalized strings into an array of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the {@code String} representations of all input strings. */ public static String[] toArray(NormalizedString... args) { if (args == null) { return null; } else if (args.length == 0) { return EMPTY_STRING_ARRAY; } String[] out = new String[args.length]; for (int i = 0; i < args.length; i++) { out[i] = valueOf(args[i]); } return out; } private static > T getCollection(T out, String... args) { Collections.addAll(out, toArray(args)); return out; } private static > T getCollection(T out, Collection args) { Collections.addAll(out, toArray(args)); return out; } private static > T getCollection(T out, NormalizedString... args) { Collections.addAll(out, toArray(args)); return out; } private static > T getStringCollection(T out, Collection args) { Collections.addAll(out, toStringArray(args)); return out; } /** * Converts multiple plain strings into an {@code ArrayList} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static ArrayList toArrayList(String... args) { return getCollection(new ArrayList(), args); } /** * Converts multiple plain strings into an {@code ArrayList} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static ArrayList toArrayList(Collection args) { return getCollection(new ArrayList(), args); } /** * Converts multiple normalized strings into a {@code HashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static ArrayList toArrayListOfStrings(NormalizedString... args) { return getCollection(new ArrayList(), args); } /** * Converts multiple normalized strings into a {@code HashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static ArrayList toArrayListOfStrings(Collection args) { return getStringCollection(new ArrayList(), args); } /** * Converts multiple plain strings into a {@code TreeSet} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static TreeSet toTreeSet(String... args) { return getCollection(new TreeSet(), args); } /** * Converts multiple plain strings into a {@code TreeSet} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static TreeSet toTreeSet(Collection args) { return getCollection(new TreeSet(), args); } /** * Converts multiple normalized strings into a {@code HashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static TreeSet toTreeSetOfStrings(NormalizedString... args) { return getCollection(new TreeSet(), args); } /** * Converts multiple normalized strings into a {@code HashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static TreeSet toTreeSetOfStrings(Collection args) { return getStringCollection(new TreeSet(), args); } /** * Converts multiple plain strings into a {@code HashSet} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static HashSet toHashSet(String... args) { return getCollection(new HashSet(), args); } /** * Converts multiple plain strings into a {@code HashSet} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static HashSet toHashSet(Collection args) { return getCollection(new HashSet(), args); } /** * Converts multiple normalized strings into a {@code HashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static HashSet toHashSetOfStrings(NormalizedString... args) { return getCollection(new HashSet(), args); } /** * Converts multiple normalized strings into a {@code HashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static HashSet toHashSetOfStrings(Collection args) { return getStringCollection(new HashSet(), args); } /** * Converts multiple plain strings into a {@code LinkedHashSet} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static LinkedHashSet toLinkedHashSet(String... args) { return getCollection(new LinkedHashSet(), args); } /** * Converts multiple plain strings into a {@code LinkedHashSet} of {@code NormalizedString}. * * @param args the strings to convert to {@code NormalizedString} * @return the {@code NormalizedString} representations of all input strings. */ public static LinkedHashSet toLinkedHashSet(Collection args) { return getCollection(new LinkedHashSet(), args); } /** * Converts multiple normalized strings into a {@code LinkedHashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static LinkedHashSet toLinkedHashSetOfStrings(NormalizedString... args) { return getCollection(new LinkedHashSet(), args); } /** * Converts multiple normalized strings into a {@code LinkedHashSet} of {@code String}. * * @param args the normalized strings to convert to {@code String} * @return the original {@code String}s of all input normalized strings. */ public static LinkedHashSet toLinkedHashSetOfStrings(Collection args) { return getStringCollection(new LinkedHashSet(), args); } /** * Returns the literal representation of this {@code NormalizedString}, meaning it will only match with * other {@code String} or {@code NormalizedString} if they have the exact same content * including character case and surrounding whitespaces. * * @return the literal representation of the current {@code NormalizedString} */ public NormalizedString toLiteral() { if (literal) { return this; } return literalValueOf(this.original); } /** * Analyzes a group of NormalizedString to identify any instances whose normalized content will generate * clashes. Any clashing entries will be converted to their literal counterparts (using {@link #toLiteral()}), * making it possible to identify one from the other. * * @param strings a group of identifiers that may contain ambiguous entries if their character case or surrounding whitespaces is not considered. * This array will be modified. * * @return the input string array, with {@code NormalizedString} literals in the positions where clashes would originally occur. */ public static NormalizedString[] toIdentifierGroupArray(NormalizedString[] strings) { identifyLiterals(strings); return strings; } /** * Analyzes a group of String to identify any instances whose normalized content will generate * clashes. Any clashing entries will be converted to their literal counterparts (using {@link #toLiteral()}), * making it possible to identify one from the other. * * @param strings a group of identifiers that may contain ambiguous entries if their character case or surrounding whitespaces is not considered. * * * @return a {@code NormalizedString} array with literals in the positions where clashes would originally occur. */ public static NormalizedString[] toIdentifierGroupArray(String[] strings) { NormalizedString[] out = toArray(strings); identifyLiterals(out, false, false); return out; } /** * Analyzes a group of NormalizedString to identify any instances whose normalized content will generate * clashes. Any clashing entries will be converted to their literal counterparts (using {@link #toLiteral()}), * making it possible to identify one from the other. * * @param strings a group of identifiers that may contain ambiguous entries if their character case or surrounding whitespaces is not considered. * This array will be modified. * * @return {@code true} if any entry has been modified to be a literal, otherwise {@code false} * */ public static boolean identifyLiterals(NormalizedString[] strings) { return identifyLiterals(strings, false, false); } /** * Analyzes a group of NormalizedString to identify any instances whose normalized content will generate * clashes. Any clashing entries will be converted to their literal counterparts (using {@link #toLiteral()}), * making it possible to identify one from the other. * * @param strings a group of identifiers that may contain ambiguous entries if their character case or surrounding whitespaces is not considered. * This array will be modified. * * @param lowercaseIdentifiers flag indicating that identifiers are stored in lower case (for compatibility with databases). * If a string has a uppercase character, it means it must become a literal. * @param uppercaseIdentifiers flag indicating that identifiers are stored in upper case (for compatibility with databases). * If a string has a lowercase character, it means it must become a literal. * * @return {@code true} if any entry has been modified to be a literal, otherwise {@code false} * */ public static boolean identifyLiterals(NormalizedString[] strings, boolean lowercaseIdentifiers, boolean uppercaseIdentifiers) { if (strings == null) { return false; } TreeMap normalizedMap = new TreeMap(); boolean modified = false; for (int i = 0; i < strings.length; i++) { NormalizedString string = strings[i]; if (string == null || string.isLiteral()) { continue; } if (shouldBeLiteral(string.original, lowercaseIdentifiers, uppercaseIdentifiers)) { strings[i] = NormalizedString.literalValueOf(string.original); continue; } Object[] clashing = normalizedMap.get(string); if (clashing != null && !string.original.equals(((NormalizedString) clashing[0]).original)) { strings[i] = NormalizedString.literalValueOf(string.original); strings[(Integer) clashing[1]] = ((NormalizedString) clashing[0]).toLiteral(); modified = true; } else { normalizedMap.put(string, new Object[]{string, i}); } } return modified; } private static boolean shouldBeLiteral(String string, boolean lowercaseIdentifiers, boolean uppercaseIdentifiers) { if (lowercaseIdentifiers || uppercaseIdentifiers) { for (int i = 0; i < string.length(); i++) { char ch = string.charAt(i); if ((uppercaseIdentifiers && !Character.isUpperCase(ch)) || (lowercaseIdentifiers && !Character.isLowerCase(ch))) { return true; } } } return false; } /** * Returns the internal string cache to allow users to tweak its size limit or clear it when appropriate * @return the string cache used to store {@code NormalizedString} instances associated with their original {@code String}. */ public static StringCache getCache(){ return stringCache; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ParserOutput.java000066400000000000000000000325511400120543400307620ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.input.*; import java.util.*; /** * The ParserOutput is the component that manages records parsed by {@link AbstractParser} and their values. * * It is solely responsible for deciding when: *

    *
  • parsed records should be reordered according to the fields selected in {@link CommonSettings}
  • *
  • characters and values parsed in {@link AbstractParser#parseRecord()} should be retained or discarded
  • *
  • input headers should be loaded from the records parsed in {@link AbstractParser#parseRecord()} or from {@link CommonSettings#getHeaders()}
  • *
* * Implementations of this class are made available to concrete parser implementations of {@link AbstractParser}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see CommonSettings */ public class ParserOutput { /** * Keeps track of the current column being parsed in the input. * Calls to {@link ParserOutput#valueParsed} and {@link ParserOutput#emptyParsed} will increase the column count. * This value is reset to zero after a row is parsed. */ protected int column = 0; /** * Stores the values parsed for a record. */ protected final String[] parsedValues; /** *

Stores (shared) references to {@link CharAppender} for each potential column (as given by {@link CommonSettings#getMaxColumns()}). *

Fields that are not selected will receive an instance of {@link NoopCharAppender} so all parser calls in {@link AbstractParser#parseRecord()} to {@link ParserOutput#appender} will do nothing. *

Selected fields (given by {@link CommonParserSettings}) will receive a functional {@link CharAppender}. */ private final CharAppender[] appenders; protected final CommonParserSettings settings; private final boolean skipEmptyLines; private final String nullValue; /** *

The appender available to parsers for accumulating characters read from the input. *

This attribute is assigned to different instances of CharAppender during parsing process, namely, * a (potentially) different CharAppender for each parsed column, taken from * {@link ParserOutput#appenders}[{@link ParserOutput#column}] */ public CharAppender appender; private final CharAppender appenderInstance; private boolean columnsToExtractInitialized; private boolean columnsReordered; private boolean columnReorderingEnabledSetting; private String[] headerStrings; private NormalizedString[] headers; private int[] selectedIndexes; private long currentRecord; public boolean trim = false; public final Deque pendingRecords = new LinkedList(); /** * Headers parsed from the input when {@link CommonParserSettings#headerExtractionEnabled} is {@code true}, * irrespective of any user-provided headers in {@link CommonParserSettings#getHeaders()} */ String[] parsedHeaders; private final AbstractParser parser; /** * Initializes the ParserOutput with the configuration specified in {@link CommonParserSettings} * * @param settings the parser configuration */ public ParserOutput(CommonParserSettings settings) { this(null, settings); } /** * Initializes the ParserOutput with the configuration specified in {@link CommonParserSettings} * * @param parser the parser whose output will be managed by this class. * @param settings the parser configuration */ public ParserOutput(AbstractParser parser, CommonParserSettings settings) { this.parser = parser; this.appenderInstance = settings.newCharAppender(); this.appender = appenderInstance; this.parsedValues = new String[settings.getMaxColumns()]; this.appenders = new CharAppender[settings.getMaxColumns() + 1]; Arrays.fill(appenders, appender); this.settings = settings; this.skipEmptyLines = settings.getSkipEmptyLines(); this.nullValue = settings.getNullValue(); this.columnsToExtractInitialized = false; this.currentRecord = 0; if (settings.isHeaderExtractionEnabled() && parser != null) { parser.ignoreTrailingWhitespace = false; parser.ignoreLeadingWhitespace = false; } if (settings.getHeaders() != null) { initializeHeaders(); } this.columnReorderingEnabledSetting = settings.isColumnReorderingEnabled(); } protected void initializeHeaders() { columnsReordered = false; selectedIndexes = null; this.appender = appenderInstance; Arrays.fill(appenders, appender); if (column > 0) { //we only initialize headers from a parsed row if it is not empty parsedHeaders = new String[column]; System.arraycopy(parsedValues, 0, parsedHeaders, 0, column); } boolean usingParsedHeaders = false; this.headers = NormalizedString.toIdentifierGroupArray(settings.getHeaders()); if (headers != null) { headers = headers.clone(); } else if (column > 0) { //we only initialize headers from a parsed row if it is not empty headers = NormalizedString.toIdentifierGroupArray(parsedHeaders.clone()); usingParsedHeaders = true; } if (parser != null) { parser.ignoreTrailingWhitespace = settings.getIgnoreTrailingWhitespaces(); parser.ignoreLeadingWhitespace = settings.getIgnoreLeadingWhitespaces(); if (usingParsedHeaders) { parser.initialize(); } } if (usingParsedHeaders) { for (int i = 0; i < headers.length; i++) { NormalizedString header = headers[i]; if (header != null && !header.isLiteral()) { if (settings.getIgnoreLeadingWhitespaces()) { if (settings.getIgnoreTrailingWhitespaces()) { headers[i] = NormalizedString.valueOf(headers[i].toString().trim()); } else { headers[i] = NormalizedString.valueOf(ArgumentUtils.trim(headers[i].toString(), true, false)); } } else if (settings.getIgnoreTrailingWhitespaces()) { headers[i] = NormalizedString.valueOf(ArgumentUtils.trim(headers[i].toString(), false, true)); } } } } columnsToExtractInitialized = true; initializeColumnsToExtract(headers); } /** * Gets all values parsed in the {@link ParserOutput#parsedValues} array * * @return the sequence of parsed values in a record. */ public String[] rowParsed() { if (!pendingRecords.isEmpty()) { return pendingRecords.poll(); } // some values were parsed. Let's return them if (column > 0) { // identifies selected columns and headers (in the first non-empty row) if (!columnsToExtractInitialized) { initializeHeaders(); //skips the header row. We want to use the headers defined in the settings. if (settings.isHeaderExtractionEnabled()) { Arrays.fill(parsedValues, null); column = 0; this.appender = appenders[0]; return null; } else if (!columnsReordered && selectedIndexes != null) { String[] out = new String[column]; for (int i = 0; i < selectedIndexes.length; i++) { int index = selectedIndexes[i]; if (index < column) { out[index] = parsedValues[index]; } } column = 0; return out; } } currentRecord++; if (columnsReordered) { if (selectedIndexes.length == 0) { column = 0; return ArgumentUtils.EMPTY_STRING_ARRAY; } String[] reorderedValues = new String[selectedIndexes.length]; for (int i = 0; i < selectedIndexes.length; i++) { int index = selectedIndexes[i]; if (index >= column || index == -1) { reorderedValues[i] = nullValue; } else { reorderedValues[i] = parsedValues[index]; } } column = 0; this.appender = appenders[0]; return reorderedValues; } else { int last = columnReorderingEnabledSetting ? column : column < headers.length ? headers.length : column; String[] out = new String[last]; System.arraycopy(parsedValues, 0, out, 0, column); column = 0; this.appender = appenders[0]; return out; } } else if (!skipEmptyLines) { //no values were parsed, but we are not skipping empty lines if (!columnsToExtractInitialized) { initializeHeaders(); } currentRecord++; if (columnsReordered) { if (selectedIndexes.length == 0) { return ArgumentUtils.EMPTY_STRING_ARRAY; } String[] out = new String[selectedIndexes.length]; Arrays.fill(out, nullValue); return out; } return new String[]{nullValue}; } // no values were parsed and we do not care about empty lines. return null; } FieldSelector getFieldSelector() { return settings.getFieldSelector(); } /** * Initializes the sequence of selected fields, if any. * * @param values a sequence of values that represent the headers of the input. This can be either a parsed record or the headers as defined in {@link CommonSettings#getHeaders()} */ private void initializeColumnsToExtract(NormalizedString[] values) { FieldSelector selector = settings.getFieldSelector(); if (selector != null) { selectedIndexes = selector.getFieldIndexes(values); if (selectedIndexes != null) { Arrays.fill(appenders, NoopCharAppender.getInstance()); for (int i = 0; i < selectedIndexes.length; i++) { int index = selectedIndexes[i]; if (index != -1) { appenders[index] = appender; } } columnsReordered = settings.isColumnReorderingEnabled(); int length = values == null ? selectedIndexes.length : values.length; if (!columnsReordered && length < appenders.length && !(selector instanceof FieldIndexSelector)) { Arrays.fill(appenders, length, appenders.length, appender); } appender = appenders[0]; } } } public String[] getHeaderAsStringArray() { if (headerStrings == null) { headerStrings = NormalizedString.toArray(getHeaders()); } return headerStrings; } /** * Returns the sequence of values that represent the headers each field in the input. This can be either a parsed record or the headers as defined in {@link CommonSettings#getHeaders()} * * @return the headers each field in the input */ public NormalizedString[] getHeaders() { if (parser != null) { parser.extractHeadersIfRequired(); } if (this.headers == null) { this.headers = NormalizedString.toIdentifierGroupArray(settings.getHeaders()); } return this.headers; } /** * Returns the selected indexes of all fields as defined in {@link CommonSettings}. Null if no fields were selected. * * @return the selected indexes of all fields as defined in {@link CommonSettings}. Null if no fields were selected. */ public int[] getSelectedIndexes() { return this.selectedIndexes; } /** * Indicates whether fields selected using the field selection methods (in {@link CommonSettings}) are being reordered. * * @return

false if no fields were selected or column reordering has been disabled in {@link CommonParserSettings#isColumnReorderingEnabled()} *

true if fields were selected and column reordering has been enabled in {@link CommonParserSettings#isColumnReorderingEnabled()} */ public boolean isColumnReorderingEnabled() { return columnsReordered; } /** * Returns the position of the current parsed value * * @return the position of the current parsed value */ public int getCurrentColumn() { return column; } /** * Adds a nullValue (as specified in {@link CommonSettings#getNullValue()}) to the output and prepares the next position in the record to receive more values. */ public void emptyParsed() { this.parsedValues[column++] = nullValue; this.appender = appenders[column]; } /** * Adds the accumulated value in the appender object to the output and prepares the next position in the record to receive more values. */ public void valueParsed() { if (trim) { appender.updateWhitespace(); } this.parsedValues[column++] = appender.getAndReset(); this.appender = appenders[column]; } /** * Adds a value processed externally to the output and prepares the next position in the record to receive more values * * @param value the value to be added to the current record position. */ public void valueParsed(String value) { this.parsedValues[column++] = value; this.appender = appenders[column]; } /** * Returns the current record index. The number returned here reflects the number of actually parsed and valid records sent to the output of {@link ParserOutput#rowParsed}. * * @return the current record index. */ public long getCurrentRecord() { return currentRecord; } /** * Discards the values parsed so far */ public final void discardValues() { column = 0; this.appender = appenders[0]; } /** * Resets the parser output and prepares for a new parsing process. */ final void reset() { this.columnsToExtractInitialized = false; this.currentRecord = 0; this.column = 0; this.headers = null; this.headerStrings = null; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ParsingContext.java000077500000000000000000000134621400120543400312600ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; import com.univocity.parsers.common.processor.*; import java.util.*; /** * Parsing context information available to instances of {@link RowProcessor}. * *

The ParsingContext can be used to control and to obtain information about the parsing process. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see RowProcessor * @see DefaultParsingContext */ public interface ParsingContext extends Context { /** * Returns the file headers that identify each parsed record. * *

If the headers are extracted from the input (i.e. {@link CommonParserSettings#isHeaderExtractionEnabled()} == true), then these values will be returned. *

If no headers are extracted from the input, then the configured headers in {@link CommonSettings#getHeaders()} will be returned. * Note that the user-provided headers will override the header list parsed from the input, if any. To obtain the * original list of headers found in the input use {@link ParsingContext#parsedHeaders()} * * @return the headers used to identify each record parsed from the input. * * @see com.univocity.parsers.common.CommonParserSettings * @see com.univocity.parsers.common.CommonSettings */ String[] headers(); /** * Returns the indexes of each field extracted from the input when fields are selected in the parser settings (i.e. using {@link CommonSettings#selectFields} and friends). * *

The indexes are relative to their original position in the input. *

For example, if the input has the fields "A, B, C, D", and the selected fields are "A, D", then the extracted field indexes will return [0, 3] * *

If no fields were selected, then this method will return null. This means all fields are being parsed. * * @return The indexes of each selected field; null if no fields were selected. * * @see com.univocity.parsers.common.CommonSettings */ int[] extractedFieldIndexes(); /** * Indicates whether selected fields (using {@link CommonSettings#selectFields} and friends) are being reordered. * *

If columns are reordered, each parsed record will contain values only for the selected fields, as specified by {@link CommonParserSettings#isColumnReorderingEnabled} * * @return true if the parsed records are being reordered by the parser, false otherwise * * @see com.univocity.parsers.common.CommonParserSettings * @see com.univocity.parsers.common.CommonSettings */ boolean columnsReordered(); /** * Returns the current line of text being processed by the parser * * @return current line of text being processed by the parser */ long currentLine(); /** * Returns the index of the last char read from the input so far. * * @return the index of the last char read from the input so far. */ long currentChar(); /** * Skips a given number of lines from the current position. * * @param lines the number of lines to be skipped. */ void skipLines(long lines); /** * Returns the headers parsed from the input, if and only if {@link CommonParserSettings#headerExtractionEnabled} is {@code true}. * The result of this method won't return the list of headers manually set by the user in {@link CommonParserSettings#getHeaders()}. * Use the {@link #headers()} method instead to obtain the headers actually used by the parser. * * @return the headers parsed from the input, when {@link CommonParserSettings#headerExtractionEnabled} is {@code true}. */ String[] parsedHeaders(); /** * Returns a String with the input character sequence parsed to produce the current record. * * @return the text content parsed for the current input record. */ String currentParsedContent(); /** * Returns the length of the character sequence parsed to produce the current record. * @return the length of the text content parsed for the current input record */ int currentParsedContentLength(); /** * Returns a String with the input character sequence accumulated on a field before {@link TextParsingException} occurred. * * @return the text content parsed for the current field of the current input record at the time of the error. */ String fieldContentOnError(); /** * Returns all comments collected by the parser so far. * An empty map will be returned if {@link CommonParserSettings#isCommentCollectionEnabled()} evaluates to {@code false}. * * @return a map containing the line numbers and comments found in each. */ Map comments(); /** * Returns the last comment found in the input. * {@code null} will be returned if {@link CommonParserSettings#isCommentCollectionEnabled()} is evaluated to {@code false}. * * @return the last comment found in the input. */ String lastComment(); /** * Returns the line separator characters used to separate individual records when parsing. This could be the line * separator defined in the {@link Format#getLineSeparator()} configuration, or the line separator sequence * identified automatically when {@link CommonParserSettings#isLineSeparatorDetectionEnabled()} evaluates to {@code true}. * * @return the line separator sequence. Might contain one or two characters. */ char[] lineSeparator(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ParsingContextSnapshot.java000066400000000000000000000052101400120543400327650ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.util.*; /** * A snapshot of a {@link ParsingContext} which retains copies of variable attributes of a given {@link ParsingContext} to * store the state of the parsing process at a given point in time. All runtime operations such as {@link #stop()} * will still work and affect the current parsing process. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class ParsingContextSnapshot extends ParsingContextWrapper { private final long currentLine; private final long currentChar; private final Map comments; private final String lastComment; private final int currentColumn; private final String currentParsedContent; private final long currentRecord; /** * Creates a snapshot of a given {@link Context} * * @param context the context object whose variable attributes will be copied over. */ public ParsingContextSnapshot(ParsingContext context) { super(context); currentLine = context.currentLine(); currentChar = context.currentChar(); comments = context.comments() == Collections.EMPTY_MAP ? Collections.emptyMap() : Collections.unmodifiableMap(context.comments()); lastComment = context.lastComment(); currentColumn = context.currentColumn(); currentParsedContent = context.currentParsedContent(); currentRecord = context.currentRecord(); } @Override public long currentLine() { return currentLine; } @Override public long currentChar() { return currentChar; } @Override public Map comments() { return comments; } @Override public String lastComment() { return lastComment; } @Override public int currentColumn() { return currentColumn; } @Override public String currentParsedContent() { return currentParsedContent; } @Override public long currentRecord() { return currentRecord; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ParsingContextWrapper.java000066400000000000000000000042551400120543400326160ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common; import com.univocity.parsers.common.record.*; import java.util.*; /** * A simple a wrapper for a {@link ParsingContext}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ParsingContextWrapper extends ContextWrapper implements ParsingContext { /** * Wraps a {@link ParsingContext}. * @param context the parsingContext object to be wrapped. */ public ParsingContextWrapper(ParsingContext context) { super(context); } @Override public long currentLine() { return context.currentLine(); } @Override public long currentChar() { return context.currentChar(); } @Override public void skipLines(long lines) { context.skipLines(lines); } @Override public String currentParsedContent() { return context.currentParsedContent(); } @Override public int currentParsedContentLength() { return context.currentParsedContentLength(); } @Override public Map comments() { return context.comments(); } @Override public String lastComment() { return context.lastComment(); } @Override public String[] parsedHeaders() { return context.parsedHeaders(); } @Override public char[] lineSeparator() { return context.lineSeparator(); } @Override public String fieldContentOnError() { return context.fieldContentOnError(); } @Override public String[] selectedHeaders() { return context.selectedHeaders(); } @Override public Record toRecord(String[] row) { return context.toRecord(row); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ProcessorErrorHandler.java000066400000000000000000000062751400120543400326000ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.common.processor.core.*; /** * The {@code ProcessorErrorHandler} is a callback used by the parser/writer to handle non-fatal {@link DataProcessingException}s that may occur when * processing rows using a {@link Processor} or {@link RowWriterProcessor}. This leaves the responsibility of error handling to the user. If the user does not * rethrow the {@code DataProcessingException}, the parsing/writing process won't stop and will proceed normally. * *

This error handler WILL NOT handle {@code TextParsingException}s or other errors that prevent the parser to reliably extract rows from a given input, * or the writer to proceed writing data.

* *

When parsing, the {@link #handleError(DataProcessingException, Object[], Context)} method will be called only when a valid record has been parsed, but the * subsequent processing executed by a {@link Processor} fails.

* *

When writing, the {@link #handleError(DataProcessingException, Object[], Context)} method will be called only when a using * the {@link AbstractWriter#processRecord(Object)} methods, and {@link RowWriterProcessor} fails to execute.

* * @see RowProcessor * @see RowWriterProcessor * @see DataProcessingException * @see TextParsingException * @see AbstractParser * @see AbstractWriter * @see CommonSettings * @see Context * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface ProcessorErrorHandler { /** * Handles non-fatal instances of {@code DataProcessingException} that are thrown by a {@link Processor} while processing a record parsed from the input, * or from a {@link RowWriterProcessor} when processing records for writing. * * @param error the exception thrown during the processing an input record. Rethrow the error to abort the parsing process. * When parsing, you can also invoke {@link ParsingContext#stop()} to stop the parser silently. * @param inputRow the record that could not be processed. When writing, the original input object (i.e. {@code null}, java bean or object array) will be sent by the writer. * @param context the parsing context with information about the state of the parser at the time the error occurred. Will be null when writing. */ void handleError(DataProcessingException error, Object[] inputRow, T context); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/ResultIterator.java000066400000000000000000000024171400120543400312730ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.util.*; /** * An {@link Iterator} that provides the current parsing context * through the {@link #getContext()} method * * @author Univocity Software Pty Ltd - dev@univocity.com */ public interface ResultIterator extends Iterator { /** * Returns the current parsing {@link Context}, if available * * @return the contextual object with information about an ongoing parsing process */ C getContext(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/RetryableErrorHandler.java000066400000000000000000000105231400120543400325410ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; /** * A {@link ProcessorErrorHandler} implementation that gives the user a chance to provide a default value for * columns that could not be processed due to an exception, through the method {@link #setDefaultValue(Object)}. * This must be called from within the implementation of the * {@link #handleError(DataProcessingException, Object[], Context)} method, and will prevent the record from being * discarded. The value provided by the user will be assigned to the problematic input row, at the column defined by * {@link DataProcessingException#getColumnIndex()}. * * NOTE:If the column index is {@code < 0}, then the record can't be * salvaged and it will be discarded regardless of the user calling {@link #setDefaultValue(Object)} or not. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see DataProcessingException * @see TextParsingException * @see AbstractParser * @see AbstractWriter * @see CommonSettings * @see Context */ public abstract class RetryableErrorHandler implements ProcessorErrorHandler { private Object defaultValue; private boolean skipRecord = true; /** * Assigns a default value to be assigned to the problematic column that raised the current {@link DataProcessingException}. * * The current column is available from the exception itself, i.e. {@link DataProcessingException#getColumnIndex()}. * * NOTE:If the column index is {@code < 0}, then the record can't be * salvaged and it will be discarded regardless of the user calling {@link #setDefaultValue(Object)} or not. * * @param defaultValue the value to be used for the current column. It will be discarded after handling the current * {@link DataProcessingException}. */ public final void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; keepRecord(); } /** * Ignores the {@link DataProcessingException} and instructs the parser/writer to continue processing the record. */ public final void keepRecord(){ skipRecord = false; } /** * Returns the default value to be assigned to the problematic column that raised the current {@link DataProcessingException}. * * The current column is available from the exception itself, i.e. {@link DataProcessingException#getColumnIndex()}. * * NOTE:If the column index is {@code < 0}, then the record can't be * salvaged and it will be discarded regardless of the user calling {@link #setDefaultValue(Object)} or not. * * @return the value to be used for the current column. It will be discarded after handling the current * {@link DataProcessingException}. */ public final Object getDefaultValue() { return defaultValue; } /** * Prepares this error handler to be executed. This is called automatically by the library prior to invoking * method {@link #handleError(DataProcessingException, Object[], Context)} */ final void prepareToRun() { skipRecord = true; defaultValue = null; } /** * Flag indicating whether the current record will be skipped. Returns {@code true} by default unless * the user invokes {@link #setDefaultValue(Object)} from within the {@link #handleError(DataProcessingException, Object[], Context)} * method implementation, in which case the current record will continue to be processed. * * @return {@code true} if the record originating the current {@link DataProcessingException} should be skipped, * otherwise {@code false} */ public final boolean isRecordSkipped() { return skipRecord; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/RowProcessorErrorHandler.java000066400000000000000000000044701400120543400332630ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.common.processor.*; /** * The {@code RowProcessorErrorHandler} is a callback used by the parser/writer to handle non-fatal {@link DataProcessingException}s that may occur when * processing rows using a {@link RowProcessor} or {@link RowWriterProcessor}. This leaves the responsibility of error handling to the user. If the user does not * rethrow the {@code DataProcessingException}, the parsing/writing process won't stop and will proceed normally. * *

This error handler WILL NOT handle {@code TextParsingException}s or other errors that prevent the parser to reliably extract rows from a given input, * or the writer to proceed writing data.

* *

When parsing, the {@link #handleError(DataProcessingException, Object[], Context)} method will be called only when a valid record has been parsed, but the * subsequent processing executed by a {@link RowProcessor} fails.

* *

When writing, the {@link #handleError(DataProcessingException, Object[], Context)} method will be called only when a using * the {@link AbstractWriter#processRecord(Object)} methods, and {@link RowWriterProcessor} fails to execute.

* * @see RowProcessor * @see RowWriterProcessor * @see DataProcessingException * @see TextParsingException * @see AbstractParser * @see AbstractWriter * @see CommonSettings * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface RowProcessorErrorHandler extends ProcessorErrorHandler { } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/StringCache.java000066400000000000000000000115221400120543400304720ustar00rootroot00000000000000/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import java.lang.ref.*; import java.util.*; import java.util.concurrent.*; /** * A simple cache of values associated with strings. It is built to simply prevent generating the same value over * and over again over a short period of time. Once its size limit is reached, the cache will be fully cleared. * Do not use this as a general purpose caching solution. This meant for storing values that can be cheaply produced * and re-generating them every now and then won't incur in any major performance impact. * * @param the type of entry to be stored in the cache */ public abstract class StringCache { private static final int DEFAULT_SIZE_LIMIT = 16384; private static final int DEFAULT_MAX_STRING_LENGTH = 0; private final Map> stringCache = new ConcurrentHashMap>(); private int sizeLimit = DEFAULT_SIZE_LIMIT; private int maxStringLength = DEFAULT_MAX_STRING_LENGTH; /** * Converts a given string to a value * * @param input the input to be converted and stored in the cache * @return the value generated from the given string/ */ protected abstract T process(String input); /** * Tests whether the cache contains the given key * * @param input a string that might have a value associated to it. * @return {@code true} if the cache contains (or contained) a value associated with the given key. */ public boolean containsKey(String input) { return stringCache.containsKey(input); } /** * Returns the size limit of this string cache. Defaults to 16,384. For simplicity, when * this limit is reached, the entire cache is cleared. * * @return the maximum number of entries that can be stored in this string cache. */ public int getSizeLimit() { return sizeLimit; } /** * Defines the size limit of this string cache (16,384 by default). For simplicity, when * this limit is reached, the entire cache is cleared. * * @param sizeLimit the maximum number of entries that can be stored in this string cache. */ public void setSizeLimit(int sizeLimit) { if (sizeLimit <= 0) { sizeLimit = DEFAULT_SIZE_LIMIT; } this.sizeLimit = sizeLimit; } /** * Associates a value to a string * * @param input the string to be associated with a given value * @param value the value associated with the given string */ public void put(String input, T value) { if (input == null || input.length() > maxStringLength) { return; } if (stringCache.size() >= sizeLimit) { stringCache.clear(); } stringCache.put(input, new SoftReference(value)); } /** * Returns the value associated with the given string. If it doesn't exist, * or if it has been evicted, a value will be populated using {@link #process(String)} * * @param input the string whose associated value will be returned * @return the value associated with the given string. */ public T get(String input) { if (input == null || (maxStringLength > 0 && input.length() > maxStringLength)) { return null; } SoftReference ref = stringCache.get(input); T out; if (ref == null || ref.get() == null) { out = process(input); ref = new SoftReference(out); stringCache.put(input, ref); } else { out = ref.get(); } return out; } /** * Removes all entries stored in this cache. */ public void clear() { stringCache.clear(); } /** * Returns the maximum length a {@code String} key can have to be used as a key in this cache. * If the {@code String} length exceeds this limit, the value associated with it won't be cached. * Defaults to 1024 * * @return the maximum length a {@code String} key can have */ public int getMaxStringLength() { return maxStringLength; } /** * Returns the maximum length a {@code String} key can have to be used as a key in this cache. * If the {@code String} length exceeds this limit, the value associated with it won't be cached. * Defaults to 1024 * * @param maxStringLength the maximum length a {@code String} key can have */ public void setMaxStringLength(int maxStringLength) { this.maxStringLength = maxStringLength; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/TextParsingException.java000066400000000000000000000126151400120543400324330ustar00rootroot00000000000000/** * **************************************************************************** * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the * License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. * **************************************************************************** */ package com.univocity.parsers.common; /** * Exception type used provide information about any issue that might happen while parsing from a given input. *

It generally provides location information about where in the input a parsing error occurred. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class TextParsingException extends AbstractException { private static final long serialVersionUID = 1410975527141918214L; private long lineIndex; private long charIndex; private long recordNumber; private int columnIndex; private String content; private String[] headers; protected int[] extractedIndexes; /** * Creates a new exception with information about an error that occurred when parsing some input. * * @param context the context of the parser when an error occurred * @param message message with details about the error * @param cause the cause of the error */ public TextParsingException(Context context, String message, Throwable cause) { super(message, cause); setContext(context); } protected void setContext(Context context) { if (context instanceof ParsingContext) { setParsingContext((ParsingContext) context); } else { setParsingContext(null); } this.columnIndex = context == null ? -1 : context.currentColumn(); this.recordNumber = context == null ? -1L : context.currentRecord(); if (this.headers == null) { this.headers = context == null ? null : context.headers(); } this.extractedIndexes = context == null ? null : context.extractedFieldIndexes(); } private void setParsingContext(ParsingContext parsingContext) { this.lineIndex = parsingContext == null ? -1L : parsingContext.currentLine(); this.charIndex = parsingContext == null ? '\0' : parsingContext.currentChar(); this.content = parsingContext == null ? null : parsingContext.fieldContentOnError(); } /** * Creates a new exception with information about an error that occurred when parsing some input. * * @param context the context of the parser when an error occurred * @param message message with details about the error */ public TextParsingException(ParsingContext context, String message) { this(context, message, null); } /** * Creates a new exception with information about an error that occurred when parsing some input. * * @param context the context of the parser when an error occurred * @param cause the cause of the error */ public TextParsingException(ParsingContext context, Throwable cause) { this(context, cause != null ? cause.getMessage() : null, cause); } /** * Creates a new exception with information about an error that occurred when parsing some input. * * @param context the context of the parser when an error occurred */ public TextParsingException(ParsingContext context) { this(context, null, null); } @Override protected String getErrorDescription() { return "Error parsing input"; } @Override protected String getDetails() { String details = ""; details = printIfNotEmpty(details, "line", lineIndex); details = printIfNotEmpty(details, "column", columnIndex); details = printIfNotEmpty(details, "record", recordNumber); details = charIndex == 0 ? details : printIfNotEmpty(details, "charIndex", charIndex); details = printIfNotEmpty(details, "headers", headers); details = printIfNotEmpty(details, "content parsed", restrictContent(content)); return details; } /** * Returns the record number when the exception occurred. * * @return the record number when the exception occurred. */ public long getRecordNumber() { return recordNumber; } /** * Returns the column index where the exception occurred. * * @return the column index where the exception occurred. */ public int getColumnIndex() { return columnIndex; } /** * Returns the line number where the exception occurred. * * @return the line number where the exception occurred. */ public long getLineIndex() { return lineIndex; } /** * Returns the location of the last character read from before the error occurred. * * @return the location of the last character read from before the error occurred. */ public long getCharIndex() { return charIndex; } /** * Returns the last chunk of content parsed before the error took place * * @return the last chunk of content parsed before the error took place */ public final String getParsedContent() { if (errorContentLength == 0) { return null; } return content; } /** * Returns the headers processed from the input, if any. * * @return the headers processed from the input, if any. */ public final String[] getHeaders() { return headers; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/TextWritingException.java000066400000000000000000000132541400120543400324530ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; /** * Exception type used provide information about any issue that might happen while writing to a given output. * *

It generally provides location and data information in case of a writing failure. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class TextWritingException extends AbstractException { private static final long serialVersionUID = 7198462597717255519L; private final long recordCount; private final Object[] recordData; private final String recordCharacters; /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param message message with details about the error * @param recordCount the number of records written until the error occurred * @param row the input row that was being written when the error occurred * @param recordCharacters the characters already written to the output record. * @param cause the cause of the error */ private TextWritingException(String message, long recordCount, Object[] row, String recordCharacters, Throwable cause) { super(message, cause); this.recordCount = recordCount; this.recordData = row; this.recordCharacters = recordCharacters; } /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param message message with details about the error * @param recordCount the number of records written until the error occurred * @param recordCharacters the characters already written to the output record. * @param cause the cause of the error */ public TextWritingException(String message, long recordCount, String recordCharacters, Throwable cause) { this(message, recordCount, null, recordCharacters, cause); } /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param message message with details about the error * @param recordCount the number of records written until the error occurred * @param row the input row that was being written when the error occurred * @param cause the cause of the error */ public TextWritingException(String message, long recordCount, Object[] row, Throwable cause) { this(message, recordCount, row, null, cause); } /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param message message with details about the error */ public TextWritingException(String message) { this(message, 0, null, null, null); } /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param cause the cause of the error */ public TextWritingException(Throwable cause) { this(cause != null ? cause.getMessage() : null, 0, null, null, cause); } /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param message message with details about the error * @param line index of the line being written to the output when the error occurred * @param row the input row that was being written when the error occurred */ public TextWritingException(String message, long line, Object[] row) { this(message, line, row, null); } /** * Creates a new exception with information about an error that occurred when writing data to some output. * * @param message message with details about the error * @param line index of the line being written to the output when the error occurred * @param recordCharacters the characters already written to the output record. */ public TextWritingException(String message, long line, String recordCharacters) { this(message, line, null, recordCharacters, null); } /** * Returns the number of records written before the exception occurred. * * @return the number of records written before the exception occurred. */ public long getRecordCount() { return recordCount; } /** * Returns the data that failed to be written * * @return the data that failed to be written */ public Object[] getRecordData() { return restrictContent(recordData); } /** * Returns the character data that failed to be written * * @return the character data that failed to be written */ public String getRecordCharacters() { if(errorContentLength == 0){ return null; } return recordCharacters; } @Override protected String getDetails() { String details = ""; details = printIfNotEmpty(details, "recordCount", recordCount); details = printIfNotEmpty(details, "recordData", restrictContent(recordData)); details = printIfNotEmpty(details, "recordCharacters", restrictContent(recordCharacters)); return details; } @Override protected String getErrorDescription() { return "Error writing data"; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/beans/000077500000000000000000000000001400120543400265245ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/beans/BeanHelper.java000066400000000000000000000106451400120543400314020ustar00rootroot00000000000000/* * Copyright (c) 2015. Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.beans; import java.lang.ref.*; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.*; /** * Helper class used to obtain property descriptors from annotated java beans whose values are set via reflection. * This class was implemented to eliminate direct compile-time dependency with {@link java.beans.Introspector} and * other classes in the {@code java.beans.*} package. This is required to allow Android developers to use univocity-parsers. * Android developers should add have openbeans-1.0.jar * in their classpath to be able to use univocity-parsers. * * When available, the classes from package {@code com.googlecode.openbeans.*} will be used, otherwise the * bean introspection classes classes from {@code java.beans.*} package will be loaded. * * If everything fails, then the parser will try to manipulate fields in annotated java beans directly, instead * of using their getters and setters. */ public final class BeanHelper { private static final PropertyWrapper[] EMPTY = new PropertyWrapper[0]; private static final Class introspectorClass = findIntrospectorImplementationClass(); private static final Method beanInfoMethod = getBeanInfoMethod(); private static final Method propertyDescriptorMethod = getMethod("getPropertyDescriptors", beanInfoMethod, false); static Method PROPERTY_WRITE_METHOD = getMethod("getWriteMethod", propertyDescriptorMethod, true); static Method PROPERTY_READ_METHOD = getMethod("getReadMethod", propertyDescriptorMethod, true); static Method PROPERTY_NAME_METHOD = getMethod("getName", propertyDescriptorMethod, true); private static final Map, WeakReference> descriptors = new ConcurrentHashMap, WeakReference>(); private BeanHelper() { } /** * Returns the property descriptors of all properties available from a class * @param beanClass the class whose property descriptors should be returned * @return an array of all property descriptors of the given class. Might be empty. */ public static PropertyWrapper[] getPropertyDescriptors(Class beanClass) { if (propertyDescriptorMethod == null) { return EMPTY; } PropertyWrapper[] out = null; WeakReference reference = descriptors.get(beanClass); if (reference != null) { out = reference.get(); } if (out == null) { try { Object beanInfo = beanInfoMethod.invoke(null, beanClass, Object.class); Object[] propertyDescriptors = (Object[]) propertyDescriptorMethod.invoke(beanInfo); out = new PropertyWrapper[propertyDescriptors.length]; for (int i = 0; i < propertyDescriptors.length; i++) { out[i] = new PropertyWrapper(propertyDescriptors[i]); } } catch (Exception ex) { out = EMPTY; } descriptors.put(beanClass, new WeakReference(out)); } return out; } private static Class findIntrospectorImplementationClass() { try { return Class.forName("com.googlecode.openbeans.Introspector"); } catch (Throwable e1) { try { return Class.forName("java.beans.Introspector"); } catch (Throwable e2) { return null; } } } private static Method getBeanInfoMethod() { if (introspectorClass == null) { return null; } try { return introspectorClass.getMethod("getBeanInfo", Class.class, Class.class); } catch (Throwable e) { return null; } } private static Method getMethod(String methodName, Method method, boolean fromComponentType) { if (method == null) { return null; } try { Class returnType = method.getReturnType(); if (fromComponentType) { returnType = returnType.getComponentType(); } return returnType.getMethod(methodName); } catch (Exception ex) { return null; } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/beans/PropertyWrapper.java000066400000000000000000000055261400120543400325640ustar00rootroot00000000000000/* * Copyright (c) 2015. Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.beans; import java.lang.reflect.*; /** * Wrapper for a implementation of PropertyDescriptor from either {@code java.beans.PropertyDescriptor} * or {@code com.googlecode.openbeans.PropertyDescriptor}. * * Used to eliminate compile-time dependencies with package {@code java.beans.*} which is not * available to Android developers. */ public final class PropertyWrapper { private static final Method NO_METHOD = getNullMethod(); private static final String NO_NAME = "!!NO_NAME!!"; private final Object propertyDescriptor; private Method writeMethod; private Method readMethod; private String name; PropertyWrapper(Object propertyDescriptor) { this.propertyDescriptor = propertyDescriptor; } /** * Returns the method that should be used to write a value to a property of a Java bean. * Might be {@code null}. * * @return The method that should be used to write the property value, if available. **/ public final Method getWriteMethod() { if (writeMethod == null) { writeMethod = (Method) invoke(propertyDescriptor, BeanHelper.PROPERTY_WRITE_METHOD); } return writeMethod == NO_METHOD ? null : writeMethod; } /** * Returns the method that should be used to read the value of a property of a Java bean. * Might be {@code null}. * * @return The method that should be used to read the property value, if available. */ public final Method getReadMethod() { if (readMethod == null) { readMethod = (Method) invoke(propertyDescriptor, BeanHelper.PROPERTY_READ_METHOD); } return readMethod == NO_METHOD ? null : readMethod; } /** * Returns the name of a property of a Java bean. * Might be {@code null}. * * @return The property name. */ public final String getName() { if (name == null) { name = (String) invoke(propertyDescriptor, BeanHelper.PROPERTY_NAME_METHOD); } return name == NO_NAME ? null : name; } private static Object invoke(Object propertyDescriptor, Method method) { try { return method.invoke(propertyDescriptor); } catch (Exception ex) { return null; } } private static Method getNullMethod() { try { return Object.class.getMethod("hashCode"); } catch (NoSuchMethodException e) { // should never happen. return null; } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/000077500000000000000000000000001400120543400267025ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/AbstractColumnMapping.java000066400000000000000000000161231400120543400340050ustar00rootroot00000000000000/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.annotations.helpers.*; import java.util.*; /** * Basic support operations for mapping attributes/methods to columns in a {@link ColumnMapper} * @param the type of key (attribute/method names or specific method definition with parameter and return types) */ abstract class AbstractColumnMapping implements Cloneable { final String prefix; Map mapping; /** * Creates a mapping with a prefix. * * @param prefix a dot separated sequence of names that represents the nesting of complex attributes inside a class (e.g customer.contact.phone). * @param parent the parent mapping of columns, relevant only when nested objects' attributes or methods are being mapped. */ AbstractColumnMapping(String prefix, AbstractColumnMapping parent) { if (parent != null) { mapping = parent.mapping; this.prefix = parent.prefix.isEmpty() ? prefix : parent.prefix + '.' + prefix; } else { mapping = new LinkedHashMap(); this.prefix = prefix; } } /** * Maps a attribute or method to a column name * @param key attribute/method name or specific method definition with parameter and return type * @param columnName name of column associated with the given key */ void mapToColumnName(K key, String columnName) { mapping.put(key, columnName); } /** * Maps a attribute or method to a column name * @param key attribute/method name or specific method definition with parameter and return type * @param column enumeration representing the column associated with the given key */ void mapToColumn(K key, Enum column) { mapping.put(key, column); } /** * Maps a attribute or method to a column name * @param key attribute/method name or specific method definition with parameter and return type * @param columnIndex number representing the position of the column associated with the given key */ void mapToColumnIndex(K key, int columnIndex) { mapping.put(key, columnIndex); } /** * Maps multiple attributes or methods to multiple column names * @param mappings the mappings to be added */ void mapToColumnNames(Map mappings) { mapping.putAll(mappings); } /** * Maps multiple attributes or methods to multiple column names * @param mappings the mappings to be added */ void mapToColumns(Map> mappings) { mapping.putAll(mappings); } /** * Maps multiple attributes or methods to multiple column names * @param mappings the mappings to be added */ void mapToColumnIndexes(Map mappings) { mapping.putAll(mappings); } /** * Tests whether a given attribute or method is mapped to a column * @param key the attribute or method name/descriptor * @return {@code true} if the key is mapped. */ boolean isMapped(K key) { return getMappedColumn(key) != null; } /** * Transforms the key so it can work with the given prefix. * * @param prefix the current object nesting level, denoted by a dot-separated string of nested attribute names. * @param key the key to transform. * @return the transformed key or {@code null} if the key can't be used with the given prefix */ abstract K prefixKey(String prefix, K key); private Object getMappedColumn(K key) { if (key == null) { return null; } key = prefixKey(prefix, key); Object out = mapping.get(key); return out; } /** * Updates the mapping of a attribute/method so a mapped class member can target * a user provided column. * * @param fieldMapping a class member that has should be mapped to a column * @param key the attribute name or method specification that matches with the given field. * * @return {@code true} if the mapping has been successfully updated. */ boolean updateFieldMapping(FieldMapping fieldMapping, K key) { Object mappedColumn = getMappedColumn(key); if (mappedColumn != null) { if (mappedColumn instanceof Enum) { mappedColumn = ((Enum) mappedColumn).name(); } if (mappedColumn instanceof String) { fieldMapping.setFieldName((String) mappedColumn); fieldMapping.setIndex(-1); return true; } else if (mappedColumn instanceof Integer) { fieldMapping.setIndex((Integer) mappedColumn); return true; } throw new IllegalStateException("Unexpected mapping of '" + key + "' to " + mappedColumn); } return false; } /** * Returns all prefixes used by the keys in this mapping. These represent names of * nested objects that will be navigated through to access their attributes/methods. * @param out the set of prefixes to populate. */ void extractPrefixes(Set out) { for (K key : mapping.keySet()) { String keyPrefix = getKeyPrefix(prefix, key); if (keyPrefix != null) { out.add(keyPrefix); } } } /** * Returns the prefix of a given key, i.e. the current nested object that is * being targeted. * * @param prefix the current prefix - a dot separated string with nested attribute names * @param key the attribute name or method definition. If its own prefix starts with the given prefix, the next element after the dot will be returned (if any) * @return the name of the next nested object relative to the current prefix. */ abstract String getKeyPrefix(String prefix, K key); /** * Creates a deep copy of this mapping that is independent from the original. * @return the duplicate of this object. */ public AbstractColumnMapping clone() { try { AbstractColumnMapping out = (AbstractColumnMapping) super.clone(); out.mapping = new LinkedHashMap(mapping); return out; } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } /** * Locates a given key based on an attribute or method name. * * @param nameWithPrefix name of the attribute or method, prefixed with nested object names that identify the path to the target class member. * @return the key formed with the given attribute name or method definition */ abstract K findKey(String nameWithPrefix); /** * Removes any mappings containing keys that have a given attribute or method name. * * @param nameWithPrefix name of the attribute or method, prefixed with nested object names that identify the path to the target class member. */ void remove(String nameWithPrefix) { K key; while ((key = findKey(nameWithPrefix)) != null) { if (mapping.remove(key) == null) { return; } } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/AllIndexesSelector.java000066400000000000000000000032501400120543400332760ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; /** * A FieldSelector that selects all indexes of a record. * * @see FieldSelector * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class AllIndexesSelector implements FieldSelector { @Override public int[] getFieldIndexes(NormalizedString[] headers) { if(headers == null){ return null; } int[] out = new int[headers.length]; for (int i = 0; i < out.length; i++) { out[i] = i; } return out; } @Override public String describe() { return "all fields"; } @Override public Object clone() { try { return super.clone(); }catch (CloneNotSupportedException e){ throw new IllegalStateException(e); } } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/ColumnMapper.java000066400000000000000000000344141400120543400321550ustar00rootroot00000000000000/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.annotations.*; import java.util.*; /** * A utility that allows users to manually define mappings from * attributes/methods of a given class to columns to be parsed or written. * * This removes the requirement of having classes annotated with {@link Parsed} or * {@link Nested}. * * Mappings defined manually take precedence over annotations. * * @see ColumnMapping */ public interface ColumnMapper extends Cloneable { /** * Maps an attribute to a column name. * * @param attributeName the name of the attribute. * Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the attribute "mobile" * from a Contact attribute inside a Customer class. * @param columnName the name of the column that: * (a) when parsing, will be read from to populate the given attribute of an object; * (b) when writing, will receive the value of the given attribute of an object; */ void attributeToColumnName(String attributeName, String columnName); /** * Maps an attribute to a column. * * @param attributeName the name of the attribute. * Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the attribute "mobile" * from a Contact attribute inside a Customer class. * @param column an enumeration representing the column that: * (a) when parsing, will be read from to populate the given attribute of an object; * (b) when writing, will receive the value of the given attribute of an object; */ void attributeToColumn(String attributeName, Enum column); /** * Maps an attribute to a column position. * * @param attributeName the name of the attribute. * Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the attribute "mobile" * from a Contact attribute inside a Customer class. * @param columnIndex the position of the column that: * (a) when parsing, will be read from to populate the given attribute of an object; * (b) when writing, will receive the value of the given attribute of an object; */ void attributeToIndex(String attributeName, int columnIndex); /** * Maps multiple attributes to multiple column names. * * @param mappings a map of attribute names associated with a corresponding column name, where: * *

    *
  • Each key is the name of the attribute. Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the attribute "mobile" * from a Contact attribute inside a Customer class. *
  • *
  • Each value is the name of the column that: * (a) when parsing, will be read from to populate the given attribute of an object; * (b) when writing, will receive the value of the given attribute of an object; *
  • *
*/ void attributesToColumnNames(Map mappings); /** * Maps multiple attributes to multiple columns. * * @param mappings a map of attribute names associated with a corresponding column, where: * *
    *
  • Each key is the name of the attribute. Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the attribute "mobile" * from a Contact attribute inside a Customer class. *
  • *
  • Each value is an enumeration representing the column that: * (a) when parsing, will be read from to populate the given attribute of an object; * (b) when writing, will receive the value of the given attribute of an object; *
  • *
*/ void attributesToColumns(Map> mappings); /** * Maps multiple attributes to multiple column positions. * * @param mappings a map of attribute names associated with a corresponding column, where: * *
    *
  • Each key is the name of the attribute. Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the attribute "mobile" * from a Contact attribute inside a Customer class. *
  • *
  • Each value is an integer representing the position of the column that: * (a) when parsing, will be read from to populate the given attribute of an object; * (b) when writing, will receive the value of the given attribute of an object; *
  • *
*/ void attributesToIndexes(Map mappings); /** * Maps a setter method to a column name. Use when {@link #methodToColumnName(String, String)} * is not enough to uniquely identify the method you need (e.g. when there are * overloaded methods with different parameter types) * * Used only for parsing. Will be ignored when writing. * * @param setterName the name of the setter method. * Use the dot character to access methods of nested objects, * e.g. {@code contact.mobile} will target the setter method "mobile(String)" * from a Contact attribute inside a Customer class. * @param parameterType the type of the parameter used in the given setter name. * @param columnName the name of the column that when parsing, will be read from to invoke given setter method of an object * */ void methodToColumnName(String setterName, Class parameterType, String columnName); /** * Maps a setter method to a column. Use when {@link #methodToColumnName(String, String)} * is not enough to uniquely identify the method you need (e.g. when there are * overloaded methods with different parameter types) * * Used only for parsing. Will be ignored when writing. * * @param setterName the name of the setter method. * Use the dot character to access methods of nested objects, * e.g. {@code contact.mobile} will target the setter method "mobile(String)" * from a Contact attribute inside a Customer class. * @param parameterType the type of the parameter used in the given setter name. * @param column an enumeration representing the column that when parsing, will be read from to invoke given setter method of an object */ void methodToColumn(String setterName, Class parameterType, Enum column); /** * Maps a setter method to a column position. Use when {@link #methodToColumnName(String, String)} * is not enough to uniquely identify the method you need (e.g. when there are * overloaded methods with different parameter types) * * Used only for parsing. Will be ignored when writing. * * @param setterName the name of the setter method. * Use the dot character to access methods of nested objects, * e.g. {@code contact.mobile} will target the setter method "mobile(String)" * from a Contact attribute inside a Customer class. * @param parameterType the type of the parameter used in the given setter name. * @param columnIndex the position of the column that when parsing, will be read from to invoke given setter method of an object */ void methodToIndex(String setterName, Class parameterType, int columnIndex); /** * Maps a method to a column name. * * When parsing, only "setter" methods will be used i.e. the given method accepts one parameter. * If the method is overridden, use {@link #methodToColumnName(String, Class, String)} * to specify the exact parameter type to match the appropriate setter method. * * When writing, only "getter" methods will be used i.e. the given method * doesn't accept any parameters and returns a value. * * @param methodName the name of the method. * Use the dot character to access methods of nested objects, * e.g. {@code contact.mobile} will target the method "mobile(String)" when parsing, or "String mobile()" when writing, * from a Contact attribute inside a Customer class. * @param columnName the name of the column that: * (a) when parsing, will be read from to invoke given setter method of an object; * (b) when writing, will receive the value returned by the given getter method of an object; * */ void methodToColumnName(String methodName, String columnName); /** * Maps a method to a column. * * When parsing, only "setter" methods will be used i.e. the given method accepts one parameter. * If the method is overridden, use {@link #methodToColumnName(String, Class, String)} * to specify the exact parameter type to match the appropriate setter method. * * When writing, only "getter" methods will be used i.e. the given method * doesn't accept any parameters and returns a value. * * @param methodName the name of the method. * Use the dot character to access methods of nested objects, * * e.g. {@code contact.mobile} will target the method "mobile(String)" when parsing, or "String mobile()" when writing, * * from a Contact attribute inside a Customer class. * @param column an enumeration representing the column that: * (a) when parsing, will be read from to invoke given setter method of an object; * (b) when writing, will receive the value returned by the given getter method of an object; * */ void methodToColumn(String methodName, Enum column); /** * Maps a method to a column position. * * When parsing, only "setter" methods will be used i.e. the given method accepts one parameter. * If the method is overridden, use {@link #methodToColumnName(String, Class, String)} * to specify the exact parameter type to match the appropriate setter method. * * When writing, only "getter" methods will be used i.e. the given method * doesn't accept any parameters and returns a value. * * @param methodName the name of the method. * Use the dot character to access methods of nested objects, * * e.g. {@code contact.mobile} will target the method "mobile(String)" when parsing, or "String mobile()" when writing, * * from a Contact attribute inside a Customer class. * @param columnIndex the position of the column that: * (a) when parsing, will be read from to invoke given setter method of an object; * (b) when writing, will receive the value returned by the given getter method of an object; * */ void methodToIndex(String methodName, int columnIndex); /** * Maps multiple methods to multiple column names. * * @param mappings a map of methods names associated with a corresponding column name, where: * *
    *
  • Each key is the name of a method. Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the method "mobile(String)" when parsing, or "String mobile()" when writing, * from a Contact attribute inside a Customer class. *
  • *
  • Each value is the name of the column that: * (a) when parsing, will be read from to invoke given setter method of an object; * (b) when writing, will receive the value returned by the given getter method of an object; *
  • *
*/ void methodsToColumnNames(Map mappings); /** * Maps multiple methods to multiple columns. * * @param mappings a map of methods names associated with a corresponding column, where: * *
    *
  • Each key is the name of a method. Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the method "mobile(String)" when parsing, or "String mobile()" when writing, * from a Contact attribute inside a Customer class. *
  • *
  • Each value is an enumeration representing the column that: * (a) when parsing, will be read from to invoke given setter method of an object; * (b) when writing, will receive the value returned by the given getter method of an object; *
  • *
*/ void methodsToColumns(Map> mappings); /** * Maps multiple methods to multiple column positions. * * @param mappings a map of methods names associated with a corresponding column position, where: * *
    *
  • Each key is the name of a method. Use the dot character to access attributes of nested objects, * e.g. {@code contact.mobile} will target the method "mobile(String)" when parsing, or "String mobile()" when writing, * from a Contact attribute inside a Customer class. *
  • *
  • Each value is an integer representing the position of the column that: * (a) when parsing, will be read from to invoke given setter method of an object; * (b) when writing, will receive the value returned by the given getter method of an object; *
  • *
*/ void methodsToIndexes(Map mappings); /** * Creates a deep copy of this object with all its mappings. Changes to the clone won't affect the original instance. * @return a clone of the current mappings. */ ColumnMapper clone(); /** * Removes any mappings that target a given method or attribute name. * @param methodOrAttributeName the name of the method or attribute to be removed. */ void remove(String methodOrAttributeName); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/ColumnMapping.java000066400000000000000000000212151400120543400323170ustar00rootroot00000000000000/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.annotations.helpers.*; import java.util.*; import static com.univocity.parsers.annotations.helpers.MethodDescriptor.*; /** * Implementation the {@link ColumnMapper} interface which allows * users to manually define mappings from attributes/methods of a given class * to columns to be parsed or written. * * @see ColumnMapper */ public final class ColumnMapping implements ColumnMapper { private class NameMapping extends AbstractColumnMapping { public NameMapping(String prefix, NameMapping parent) { super(prefix, parent); } @Override String prefixKey(String prefix, String key) { if (prefix.isEmpty()) { return key; } else { return prefix + '.' + key; } } @Override String getKeyPrefix(String prefix, String key) { return getCurrentAttributePrefix(prefix, key); } @Override String findKey(String nameWithPrefix) { return nameWithPrefix; } } private class MethodMapping extends AbstractColumnMapping { public MethodMapping(String prefix, MethodMapping parent) { super(prefix, parent); } @Override MethodDescriptor prefixKey(String prefix, MethodDescriptor key) { if (key.getPrefix().equals(prefix)) { return key; } return null; } @Override String getKeyPrefix(String prefix, MethodDescriptor key) { return getCurrentAttributePrefix(prefix, key.getPrefixedName()); } @Override MethodDescriptor findKey(String nameWithPrefix) { for (MethodDescriptor k : this.mapping.keySet()) { if (k.getPrefixedName().equals(nameWithPrefix)) { return k; } } return null; } } private static String getCurrentAttributePrefix(String prefix, String name) { if (!name.startsWith(prefix)) { return null; } int off = prefix.isEmpty() ? 0 : 1; int dot = name.indexOf('.', prefix.length() + off); if (dot != -1) { String attributePrefix = name.substring(prefix.length() + off, dot); return attributePrefix; } return null; } private NameMapping attributeMapping; private NameMapping methodNameMapping; private MethodMapping methodMapping; /** * Creates a new column mapping instance */ public ColumnMapping() { this("", null); } /** * Creates a nested column mapping instance for handling nested attributes. For internal use. * * @param prefix the current nesting path, denoted by a dot separated string of attribute names * @param parent the mappings of the parent object in the nested structure. */ public ColumnMapping(String prefix, ColumnMapping parent) { attributeMapping = new NameMapping(prefix, parent == null ? null : parent.attributeMapping); methodNameMapping = new NameMapping(prefix, parent == null ? null : parent.methodNameMapping); methodMapping = new MethodMapping(prefix, parent == null ? null : parent.methodMapping); } @Override public void attributeToColumnName(String attributeName, String columnName) { attributeMapping.mapToColumnName(attributeName, columnName); } @Override public void attributeToColumn(String attributeName, Enum column) { attributeMapping.mapToColumn(attributeName, column); } @Override public void attributeToIndex(String attributeName, int columnIndex) { attributeMapping.mapToColumnIndex(attributeName, columnIndex); } @Override public void attributesToColumnNames(Map mappings) { attributeMapping.mapToColumnNames(mappings); } @Override public void attributesToColumns(Map> mappings) { attributeMapping.mapToColumns(mappings); } @Override public void attributesToIndexes(Map mappings) { attributeMapping.mapToColumnIndexes(mappings); } private void methodToColumnName(MethodDescriptor method, String columnName) { methodMapping.mapToColumnName(method, columnName); } private void methodToColumn(MethodDescriptor method, Enum column) { methodMapping.mapToColumn(method, column); } private void methodToIndex(MethodDescriptor method, int columnIndex) { methodMapping.mapToColumnIndex(method, columnIndex); } /** * Tests whether a method or attribute has been mapped to a column. * @param method a descriptor of getter/setter methods (can be {@code null}) * @param targetName name of a method or attribute * @return {@code true} if the given method or attribute has been mapped to a column */ public boolean isMapped(MethodDescriptor method, String targetName) { return methodMapping.isMapped(method) || attributeMapping.isMapped(targetName) || methodNameMapping.isMapped(targetName); } /** * Updates the mapping of a attribute/method so a mapped class member can target * a user provided column. * * @param fieldMapping a class member that has should be mapped to a column * @param targetName name of a method or attribute * @param method a descriptor of getter/setter methods (can be {@code null}) * * @return {@code true} if the mapping has been successfully updated. */ public boolean updateMapping(FieldMapping fieldMapping, String targetName, MethodDescriptor method) { if (methodMapping.isMapped(method)) { return methodMapping.updateFieldMapping(fieldMapping, method); } else if (attributeMapping.isMapped(targetName)) { return attributeMapping.updateFieldMapping(fieldMapping, targetName); } else if (methodNameMapping.isMapped(targetName)) { return methodNameMapping.updateFieldMapping(fieldMapping, targetName); } return false; } /** * Returns object the nesting path associated with the current mapping. * @return a dot separated string of nested attribute names */ public String getPrefix() { return methodMapping.prefix; } @Override public void methodToColumnName(String methodName, String columnName) { methodNameMapping.mapToColumnName(methodName, columnName); } @Override public void methodToColumn(String methodName, Enum column) { methodNameMapping.mapToColumn(methodName, column); } @Override public void methodToIndex(String methodName, int columnIndex) { methodNameMapping.mapToColumnIndex(methodName, columnIndex); } @Override public void methodsToColumnNames(Map mappings) { methodNameMapping.mapToColumnNames(mappings); } @Override public void methodsToColumns(Map> mappings) { methodNameMapping.mapToColumns(mappings); } @Override public void methodsToIndexes(Map mappings) { methodNameMapping.mapToColumnIndexes(mappings); } public void remove(String methodOrAttributeName) { attributeMapping.remove(methodOrAttributeName); methodNameMapping.remove(methodOrAttributeName); methodMapping.remove(methodOrAttributeName); } @Override public void methodToColumnName(String setterName, Class parameterType, String columnName) { methodToColumnName(setter(setterName, parameterType), columnName); } @Override public void methodToColumn(String setterName, Class parameterType, Enum column) { methodToColumn(setter(setterName, parameterType), column); } @Override public void methodToIndex(String setterName, Class parameterType, int columnIndex) { methodToIndex(setter(setterName, parameterType), columnIndex); } /** * Returns the first-level names of all nested members whose attributes or methods have been mapped * @return the names of nested objects to visit from the current object */ public Set getNestedAttributeNames() { Set out = new HashSet(); attributeMapping.extractPrefixes(out); methodNameMapping.extractPrefixes(out); methodMapping.extractPrefixes(out); return out; } @Override public ColumnMapper clone() { try { ColumnMapping out = (ColumnMapping) super.clone(); out.attributeMapping = (NameMapping) this.attributeMapping.clone(); out.methodNameMapping = (NameMapping) this.methodNameMapping.clone(); out.methodMapping = (MethodMapping) this.methodMapping.clone(); return out; } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } } ExcludeFieldEnumSelector.java000066400000000000000000000044431400120543400343560ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; /** * A FieldSelector capable of deselecting fields in a record. *

This selector stores undesired fields, represented by values of an enumeration, * and will return the indexes of those fields that are not part of the selection. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see FieldSelector * @see FieldSet */ @SuppressWarnings("rawtypes") public class ExcludeFieldEnumSelector extends FieldSet implements FieldSelector { private ExcludeFieldNameSelector names = new ExcludeFieldNameSelector(); /** * Returns the indexes of any that are part of a sequence of headers but not part of the selection. * * @param headers the sequence of headers that might have some elements selected by this FieldSelector * @return the positions of all elements which were not selected. */ @Override public int[] getFieldIndexes(NormalizedString[] headers) { if(headers == null){ return null; } names.set(ArgumentUtils.toArray(this.get())); return names.getFieldIndexes(headers); } @Override public String describe() { return "undesired " + super.describe(); } public ExcludeFieldEnumSelector clone(){ ExcludeFieldEnumSelector out = (ExcludeFieldEnumSelector) super.clone(); out.names = (ExcludeFieldNameSelector) names.clone(); return out; } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } ExcludeFieldIndexSelector.java000066400000000000000000000045561400120543400345260ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; import java.util.*; /** * A FieldSelector capable of deselecting fields by their position in a record. * *

This selector stores undesired fields and will return the indexes of those fields that are not part of the selection. * * @see FieldSelector * @see FieldSet * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ExcludeFieldIndexSelector extends FieldSet implements FieldSelector { /** * Returns the indexes of any that are part of a sequence of headers but not part of the selection. * @param columns the sequence of headers that might have some elements selected by this FieldSelector * @return the positions of all elements which were not selected. */ @Override public int[] getFieldIndexes(NormalizedString[] columns) { if(columns == null){ return null; } Set chosenFields = new HashSet(this.get()); Iterator it = chosenFields.iterator(); while(it.hasNext()){ Integer chosenIndex = it.next(); if (chosenIndex >= columns.length || chosenIndex < 0) { it.remove(); } } int[] out = new int[columns.length - chosenFields.size()]; int j = 0; for (int i = 0; i < columns.length; i++) { if (!chosenFields.contains(i)) { out[j++] = i; } } return out; } @Override public String describe() { return "undesired " + super.describe(); } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } ExcludeFieldNameSelector.java000066400000000000000000000046611400120543400343340ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; import java.util.*; /** * A FieldSelector capable of deselecting fields by their name in a record. * *

This selector stores undesired fields and will return the indexes of those fields that are not part of the selection. * * @see FieldSelector * @see FieldSet * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ExcludeFieldNameSelector extends FieldSet implements FieldSelector, Cloneable { /** * Returns the indexes of any that are part of a sequence of headers but not part of the selection. * @param headers the sequence of headers that might have some elements selected by this FieldSelector * @return the positions of all elements which were not selected. */ @Override public int[] getFieldIndexes(NormalizedString[] headers) { if(headers == null){ return null; } NormalizedString[] normalizedHeaders = headers; // removes duplicates if any Set chosenFields = NormalizedString.toHashSet(this.get()); Object[] unknownFields = ArgumentUtils.findMissingElements(normalizedHeaders, chosenFields); int[] out = new int[normalizedHeaders.length - (chosenFields.size() - unknownFields.length)]; int j = 0; for (int i = 0; i < normalizedHeaders.length; i++) { if (!chosenFields.contains(normalizedHeaders[i])) { out[j++] = i; } } return out; } @Override public String describe() { return "undesired " + super.describe(); } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } FieldConversionMapping.java000066400000000000000000000465401400120543400341040ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import java.util.*; /** * A class for mapping field selections to sequences of {@link Conversion} objects * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class FieldConversionMapping implements Cloneable { @SuppressWarnings("rawtypes") private static final Conversion[] EMPTY_CONVERSION_ARRAY = new Conversion[0]; public int[] validatedIndexes; /** * This list contains the sequence of conversions applied to sets of fields over multiple calls. *

It is shared by {@link FieldConversionMapping#fieldNameConversionMapping}, {@link FieldConversionMapping#fieldIndexConversionMapping} and {@link FieldConversionMapping#convertAllMapping}. *

Every time the user associates a sequence of conversions to a field, conversionSequence list will receive the FieldSelector. */ private List conversionSequence = new ArrayList(); private AbstractConversionMapping fieldNameConversionMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new FieldNameSelector(); } }; private AbstractConversionMapping fieldIndexConversionMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new FieldIndexSelector(); } }; @SuppressWarnings("rawtypes") private AbstractConversionMapping fieldEnumConversionMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new FieldEnumSelector(); } }; private AbstractConversionMapping convertAllMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new AllIndexesSelector(); } }; /** * This is the final sequence of conversions applied to each index in a record. It is populated when {@link FieldConversionMapping#prepareExecution(boolean, String[])} is invoked. */ private Map>> conversionsByIndex = Collections.emptyMap(); private Map> validationsByIndex = Collections.emptyMap(); /** * Prepares the conversions registered in this object to be executed against a given sequence of fields * * @param writing flag indicating whether a writing process is being initialized. * @param values The field sequence that identifies how records will be organized. *

This is generally the sequence of headers in a record, but it might be just the first parsed row from a given input (as field selection by index is allowed). */ public void prepareExecution(boolean writing, String[] values) { if (fieldNameConversionMapping.isEmpty() && fieldEnumConversionMapping.isEmpty() && fieldIndexConversionMapping.isEmpty() && convertAllMapping.isEmpty()) { return; } if (!conversionsByIndex.isEmpty()) { return; } //Note this property is shared across all conversion mappings. This is required so //the correct conversion sequence is registered for all fields. conversionsByIndex = new HashMap>>(); // adds the conversions in the sequence they were created. for (FieldSelector next : conversionSequence) { fieldNameConversionMapping.prepareExecution(writing, next, conversionsByIndex, values); fieldIndexConversionMapping.prepareExecution(writing, next, conversionsByIndex, values); fieldEnumConversionMapping.prepareExecution(writing, next, conversionsByIndex, values); convertAllMapping.prepareExecution(writing, next, conversionsByIndex, values); } Iterator>>> entryIterator = conversionsByIndex.entrySet().iterator(); while (entryIterator.hasNext()) { Map.Entry>> e = entryIterator.next(); Iterator> it = e.getValue().iterator(); while (it.hasNext()) { Conversion conversion = it.next(); if (conversion instanceof ValidatedConversion) { if (validationsByIndex.isEmpty()) { validationsByIndex = new TreeMap>(); } it.remove(); List validations = validationsByIndex.get(e.getKey()); if (validations == null) { validations = new ArrayList(1); validationsByIndex.put(e.getKey(), validations); } validations.add((ValidatedConversion) conversion); } } if (e.getValue().isEmpty()) { entryIterator.remove(); } } validatedIndexes = ArgumentUtils.toIntArray(validationsByIndex.keySet()); } /** * Applies a sequence of conversions on all fields. * * @param conversions the sequence of conversions to be applied */ public void applyConversionsOnAllFields(Conversion... conversions) { convertAllMapping.registerConversions(conversions); } /** * Applies a sequence of conversions on a selection of field indexes * * @param conversions the sequence of conversions to be applied * * @return a selector of column indexes. */ public FieldSet applyConversionsOnFieldIndexes(Conversion... conversions) { return fieldIndexConversionMapping.registerConversions(conversions); } /** * Applies a sequence of conversions on a selection of field name * * @param conversions the sequence of conversions to be applied * * @return a selector of column names. */ public FieldSet applyConversionsOnFieldNames(Conversion... conversions) { return fieldNameConversionMapping.registerConversions(conversions); } /** * Applies a sequence of conversions on a selection of enumerations that represent fields * * @param conversions the sequence of conversions to be applied * * @return a selector of enumerations. */ @SuppressWarnings("rawtypes") public FieldSet applyConversionsOnFieldEnums(Conversion... conversions) { return fieldEnumConversionMapping.registerConversions(conversions); } /** * Applies any validations associated with a field at a given index in a record * @param index The index of parsed value in a record * @param value The value of the record at the given index */ public void executeValidations(int index, Object value) { List validations = validationsByIndex.get(index); if (validations != null) { for (int i = 0; i < validations.size(); i++) { validations.get(i).execute(value); } } } /** * Applies a sequence of conversions associated with an Object value at a given index in a record. * * @param executeInReverseOrder flag to indicate whether or not the conversion sequence must be executed in reverse order * @param index The index of parsed value in a record * @param value The value in a record * @param convertedFlags an array of flags that indicate whether a conversion occurred. Used to determine whether * or not a default conversion by type (specified with {@link ConversionProcessor#convertType(Class, Conversion[])}) should be applied. * * @return the Object resulting from a sequence of conversions against the original value. */ @SuppressWarnings({"unchecked", "rawtypes"}) public Object reverseConversions(boolean executeInReverseOrder, int index, Object value, boolean[] convertedFlags) { List> conversions = conversionsByIndex.get(index); if (conversions != null) { if (convertedFlags != null) { convertedFlags[index] = true; } Conversion conversion = null; try { if (executeInReverseOrder) { for (int i = conversions.size() - 1; i >= 0; i--) { conversion = conversions.get(i); value = conversion.revert(value); } } else { for (Conversion c : conversions) { conversion = c; value = conversion.revert(value); } } } catch (DataProcessingException ex) { ex.setValue(value); ex.setColumnIndex(index); ex.markAsNonFatal(); throw ex; } catch (Throwable ex) { DataProcessingException exception; if (conversion != null) { exception = new DataProcessingException("Error converting value '{value}' using conversion " + conversion.getClass().getName(), ex); } else { exception = new DataProcessingException("Error converting value '{value}'", ex); } exception.setValue(value); exception.setColumnIndex(index); exception.markAsNonFatal(); throw exception; } } return value; } /** * Applies a sequence of conversions associated with a String value parsed from a given index. * * @param index The index of parsed value in a record * @param stringValue The parsed value in a record * @param convertedFlags an array of flags that indicate whether a conversion occurred. Used to determine whether * or not a default conversion by type (specified with {@link ConversionProcessor#convertType(Class, Conversion[])}) should be applied. * * @return the Object produced by a sequence of conversions against the original String value. */ @SuppressWarnings({"rawtypes", "unchecked"}) public Object applyConversions(int index, String stringValue, boolean[] convertedFlags) { List> conversions = conversionsByIndex.get(index); if (conversions != null) { if (convertedFlags != null) { convertedFlags[index] = true; } Object result = stringValue; for (Conversion conversion : conversions) { try { result = conversion.execute(result); } catch (DataProcessingException ex) { ex.setColumnIndex(index); ex.markAsNonFatal(); throw ex; } catch (Throwable ex) { DataProcessingException exception = new DataProcessingException("Error converting value '{value}' using conversion " + conversion.getClass().getName(), ex); exception.setValue(result); exception.setColumnIndex(index); exception.markAsNonFatal(); throw exception; } } return result; } return stringValue; } /** * Returns the sequence of conversions to be applied at a given column index * * @param index the index of the column where the conversions should be executed * @param expectedType the type resulting from the conversion sequence. * * @return the sequence of conversions to be applied at a given column index */ @SuppressWarnings("rawtypes") public Conversion[] getConversions(int index, Class expectedType) { List> conversions = conversionsByIndex.get(index); Conversion[] out; if (conversions != null) { out = new Conversion[conversions.size()]; int i = 0; for (Conversion conversion : conversions) { out[i++] = conversion; } } else if (expectedType == String.class) { return EMPTY_CONVERSION_ARRAY; } else { out = new Conversion[1]; out[0] = AnnotationHelper.getDefaultConversion(expectedType, null, null); if (out[0] == null) { return EMPTY_CONVERSION_ARRAY; } } return out; } @Override public FieldConversionMapping clone() { try { FieldConversionMapping out = (FieldConversionMapping) super.clone(); out.validatedIndexes = validatedIndexes == null ? null : this.validatedIndexes.clone(); out.conversionSequence = new ArrayList(); Map clonedSelectors = new HashMap(); for (FieldSelector selector : this.conversionSequence) { FieldSelector clone = (FieldSelector) selector.clone(); out.conversionSequence.add(clone); clonedSelectors.put(selector, clone); } out.fieldNameConversionMapping = fieldNameConversionMapping.clone(clonedSelectors, out.conversionSequence); out.fieldIndexConversionMapping = fieldIndexConversionMapping.clone(clonedSelectors, out.conversionSequence); out.fieldEnumConversionMapping = fieldEnumConversionMapping.clone(clonedSelectors, out.conversionSequence); out.convertAllMapping = convertAllMapping.clone(clonedSelectors, out.conversionSequence); out.conversionsByIndex = new HashMap>>(conversionsByIndex); out.validationsByIndex = new TreeMap>(validationsByIndex); return out; } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } } /** * Class responsible for managing field selections and any conversion sequence associated with each. * * @param the FieldSelector type information used to uniquely identify a field (e.g. references to field indexes would use Integer, while references to field names would use String). * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see FieldNameSelector * @see FieldIndexSelector */ abstract class AbstractConversionMapping implements Cloneable { private Map[]> conversionsMap; private List conversionSequence; AbstractConversionMapping(List conversionSequence) { this.conversionSequence = conversionSequence; } /** * Registers a sequence of conversions to a set of fields. *

The selector instance that is used to store which fields should be converted is added to the {@link AbstractConversionMapping#conversionSequence} list in order to keep track of the correct conversion order. *

This is required further conversion sequences might be added to the same fields in separate calls. * * @param conversions the conversion sequence to be applied to a set of fields. * * @return a FieldSet which provides methods to select the fields that must be converted or null if the FieldSelector returned by #newFieldSelector is not an instance of FieldSet (which is the case of {@link AllIndexesSelector}). */ @SuppressWarnings("unchecked") public FieldSet registerConversions(Conversion... conversions) { ArgumentUtils.noNulls("Conversions", conversions); FieldSelector selector = newFieldSelector(); if (conversionsMap == null) { conversionsMap = new LinkedHashMap[]>(); } conversionsMap.put(selector, conversions); conversionSequence.add(selector); if (selector instanceof FieldSet) { return (FieldSet) selector; } return null; } /** * Creates a FieldSelector instance of the desired type. Used in @link FieldConversionMapping}. * * @return a new FieldSelector instance. */ protected abstract FieldSelector newFieldSelector(); /** * Get all indexes in the given selector and adds the conversions defined at that index to the map of conversionsByIndex. *

This method is called in the same sequence each selector was created (in {@link FieldConversionMapping#prepareExecution(boolean, String[])}) *

At the end of the process, the map of conversionsByIndex will have each index with its list of conversions in the order they were declared. * * @param writing flag indicating whether a writing process is being initialized. * @param selector the selected fields for a given conversion sequence. * @param conversionsByIndex map of all conversions registered to every field index, in the order they were declared * @param values The field sequence that identifies how records will be organized. *

This is generally the sequence of headers in a record, but it might be just the first parsed row from a given input (as field selection by index is allowed). */ public void prepareExecution(boolean writing, FieldSelector selector, Map>> conversionsByIndex, String[] values) { if (conversionsMap == null) { return; } //conversionsMap contains maps the conversions applied to a field selection //we will match the indexes where these conversions where applied and add them to the corresponding list in conversionsByIndex Conversion[] conversions = conversionsMap.get(selector); if (conversions == null) { return; } int[] fieldIndexes = selector.getFieldIndexes(NormalizedString.toIdentifierGroupArray(values)); if (fieldIndexes == null) { fieldIndexes = ArgumentUtils.toIntArray(conversionsByIndex.keySet()); } for (int fieldIndex : fieldIndexes) { List> conversionsAtIndex = conversionsByIndex.get(fieldIndex); if (conversionsAtIndex == null) { conversionsAtIndex = new ArrayList>(); conversionsByIndex.put(fieldIndex, conversionsAtIndex); } validateDuplicates(selector, conversionsAtIndex, conversions); conversionsAtIndex.addAll(Arrays.asList(conversions)); } } /** * Ensures an individual field does not have the same conversion object applied to it more than once. * * @param selector the selection of fields * @param conversionsAtIndex the sequence of conversions applied to a given index * @param conversionsToAdd the sequence of conversions to add to conversionsAtIndex */ private static void validateDuplicates(FieldSelector selector, List> conversionsAtIndex, Conversion[] conversionsToAdd) { for (Conversion toAdd : conversionsToAdd) { for (Conversion existing : conversionsAtIndex) { if (toAdd == existing) { throw new DataProcessingException("Duplicate conversion " + toAdd.getClass().getName() + " being applied to " + selector.describe()); } } } } /** * Queries if any conversions were associated with any field * * @return true if no conversions were associated with any field; false otherwise */ public boolean isEmpty() { return conversionsMap == null || conversionsMap.isEmpty(); } public AbstractConversionMapping clone() { try { return (AbstractConversionMapping) super.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } public AbstractConversionMapping clone(Map clonedSelectors, List clonedConversionSequence) { AbstractConversionMapping out = clone(); out.conversionSequence = clonedConversionSequence; if (conversionsMap != null) { out.conversionsMap = new HashMap[]>(); for (FieldSelector selector : this.conversionSequence) { FieldSelector clone = clonedSelectors.get(selector); if (clone == null) { throw new IllegalStateException("Internal error cloning conversion mappings"); } Conversion[] conversions = conversionsMap.get(selector); out.conversionsMap.put(clone, conversions); } } return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/FieldEnumSelector.java000066400000000000000000000037471400120543400331310ustar00rootroot00000000000000/* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; /** * A FieldSelector capable of selecting fields represented by values of an enumeration type. * The {@code toString()} output of the enumeration value will be used to match name of the fields. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see FieldSelector * @see FieldSet */ @SuppressWarnings("rawtypes") public class FieldEnumSelector extends FieldSet implements FieldSelector { private FieldNameSelector names = new FieldNameSelector(); /** * Returns the position of a given column represented by an enumeration value. * * @param column the column whose position will be returned * @return the position of the given column. */ public int getFieldIndex(Enum column) { return names.getFieldIndex(column.toString()); } @Override public int[] getFieldIndexes(NormalizedString[] headers) { if(headers == null){ return null; } names.set(ArgumentUtils.toArray(this.get())); return names.getFieldIndexes(headers); } public FieldEnumSelector clone() { FieldEnumSelector out = (FieldEnumSelector) super.clone(); out.names = (FieldNameSelector) names.clone(); return out; } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/FieldIndexSelector.java000066400000000000000000000030711400120543400332620ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; import java.util.*; /** * A FieldSelector capable of selecting fields by their position in a record. * * @see FieldSelector * @see FieldSet * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class FieldIndexSelector extends FieldSet implements FieldSelector { @Override public int[] getFieldIndexes(NormalizedString[] columns) { List chosenIndexes = this.get(); int[] out = new int[chosenIndexes.size()]; int i = 0; for (Integer index : chosenIndexes) { out[i++] = index; } return out; } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/FieldNameSelector.java000066400000000000000000000054531400120543400331010ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; import java.util.*; /** * A FieldSelector capable of selecting fields by their name. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see FieldSelector * @see FieldSet */ public class FieldNameSelector extends FieldSet implements FieldSelector, Cloneable { /** * Returns the position of a given header * * @param header the header whose position will be returned * @return the position of the given header. */ public int getFieldIndex(String header) { return getFieldIndexes(new NormalizedString[]{NormalizedString.valueOf(header)})[0]; } @Override public int[] getFieldIndexes(NormalizedString[] headers) { if (headers == null) { return null; } NormalizedString[] normalizedHeader = headers; List selection = NormalizedString.toArrayList(this.get()); NormalizedString[] chosenFields = selection.toArray(new NormalizedString[0]); Object[] unknownFields = ArgumentUtils.findMissingElements(normalizedHeader, chosenFields); //if we get a subset of the expected columns, we can parse normally, considering missing column values as null. if (unknownFields.length > 0 && !selection.containsAll(Arrays.asList(normalizedHeader))) { //nothing matched, just return an empty array and proceed. if (unknownFields.length == chosenFields.length) { return new int[0]; } } int[] out = new int[selection.size()]; Arrays.fill(out, -1); int i = 0; for (NormalizedString chosenField : selection) { int[] indexes = ArgumentUtils.indexesOf(normalizedHeader, chosenField); if (indexes.length > 1) { out = Arrays.copyOf(out, out.length + indexes.length - 1); } if (indexes.length != 0) { for (int j = 0; j < indexes.length; j++) { out[i++] = indexes[j]; } } else { i++; } } return out; } @Override public int[] getFieldIndexes(String[] headers) { return getFieldIndexes(NormalizedString.toIdentifierGroupArray(headers)); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/FieldSelector.java000066400000000000000000000041411400120543400322710ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; /** * * Interface used to identify classes capable of selecting fields and returning their positions in a given sequence. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface FieldSelector extends Cloneable { /** * Returns the indexes of any selected fields that are part of a sequence of headers. * @param headers the sequence of headers that might have some elements selected by this FieldSelector * @return the positions of all selected elements in the given headers sequence. */ int[] getFieldIndexes(String[] headers); /** * Returns the indexes of any selected fields that are part of a sequence of headers. * @param headers the sequence of headers that might have some elements selected by this FieldSelector * @return the positions of all selected elements in the given headers sequence. */ int[] getFieldIndexes(NormalizedString[] headers); /** * Returns a string that represents the current field selection * @return a string that represents the current field selection */ String describe(); /** * Clones this field selector. Changes to the selection on the clone won't be reflected on the original * @return a copy of the current field selector. */ Object clone(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/fields/FieldSet.java000066400000000000000000000125361400120543400312530ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import com.univocity.parsers.common.*; import java.util.*; /** * A set of selected fields. * *

Used by {@link CommonSettings} to select fields for reading/writing *

Also used by {@code com.univocity.parsers.common.processor.ConversionProcessor} to select fields that have to be converted. * * @see FieldNameSelector * @see FieldIndexSelector * * @see CommonSettings * * @param the type of the reference information used to uniquely identify a field (e.g. references to field indexes would use Integer, while references to field names would use String). * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class FieldSet implements Cloneable { private List fields = new ArrayList(); private List> wrappedFieldSets; /** * Creates am empty field set. For internal use only. */ public FieldSet() { this.wrappedFieldSets = Collections.emptyList(); } /** * Creates a field set that wraps a collection of other field sets. For internal use only. * @param wrappedFieldSets the field sets to be wrapped. */ public FieldSet(List> wrappedFieldSets) { this.wrappedFieldSets = wrappedFieldSets; if (this.wrappedFieldSets.contains(this)) { this.wrappedFieldSets.remove(this); } } /** * Returns a copy of the fields in this set * @return a copy of the fields in this set */ public List get() { return new ArrayList(fields); } /** * Validates and sets multiple field references. Any existing reference will be discarded. * @param fields information that uniquely identifies each field * @return the set of currently selected fields */ public FieldSet set(T... fields) { this.fields.clear(); add(fields); for (FieldSet wrapped : wrappedFieldSets) { wrapped.set(fields); } return this; } /** * Validates and adds multiple field references * @param fields information that uniquely identifies each field * @return the set of currently selected fields */ public FieldSet add(T... fields) { for (T field : fields) { addElement(field); } for (FieldSet wrapped : wrappedFieldSets) { wrapped.add(fields); } return this; } /** * Validates and adds a reference to a field. * @param field information that uniquely identifies a field */ private void addElement(T field) { fields.add(field); } /** * Validates and sets multiple field references. Any existing reference will be discarded. * @param fields information that uniquely identifies each field * @return the set of currently selected fields */ public FieldSet set(Collection fields) { this.fields.clear(); add(fields); for (FieldSet wrapped : wrappedFieldSets) { wrapped.set(fields); } return this; } /** * Validates and adds multiple field references * @param fields information that uniquely identifies each field * @return the set of currently selected fields */ public FieldSet add(Collection fields) { for (T field : fields) { addElement(field); } for (FieldSet wrapped : wrappedFieldSets) { wrapped.add(fields); } return this; } /** * Removes multiple field references in the selection * @param fields information that uniquely identifies each field * @return the set of currently selected fields */ public FieldSet remove(T... fields) { for (T field : fields) { this.fields.remove(field); } for (FieldSet wrapped : wrappedFieldSets) { wrapped.remove(fields); } return this; } /** * Removes multiple field references in the selection * @param fields information that uniquely identifies each field * @return the set of currently selected fields */ public FieldSet remove(Collection fields) { this.fields.removeAll(fields); for (FieldSet wrapped : wrappedFieldSets) { wrapped.remove(fields); } return this; } /** * Returns a string that represents the current field selection * @return a string that represents the current field selection */ public String describe() { return "field selection: " + fields.toString(); } @Override public String toString() { return fields.toString(); } public FieldSet clone() { try { FieldSet out = (FieldSet) super.clone(); out.fields = new ArrayList(fields); if (wrappedFieldSets != null) { out.wrappedFieldSets = new ArrayList>(); for (FieldSet fieldSet : wrappedFieldSets) { out.wrappedFieldSets.add(fieldSet.clone()); } } return out; } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/000077500000000000000000000000001400120543400265735ustar00rootroot00000000000000AbstractCharInputReader.java000066400000000000000000000410661400120543400340720ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; import java.io.*; import java.util.*; /** * The base class for implementing different flavours of {@link CharInputReader}. * *

It provides the essential conversion of sequences of newline characters defined by {@link Format#getLineSeparator()} into the normalized newline character provided in {@link Format#getNormalizedNewline()}. *

It also provides a default implementation for most of the methods specified by the {@link CharInputReader} interface. *

Extending classes must essentially read characters from a given {@link java.io.Reader} and assign it to the public {@link AbstractCharInputReader#buffer} when requested (in the {@link AbstractCharInputReader#reloadBuffer()} method). * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.Format * @see com.univocity.parsers.common.input.DefaultCharInputReader * @see com.univocity.parsers.common.input.concurrent.ConcurrentCharInputReader */ public abstract class AbstractCharInputReader implements CharInputReader { private final ExpandingCharAppender tmp; private boolean lineSeparatorDetected; private final boolean detectLineSeparator; private List inputAnalysisProcesses; private char lineSeparator1; private char lineSeparator2; private final char normalizedLineSeparator; private long lineCount; private long charCount; private int recordStart; final int whitespaceRangeStart; private boolean skipping = false; private boolean commentProcessing = false; protected final boolean closeOnStop; /** * Current position in the buffer */ public int i; private char ch; /** * The buffer itself */ public char[] buffer; /** * Number of characters available in the buffer. */ public int length = -1; private boolean incrementLineCount; private boolean normalizeLineEndings = true; /** * Creates a new instance that attempts to detect the newlines used in the input automatically. * * @param normalizedLineSeparator the normalized newline character (as defined in {@link Format#getNormalizedNewline()}) that is used to replace any lineSeparator sequence found in the input. * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param closeOnStop indicates whether to automatically close the input when {@link #stop()} is called */ public AbstractCharInputReader(char normalizedLineSeparator, int whitespaceRangeStart, boolean closeOnStop) { this(null, normalizedLineSeparator, whitespaceRangeStart, closeOnStop); } /** * Creates a new instance with the mandatory characters for handling newlines transparently. * * @param lineSeparator the sequence of characters that represent a newline, as defined in {@link Format#getLineSeparator()} * @param normalizedLineSeparator the normalized newline character (as defined in {@link Format#getNormalizedNewline()}) that is used to replace any lineSeparator sequence found in the input. * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param closeOnStop indicates whether to automatically close the input when {@link #stop()} is called */ public AbstractCharInputReader(char[] lineSeparator, char normalizedLineSeparator, int whitespaceRangeStart, boolean closeOnStop) { this.whitespaceRangeStart = whitespaceRangeStart; this.tmp = new ExpandingCharAppender(4096, null, whitespaceRangeStart); if (lineSeparator == null) { detectLineSeparator = true; submitLineSeparatorDetector(); this.lineSeparator1 = '\0'; this.lineSeparator2 = '\0'; } else { setLineSeparator(lineSeparator); this.detectLineSeparator = false; } this.normalizedLineSeparator = normalizedLineSeparator; this.closeOnStop = closeOnStop; } private void submitLineSeparatorDetector() { if (detectLineSeparator && !lineSeparatorDetected) { addInputAnalysisProcess(new LineSeparatorDetector() { @Override protected void apply(char separator1, char separator2) { if (separator1 != '\0') { lineSeparatorDetected = true; lineSeparator1 = separator1; lineSeparator2 = separator2; } else { setLineSeparator(Format.getSystemLineSeparator()); } } }); } } private void setLineSeparator(char[] lineSeparator) { if (lineSeparator == null || lineSeparator.length == 0) { throw new IllegalArgumentException("Invalid line separator. Expected 1 to 2 characters"); } if (lineSeparator.length > 2) { throw new IllegalArgumentException("Invalid line separator. Up to 2 characters are expected. Got " + lineSeparator.length + " characters."); } this.lineSeparator1 = lineSeparator[0]; this.lineSeparator2 = lineSeparator.length == 2 ? lineSeparator[1] : '\0'; } /** * Passes the {@link java.io.Reader} provided in the {@link AbstractCharInputReader#start(Reader)} method to the extending class so it can begin loading characters from it. * * @param reader the {@link java.io.Reader} provided in {@link AbstractCharInputReader#start(Reader)} */ protected abstract void setReader(Reader reader); /** * Informs the extending class that the buffer has been read entirely and requests for another batch of characters. * Implementors must assign the new character buffer to the public {@link AbstractCharInputReader#buffer} attribute, as well as the number of characters available to the public {@link AbstractCharInputReader#length} attribute. * To notify the input does not have any more characters, {@link AbstractCharInputReader#length} must receive the -1 value */ protected abstract void reloadBuffer(); protected final void unwrapInputStream(BomInput.BytesProcessedNotification notification) { InputStream inputStream = notification.input; String encoding = notification.encoding; if (encoding != null) { try { start(new InputStreamReader(inputStream, encoding)); } catch (Exception e) { throw new IllegalStateException(e); } } else { length = -1; start(new InputStreamReader(inputStream), false); } } private void start(Reader reader, boolean resetTmp){ if(resetTmp) { tmp.reset(); } stop(); setReader(reader); lineCount = 0; lineSeparatorDetected = false; submitLineSeparatorDetector(); updateBuffer(); //if the input has been properly decoded with the correct UTF* character set, but has a BOM marker, we can safely discard it. if (length > 0 && buffer[0] == '\uFEFF') { //regardless of the UTF* encoding used, the BOM bytes always produce the '\uFEFF' character when decoded. i++; } } @Override public final void start(Reader reader) { start(reader, true); } /** * Requests the next batch of characters from the implementing class and updates * the character count. * *

If there are no more characters in the input, the reading will stop by invoking the {@link AbstractCharInputReader#stop()} method. */ private void updateBuffer() { if (!commentProcessing && length - recordStart > 0 && buffer != null && !skipping) { tmp.append(buffer, recordStart, length - recordStart); } recordStart = 0; reloadBuffer(); charCount += i; i = 0; if (length == -1) { stop(); incrementLineCount = true; } if (inputAnalysisProcesses != null) { if (length > 0 && length <= 4) { int tmpLength = length; char[] tmp = Arrays.copyOfRange(buffer, 0, length + 1); // length + 1 to assist CSV detection process: length < buffer.length indicates all data was read into the buffer. //sets processes temporarily to null to prevent them running if method `unwrapInputStream` is called. List processes = inputAnalysisProcesses; inputAnalysisProcesses = null; reloadBuffer(); inputAnalysisProcesses = processes; if (length != -1) { char[] newBuffer = new char[tmpLength + buffer.length]; System.arraycopy(tmp, 0, newBuffer, 0, tmpLength); System.arraycopy(buffer, 0, newBuffer, tmpLength, length); buffer = newBuffer; length += tmpLength; } else { buffer = tmp; length = tmpLength; } } try { for (InputAnalysisProcess process : inputAnalysisProcesses) { process.execute(buffer, length); } } finally { if (length > 4) { inputAnalysisProcesses = null; } } } } /** * Submits a custom {@link InputAnalysisProcess} to analyze the input buffer and potentially discover configuration options such as * column separators is CSV, data formats, etc. The process will be execute only once. * * @param inputAnalysisProcess a custom process to analyze the contents of the input buffer. */ public final void addInputAnalysisProcess(InputAnalysisProcess inputAnalysisProcess) { if (inputAnalysisProcess == null) { return; } if (this.inputAnalysisProcesses == null) { inputAnalysisProcesses = new ArrayList(); } inputAnalysisProcesses.add(inputAnalysisProcess); } private void throwEOFException() { if (incrementLineCount) { lineCount++; } ch = '\0'; throw new EOFException(); } @Override public final char nextChar() { if (length == -1) { throwEOFException(); } ch = buffer[i++]; if (i >= length) { updateBuffer(); } if (lineSeparator1 == ch && (lineSeparator2 == '\0' || length != -1 && lineSeparator2 == buffer[i])) { lineCount++; if (normalizeLineEndings) { ch = normalizedLineSeparator; if (lineSeparator2 == '\0') { return ch; } if (++i >= length) { if (length != -1) { updateBuffer(); } else { throwEOFException(); } } } } return ch; } @Override public final char getChar() { return ch; } @Override public final long lineCount() { return lineCount; } @Override public final void skipLines(long lines) { if (lines < 1) { skipping = false; return; } skipping = true; long expectedLineCount = this.lineCount + lines; try { do { nextChar(); } while (lineCount < expectedLineCount); skipping = false; } catch (EOFException ex) { skipping = false; } } @Override public String readComment() { long expectedLineCount = lineCount + 1; commentProcessing = true; tmp.reset(); try { do { char ch = nextChar(); if (ch <= ' ' && whitespaceRangeStart < ch) { ch = skipWhitespace(ch, normalizedLineSeparator, normalizedLineSeparator); } tmp.appendUntil(ch, this, normalizedLineSeparator, normalizedLineSeparator); if (lineCount < expectedLineCount) { tmp.appendIgnoringWhitespace(nextChar()); } else { tmp.updateWhitespace(); return tmp.getAndReset(); } } while (true); } catch (EOFException ex) { tmp.updateWhitespace(); return tmp.getAndReset(); } finally { commentProcessing = false; } } @Override public final long charCount() { return charCount + i; } @Override public final void enableNormalizeLineEndings(boolean normalizeLineEndings) { this.normalizeLineEndings = normalizeLineEndings; } @Override public char[] getLineSeparator() { if (lineSeparator2 != '\0') { return new char[]{lineSeparator1, lineSeparator2}; } else { return new char[]{lineSeparator1}; } } @Override public final char skipWhitespace(char ch, char stopChar1, char stopChar2) { while (ch <= ' ' && ch != stopChar1 && ch != normalizedLineSeparator && ch != stopChar2 && whitespaceRangeStart < ch) { ch = nextChar(); } return ch; } @Override public final int currentParsedContentLength() { return i - recordStart + tmp.length(); } @Override public final String currentParsedContent() { if (tmp.length() == 0) { if (i > recordStart) { return new String(buffer, recordStart, i - recordStart); } return null; } if (i > recordStart) { tmp.append(buffer, recordStart, i - recordStart); } return tmp.getAndReset(); } @Override public final int lastIndexOf(char ch) { if (tmp.length() == 0) { if (i > recordStart) { for (int x = i - 1, c = 0; x >= recordStart; x--, c++) { if (buffer[x] == ch) { return recordStart + c; } } } return -1; } if(i > recordStart){ for (int x = i - 1, c = 0; x >= recordStart; x--, c++) { if (buffer[x] == ch) { return tmp.length() + recordStart + c; } } } return tmp.lastIndexOf(ch); } @Override public final void markRecordStart() { tmp.reset(); recordStart = i % length; } @Override public final boolean skipString(char ch, char stop) { if (i == 0) { return false; } int i = this.i; for (; ch != stop; ch = buffer[i++]) { if (i >= length) { return false; } if (lineSeparator1 == ch && (lineSeparator2 == '\0' || lineSeparator2 == buffer[i])) { break; } } this.i = i - 1; nextChar(); return true; } @Override public final String getString(char ch, char stop, boolean trim, String nullValue, int maxLength) { if (i == 0) { return null; } int i = this.i; for (; ch != stop; ch = buffer[i++]) { if (i >= length) { return null; } if (lineSeparator1 == ch && (lineSeparator2 == '\0' || lineSeparator2 == buffer[i])) { break; } } int pos = this.i - 1; int len = i - this.i; if (maxLength != -1 && len > maxLength) { //validating before trailing whitespace handling so this behaves as an appender. return null; } this.i = i - 1; if (trim) { i = i - 2; while (buffer[i] <= ' ' && whitespaceRangeStart < buffer[i]) { len--; i--; } } String out; if (len <= 0) { out = nullValue; } else { out = new String(buffer, pos, len); } nextChar(); return out; } @Override public final String getQuotedString(char quote, char escape, char escapeEscape, int maxLength, char stop1, char stop2, boolean keepQuotes, boolean keepEscape, boolean trimLeading, boolean trimTrailing) { if (i == 0) { return null; } int i = this.i; while (true) { if (i >= length) { return null; } ch = buffer[i]; if (ch == quote) { if (buffer[i - 1] == escape) { if (keepEscape) { i++; continue; } return null; } if (i + 1 < length) { char next = buffer[i + 1]; if (next == stop1 || next == stop2) { break; } } return null; } else if (ch == escape && !keepEscape) { if (i + 1 < length) { char next = buffer[i + 1]; if (next == quote || next == escapeEscape) { return null; } } } else if (lineSeparator1 == ch && normalizeLineEndings && (lineSeparator2 == '\0' || i + 1 < length && lineSeparator2 == buffer[i + 1])) { return null; } i++; } int pos = this.i; int len = i - this.i; if (maxLength != -1 && len > maxLength) { //validating before trailing whitespace handling so this behaves as an appender. return null; } if (keepQuotes) { pos--; len += 2; } else { if (trimTrailing) { while (len > 0 && buffer[pos + len - 1] <= ' ') { len--; } } if (trimLeading) { while (len > 0 && buffer[pos] <= ' ') { pos++; len--; } } } this.i = i + 1; String out; if (len <= 0) { out = ""; } else { out = new String(buffer, pos, len); } if (this.i >= length) { updateBuffer(); } return out; } public final boolean skipQuotedString(char quote, char escape, char stop1, char stop2) { if (i == 0) { return false; } int i = this.i; while (true) { if (i >= length) { return false; } ch = buffer[i]; if (ch == quote) { if (buffer[i - 1] == escape) { i++; continue; } if (i + 1 < length) { char next = buffer[i + 1]; if (next == stop1 || next == stop2) { break; } } return false; } else if (lineSeparator1 == ch && normalizeLineEndings && (lineSeparator2 == '\0' || i + 1 < length && lineSeparator2 == buffer[i + 1])) { return false; } i++; } this.i = i + 1; if (this.i >= length) { updateBuffer(); } return true; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/BomInput.java000066400000000000000000000150731400120543400312010ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import java.io.*; import java.nio.charset.*; import static com.univocity.parsers.common.ArgumentUtils.*; /** * A wrapper for an {@link InputStream} that attempts to detect a Byte Order Mark (BOM) in the input * and derive the character encoding that should be used to decode the incoming content. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public final class BomInput extends InputStream { public static final byte[] UTF_8_BOM = toByteArray(0xEF, 0xBB, 0xBF); public static final byte[] UTF_16BE_BOM = toByteArray(0xFE, 0xFF); public static final byte[] UTF_16LE_BOM = toByteArray(0xFF, 0xFE); public static final byte[] UTF_32BE_BOM = toByteArray(0x00, 0x00, 0xFE, 0xFF); public static final byte[] UTF_32LE_BOM = toByteArray(0xFF, 0xFE, 0x00, 0x00); private int bytesRead; private int bytes[] = new int[4]; private String encoding; private int consumed = 0; private final InputStream input; private IOException exception; /** * Wraps an {@link InputStream} and reads the first bytes found on it to attempt to read a BOM. * * @param input the input whose first bytes should be analyzed. */ public BomInput(InputStream input) { this.input = input; try { //This looks shitty on purpose (all in the name of speed). if ((bytes[0] = next()) == 0xEF) { if ((bytes[1] = next()) == 0xBB) { if ((bytes[2] = next()) == 0xBF) { setEncoding("UTF-8"); } } } else if (bytes[0] == 0xFE) { if ((bytes[1] = next()) == 0xFF) { setEncoding("UTF-16BE"); } } else if (bytes[0] == 0xFF) { if ((bytes[1] = next()) == 0xFE) { if ((bytes[2] = next()) == 0x00) { if ((bytes[3] = next()) == 0x00) { setEncoding("UTF-32LE"); } else { setEncoding("UTF-16LE"); //gotcha! } } else { setEncoding("UTF-16LE"); //gotcha! } } } else if (bytes[0] == 0x00) { if ((bytes[1] = next()) == 0x00) { if ((bytes[2] = next()) == 0xFE) { if ((bytes[3] = next()) == 0xFF) { setEncoding("UTF-32BE"); } } } } } catch (IOException e) { // store the exception for later. We want the wrapper to behave exactly like the original input stream and // might need to return any bytes read before this blew up. exception = e; } } private void setEncoding(String encoding) { this.encoding = encoding; if (encoding.equals("UTF-16LE")) { //gotcha! if (bytesRead == 3) { //third byte not a 0x00 bytesRead = 1; bytes[0] = bytes[2]; try { bytes[1] = next(); //reads next byte to be able to decode to a character } catch (Exception e) { exception = (IOException) e; } return; } else if (bytesRead == 4) { //fourth byte not a 0x00 bytesRead = 2; bytes[0] = bytes[2]; bytes[1] = bytes[3]; return; } } this.bytesRead = 0; } private int next() throws IOException { int out = input.read(); bytesRead++; return out; } @Override public final int read() throws IOException { if (bytesRead > 0 && bytesRead > consumed) { int out = bytes[consumed]; // Ensures that if the original input stream returned a byte, it will be consumed. // In case of exceptions, bytes produced prior to the exception will still be returned. // Once the last byte has been consumed, the original exception will be thrown. if (++consumed == bytesRead && exception != null) { throw exception; } return out; } if (consumed == bytesRead) { consumed++; return -1; } throw new BytesProcessedNotification(input, encoding); } /** * Returns a flag indicating whether or not all bytes read from the wrapped input stream have been consumed. This * allows client code to determine if the original input stream can be used directly and safely, or if this * {@code BomInput} wrapper class should be used instead. * * If there are stored bytes that need to be consumed before the wrapped input stream is consumed again, * this method will return {@code true}. * * @return {@code false} if there are no bytes stored and the original input stream can be used directly. If this wrapper * needs to be used to return stored bytes before, then {@code true} will be returned. */ public final boolean hasBytesStored() { return bytesRead > 0; } /** * Returns the detected {@link Charset} determined by the Byte Order Mark (BOM) available in the * input provided in the constructor of this class. * * If no BOM was detected, this method will return {@code null}. * * @return the detected {@link Charset} or {@code null} if a BOM could not be matched. */ public final Charset getCharset() { if (encoding == null) { return null; } return Charset.forName(encoding); } /** * Returns the detected encoding name determined by the Byte Order Mark (BOM) available in the * input provided in the constructor of this class. * * If no BOM was detected, this method will return {@code null}. * * @return the detected encoding name or {@code null} if a BOM could not be matched. */ public final String getEncoding() { return encoding; } /** * Internal notification exception used to re-wrap the original {@link InputStream} into a {@link Reader}. * This is required for performance reasons as overriding {@link InputStream#read()} incurs a heavy performance * penalty when the implementation is native (as in {@link FileInputStream#read()}. */ public static final class BytesProcessedNotification extends RuntimeException { public final InputStream input; public final String encoding; public BytesProcessedNotification(InputStream input, String encoding) { this.input = input; this.encoding = encoding; } @Override public Throwable fillInStackTrace() { return this; } } @Override public void close() throws IOException { input.close(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/CharAppender.java000077500000000000000000000303431400120543400320000ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; /** * * The general interface for classes responsible for appending characters efficiently while handling whitespaces and padding characters. * *

Calls to {@link CharAppender#appendIgnoringWhitespace(char)}, {@link CharAppender#appendIgnoringPadding(char, char)} and {@link CharAppender#appendIgnoringWhitespaceAndPadding(char, char)} should accumulate the * given character and only discard whitespaces/padding if no non-whitespace is appended: * *

For example: * *


 * append('a');                   // accumulated value is now "a";        whitespaceCount = 0;
 * appendIgnoringWhitespace('b'); // accumulated value is now "ab";       whitespaceCount = 0;
 * appendIgnoringWhitespace(' '); // accumulated value remains "ab";      whitespaceCount = 1;
 * appendIgnoringWhitespace(' '); // accumulated value remains "ab";      whitespaceCount = 2;
 * appendIgnoringWhitespace('c'); // accumulated value is now "ab  c";    whitespaceCount = 0;
 * appendIgnoringWhitespace(' '); // accumulated value remains "ab  c";   whitespaceCount = 1;
 * appendIgnoringWhitespace('d'); // accumulated value is now "ab  c d";  whitespaceCount = 0;
 * append(' ');					  // accumulated value is now "ab  c d "; whitespaceCount = 0;
 *  

* *

Implementation note: White spaces should be identified as any character {@code <= ' '} * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface CharAppender extends CharSequence { /** * Appends the given character and marks it as ignored if it is a whitespace ({@code ch <= ' '}) * @param ch character to append */ void appendIgnoringWhitespace(char ch); /** * Appends the given character and marks it as ignored if it is a padding character (the definition of a padding character is implementation dependent.) * @param ch character to append * @param padding the padding character to ignore */ void appendIgnoringPadding(char ch, char padding); /** * Appends the given character and marks it as ignored if it is a whitespace ({@code ch <= ' '}) or a padding character (the definition of a padding character is implementation dependent.) * @param ch character to append * @param padding the padding character to ignore */ void appendIgnoringWhitespaceAndPadding(char ch, char padding); /** * Appends the given character. * @param ch the character to append */ void append(char ch); /** * Returns first the position of a given character * @param ch the character to look for * @param from the starting index from where the search will begin. * @return the position of the given character in the appended content, {@code -1} if not found */ int indexOf(char ch, int from); /** * Returns first the position of a given character sequence * @param charSequence the character sequence to look for * @param from the starting index from where the search will begin. * @return the position of the given character sequence in the appended content, {@code -1} if not found */ int indexOf(char[] charSequence, int from); /** * Returns first the position of a given character sequence * @param charSequence the character sequence to look for * @param from the starting index from where the search will begin. * @return the position of the given character sequence in the appended content, {@code -1} if not found */ int indexOf(CharSequence charSequence, int from); /** * Returns the first position of any given character * @param chars the characters to look for * @param from the starting index from where the search will begin. * @return the position any one of the given characters in the appended content, {@code -1} if none found */ int indexOfAny(char[] chars, int from); /** * Returns a section of the appended content * @param from the starting position in the buffer * @param length the number of characters to accumulate from the given start position * @return a {@code String} with the section of characters accumulated by this appender. */ String substring(int from, int length); /** * Removes a section from the appended content * @param from the starting position in the buffer (inclusive) * @param length the number of characters to accumulate from the given start position */ void remove(int from, int length); /** * Appends the given codepoint. * @param ch the codepoint to append */ void append(int ch); /** * Appends the {@code String} representation of a given object. * @param obj the object whose {@code String} representation will be appended. */ void append(Object obj); /** * Returns the current accumulated value length (the sum of all appended characters - whitespaceCount). * @return the current accumulated value length (the sum of all appended characters - whitespaceCount). */ int length(); /** * Returns the current number of whitespaces accumulated after the last non-whitespace character. *

This is the number of whitespaces accumulated using {@link CharAppender#appendIgnoringWhitespace(char)}, {@link CharAppender#appendIgnoringPadding(char, char)} or {@link CharAppender#appendIgnoringWhitespaceAndPadding(char, char)} * @return the number of whitespaces accumulated using {@link CharAppender#appendIgnoringWhitespace(char)}, {@link CharAppender#appendIgnoringPadding(char, char)} or {@link CharAppender#appendIgnoringWhitespaceAndPadding(char, char)} */ int whitespaceCount(); /** * Resets the number of whitespaces accumulated after the last non-whitespace character. *

This is the number of whitespaces accumulated using {@link CharAppender#appendIgnoringWhitespace(char)}, {@link CharAppender#appendIgnoringPadding(char, char)} or {@link CharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

A subsequent call to {@link CharAppender#whitespaceCount()} should return 0. */ void resetWhitespaceCount(); /** * Returns the accumulated value as a String, discarding any trailing whitespace characters identified when using {@link CharAppender#appendIgnoringWhitespace(char)}, {@link CharAppender#appendIgnoringPadding(char, char)} or {@link CharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

The internal accumulated value is discarded after invoking this method (as in {@link CharAppender#reset()}) * @return a String containing the accumulated characters without the trailing whitespaces. */ String getAndReset(); /** * Clears the accumulated value and the whitespace count. */ void reset(); /** * Returns the accumulated characters, discarding any trailing whitespace characters identified when using {@link CharAppender#appendIgnoringWhitespace(char)}, {@link CharAppender#appendIgnoringPadding(char, char)} or {@link CharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

The internal accumulated value is discarded after invoking this method (as in {@link CharAppender#reset()}) * @return a character array containing the accumulated characters without the trailing whitespaces. */ char[] getCharsAndReset(); /** * Returns the internal character array. * @return the internal character array. */ char[] getChars(); /** * Adds a sequence of repeated characters to the input. * @param ch the character to append * @param length the number of times the given character should be appended. */ void fill(char ch, int length); /** * Prepends the current accumulated value with a character * @param ch the character to prepend in front of the current accumulated value. */ void prepend(char ch); /** * Prepends the current accumulated value with a couple of characters * @param ch1 the first character to prepend in front of the current accumulated value. * @param ch2 the second character to prepend in front of the current accumulated value. */ void prepend(char ch1, char ch2); /** * Prepends the current accumulated value a sequence of characters * @param chars the character sequence to prepend in front of the current accumulated value. */ void prepend(char[] chars); /** * Updates the internal whitespace count of this appender to trim trailing whitespaces. */ void updateWhitespace(); /** * Appends characters from the input, until a stop character is found * @param ch the first character of the input to be appended. * @param input the input whose the following characters will be appended * @param stop the stop character * @return the stop character found on the input. */ char appendUntil(char ch, CharInput input, char stop); /** * Appends characters from the input, until a stop character is found * @param ch the first character of the input to be appended. * @param input the input whose the following characters will be appended * @param stop1 the first stop character * @param stop2 the second stop character * @return one of the stop characters found on the input. */ char appendUntil(char ch, CharInput input, char stop1, char stop2); /** * Appends characters from the input, until a stop character is found * @param ch the first character of the input to be appended. * @param input the input whose the following characters will be appended * @param stop1 the first stop character * @param stop2 the second stop character * @param stop3 the third stop character * @return one of the stop characters found on the input. */ char appendUntil(char ch, CharInput input, char stop1, char stop2, char stop3); /** * Appends characters from an input array * @param ch the character array * @param from the position of the first character in the array to be appended * @param length the number of characters to be appended from the given posiion. */ void append(char[] ch, int from, int length); /** * Appends characters from an input array * @param ch the character array */ void append(char[] ch); /** * Appends codepoints from an input array * @param ch the codepoint array */ void append(int[] ch); /** * Appends characters from an input {@code String} * @param string the input String */ void append(String string); /** * Appends the contents of a String to this appender * * @param string the string whose characters will be appended. * @param from the index of the first character to append * @param to the index of the last character to append */ void append(String string, int from, int to); /** * Ignores the given number of characters at the end of the appended content, * effectively marking these as whitespace. Invoking {@link #resetWhitespaceCount()} * or {@link #updateWhitespace()} will undo this effect. * * @param count the number of characters to ignore */ void ignore(int count); /** * Deletes a given number of characters from the end of the appended content. * Will reset the internal whitespace count if any. Invoke {@link #updateWhitespace()} * to recalculate the number of trailing whitespaces in the appended content. * @param count the number of characters to delete. */ void delete(int count); /** * Indicates whether this appender represents an empty {@code String}. * @return {@code} true calling {@link #getAndReset()} would return {@code null}, otherwise {@code false}. */ boolean isEmpty(); /** * Returns the last index of a given character in the current appended (characters that have been marked as whitespace will be ignored) * @param ch the character to look for * @return the last position of the given character in the appended content, or {@code -1} if not found. */ int lastIndexOf(char ch); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/CharInput.java000077500000000000000000000027621400120543400313450ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; /** * A (very) basic character input definition. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see Format */ public interface CharInput { /** * Returns the next character in the input. * * @return the next character in the input. '\0' if there are no more characters in the input or if the CharInput is stopped. */ char nextChar(); /** * Returns the last character returned by the {@link #nextChar()} method. * * @return the last character returned by the {@link #nextChar()} method.'\0' if there are no more characters in the input or if the CharInput is stopped. */ char getChar(); }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/CharInputReader.java000077500000000000000000000240241400120543400324630ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; import java.io.*; /** * The definition of a character input reader used by all univocity-parsers that extend {@link AbstractParser}. * *

This interface declares basic functionalities to provide a common input manipulation structure for all parser classes. *

Implementations of this interface MUST convert the sequence of newline characters defined by {@link Format#getLineSeparator()} into the normalized newline character provided in {@link Format#getNormalizedNewline()}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.Format */ public interface CharInputReader extends CharInput { /** * Initializes the CharInputReader implementation with a {@link java.io.Reader} which provides access to the input. * * @param reader A {@link java.io.Reader} that provides access to the input. */ void start(Reader reader); /** * Stops the CharInputReader from reading characters from the {@link java.io.Reader} provided in {@link CharInputReader#start(Reader)} and closes it. */ void stop(); /** * Returns the next character in the input provided by the active {@link java.io.Reader}. *

If the input contains a sequence of newline characters (defined by {@link Format#getLineSeparator()}), this method will automatically converted them to the newline character specified in {@link Format#getNormalizedNewline()}. *

A subsequent call to this method will return the character after the newline sequence. * * @return the next character in the input. '\0' if there are no more characters in the input or if the CharInputReader was stopped. */ char nextChar(); /** * Returns the last character returned by the {@link #nextChar()} method. * * @return the last character returned by the {@link #nextChar()} method.'\0' if there are no more characters in the input or if the CharInputReader was stopped. */ char getChar(); /** * Returns the number of characters returned by {@link CharInputReader#nextChar()} at any given time. * * @return the number of characters returned by {@link CharInputReader#nextChar()} */ long charCount(); /** * Returns the number of newlines read so far. * * @return the number of newlines read so far. */ long lineCount(); /** * Skips characters in the input until the given number of lines is discarded. * * @param lineCount the number of lines to skip from the current location in the input */ void skipLines(long lineCount); /** * Collects the comment line found on the input. * * @return the text found in the comment from the current position. */ String readComment(); /** * Indicates to the input reader that the parser is running in "escape" mode and * new lines should be returned as-is to prevent modifying the content of the parsed value. * * @param escaping flag indicating that the parser is escaping values and line separators are to be returned as-is. */ void enableNormalizeLineEndings(boolean escaping); /** * Returns the line separator by this character input reader. This could be the line separator defined * in the {@link Format#getLineSeparator()} configuration, or the line separator sequence identified automatically * when {@link CommonParserSettings#isLineSeparatorDetectionEnabled()} evaluates to {@code true}. * * @return the line separator in use. */ char[] getLineSeparator(); /** * Skips characters from the current input position, until a non-whitespace character, or a stop character is found * * @param current the current character of the input * @param stopChar1 the first stop character (which can be a whitespace) * @param stopChar2 the second character (which can be a whitespace) * * @return the first non-whitespace character (or delimiter) found in the input. */ char skipWhitespace(char current, char stopChar1, char stopChar2); /** * Returns the length of the character sequence parsed to produce the current record. * @return the length of the text content parsed for the current input record */ int currentParsedContentLength(); /** * Returns a String with the input character sequence parsed to produce the current record. * * @return the text content parsed for the current input record. */ String currentParsedContent(); /** * Returns the last index of a given character in the current parsed content * @param ch the character to look for * @return the last position of the given character in the current parsed content, or {@code -1} if not found. */ int lastIndexOf(char ch); /** * Marks the start of a new record in the input, used internally to calculate the result of {@link #currentParsedContent()} */ void markRecordStart(); /** * Attempts to collect a {@code String} from the current position until a stop character is found on the input, * or a line ending is reached. If the {@code String} can be obtained, the current position of the parser will be updated to * the last consumed character. If the internal buffer needs to be reloaded, this method will return {@code null} * and the current position of the buffer will remain unchanged. * * @param ch the current character to be considered. If equal to the stop character the {@code nullValue} will be returned * @param stop the stop character that identifies the end of the content to be collected * @param trim flag indicating whether or not trailing whitespaces should be discarded * @param nullValue value to return when the length of the content to be returned is {@code 0}. * @param maxLength the maximum length of the {@code String} to be returned. If the length exceeds this limit, {@code null} will be returned * * @return the {@code String} found on the input, or {@code null} if the buffer needs to reloaded or the maximum length has been exceeded. */ String getString(char ch, char stop, boolean trim, String nullValue, int maxLength); /** * Attempts to skip a {@code String} from the current position until a stop character is found on the input, * or a line ending is reached. If the {@code String} can be skipped, the current position of the parser will be updated to * the last consumed character. If the internal buffer needs to be reloaded, this method will return {@code false} * and the current position of the buffer will remain unchanged. * * @param ch the current character to be considered. If equal to the stop character {@code false} will be returned * @param stop the stop character that identifies the end of the content to be collected * * @return {@code true} if an entire {@code String} value was found on the input and skipped, or {@code false} if the buffer needs to reloaded. */ boolean skipString(char ch, char stop); /** * Attempts to collect a quoted {@code String} from the current position until a closing quote or stop character is found on the input, * or a line ending is reached. If the {@code String} can be obtained, the current position of the parser will be updated to * the last consumed character. If the internal buffer needs to be reloaded, this method will return {@code null} * and the current position of the buffer will remain unchanged. * * @param quote the quote character * @param escape the quote escape character * @param escapeEscape the escape of the quote escape character * @param maxLength the maximum length of the {@code String} to be returned. If the length exceeds this limit, {@code null} will be returned * @param stop1 the first stop character that identifies the end of the content to be collected * @param stop2 the second stop character that identifies the end of the content to be collected * @param keepQuotes flag to indicate the quotes that wrap the resulting {@code String} should be kept. * @param keepEscape flag to indicate that escape sequences should be kept * @param trimLeading flag to indicate leading whitespaces should be trimmed * @param trimTrailing flag to indicate that trailing whitespaces should be trimmed * @return the {@code String} found on the input, or {@code null} if the buffer needs to reloaded or the maximum length has been exceeded. */ String getQuotedString(char quote, char escape, char escapeEscape, int maxLength, char stop1, char stop2, boolean keepQuotes, boolean keepEscape, boolean trimLeading, boolean trimTrailing); /** * Attempts to skip a quoted {@code String} from the current position until a stop character is found on the input, * or a line ending is reached. If the {@code String} can be skipped, the current position of the parser will be updated to * the last consumed character. If the internal buffer needs to be reloaded, this method will return {@code false} * and the current position of the buffer will remain unchanged. * * @param quote the quote character * @param escape the quote escape character * @param stop1 the first stop character that identifies the end of the content to be collected * @param stop2 the second stop character that identifies the end of the content to be collected * * @return {@code true} if an entire {@code String} value was found on the input and skipped, or {@code false} if the buffer needs to reloaded. */ boolean skipQuotedString(char quote, char escape, char stop1, char stop2); }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/DefaultCharAppender.java000077500000000000000000000275101400120543400333070ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; /** * Default implementation of the {@link CharAppender} interface */ public class DefaultCharAppender implements CharAppender { final int whitespaceRangeStart; final char[] emptyChars; // default value to return when no characters have been accumulated char[] chars; int index; final String emptyValue; // default value to return when no characters have been accumulated int whitespaceCount; /** * Creates a DefaultCharAppender with a maximum limit of characters to append and the default value to return when no characters have been accumulated. * The padding character is defaulted to a whitespace character ' '. * * @param maxLength maximum limit of characters to append * @param emptyValue default value to return when no characters have been accumulated * @param whitespaceRangeStart starting range of characters considered to be whitespace. */ public DefaultCharAppender(int maxLength, String emptyValue, int whitespaceRangeStart) { this.whitespaceRangeStart = whitespaceRangeStart; this.chars = new char[maxLength]; this.emptyValue = emptyValue; if (emptyValue == null) { emptyChars = null; } else { emptyChars = emptyValue.toCharArray(); } } @Override public void appendIgnoringPadding(char ch, char padding) { chars[index++] = ch; if (ch == padding) { whitespaceCount++; } else { whitespaceCount = 0; } } @Override public void appendIgnoringWhitespaceAndPadding(char ch, char padding) { chars[index++] = ch; if (ch == padding || (ch <= ' ' && whitespaceRangeStart < ch)) { whitespaceCount++; } else { whitespaceCount = 0; } } @Override public void appendIgnoringWhitespace(char ch) { chars[index++] = ch; if (ch <= ' ' && whitespaceRangeStart < ch) { whitespaceCount++; } else { whitespaceCount = 0; } } @Override public int indexOf(char ch, int from) { int len = index - whitespaceCount; for (int i = from; i < len; i++) { if (chars[i] == ch) { return i; } } return -1; } @Override public int indexOfAny(char[] chars, int from) { int len = index - whitespaceCount; for (int i = from; i < len; i++) { for (int j = 0; j < chars.length; j++) { if (this.chars[i] == chars[j]) { return i; } } } return -1; } @Override public String substring(int from, int length) { return new String(chars, from, length); } @Override public void remove(int from, int length) { if (length > 0) { int srcPos = from + length; int len = index - length; if (srcPos + len > index) { len = len - from; } System.arraycopy(chars, srcPos, chars, from, len); index -= length; } } @Override public void append(char ch) { chars[index++] = ch; } @Override public final void append(Object o) { append(String.valueOf(o)); } @Override public final void append(int ch) { if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { append((char) ch); } else { int off = ch - Character.MIN_SUPPLEMENTARY_CODE_POINT; append((char) ((off >>> 10) + Character.MIN_HIGH_SURROGATE)); append((char) ((off & 0x3ff) + Character.MIN_LOW_SURROGATE)); } } @Override public final void append(int[] ch) { for (int i = 0; i < ch.length; i++) { append(ch[i]); } } /** * Returns the accumulated value as a String, discarding any trailing whitespace characters identified when using {@link DefaultCharAppender#appendIgnoringWhitespace(char)}, {@link DefaultCharAppender#appendIgnoringPadding(char, char)} or {@link DefaultCharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

The internal accumulated value is discarded after invoking this method (as in {@link DefaultCharAppender#reset()}) *

If the accumulated value is empty (i.e. no characters were appended, or all appended characters where ignored as whitespace or padding), then the return value will be {@link DefaultCharAppender#emptyValue} attribute defined in the constructor of this class. * * @return a String containing the accumulated characters without the trailing white spaces. Or the {@link DefaultCharAppender#emptyValue} defined in the constructor of this class. */ @Override public String getAndReset() { String out = emptyValue; if (index > whitespaceCount) { out = new String(chars, 0, index - whitespaceCount); } index = 0; whitespaceCount = 0; return out; } /** * Returns the accumulated value as a String, discarding any trailing whitespace characters identified when using {@link DefaultCharAppender#appendIgnoringWhitespace(char)}, {@link DefaultCharAppender#appendIgnoringPadding(char, char)} or {@link DefaultCharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

Does not discard the accumulated value. *

If the accumulated value is empty (i.e. no characters were appended, or all appended characters where ignored as whitespace or padding), then the return value will be {@link DefaultCharAppender#emptyValue} attribute defined in the constructor of this class. * * @return a String containing the accumulated characters without the trailing white spaces. Or the {@link DefaultCharAppender#emptyValue} defined in the constructor of this class. */ @Override public final String toString() { if (index <= whitespaceCount) { return emptyValue; } return new String(chars, 0, index - whitespaceCount); } @Override public final int length() { return index - whitespaceCount; } /** * Returns the accumulated characters, discarding any trailing whitespace characters identified when using {@link DefaultCharAppender#appendIgnoringWhitespace(char)}, {@link DefaultCharAppender#appendIgnoringPadding(char, char)} or {@link DefaultCharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

The internal accumulated value is discarded after invoking this method (as in {@link DefaultCharAppender#reset()}) *

If the accumulated value is empty (i.e. no characters were appended, or all appended characters where ignored as whitespace or padding), then the return value will be character sequence of the {@link DefaultCharAppender#emptyValue} attribute defined in the constructor of this class. * * @return a character array containing the accumulated characters without the trailing white spaces. Or the characters of the {@link DefaultCharAppender#emptyValue} defined in the constructor of this class. */ @Override public char[] getCharsAndReset() { char[] out = emptyChars; if (index > whitespaceCount) { int length = index - whitespaceCount; out = new char[length]; System.arraycopy(chars, 0, out, 0, length); } index = 0; whitespaceCount = 0; return out; } @Override public final int whitespaceCount() { return whitespaceCount; } @Override public void reset() { index = 0; whitespaceCount = 0; } /** * Appends the contents of another DefaultCharAppender, discarding any of its trailing whitespace characters * * @param appender The DefaultCharAppender instance got get contents from. */ public void append(DefaultCharAppender appender) { System.arraycopy(appender.chars, 0, this.chars, this.index, appender.index - appender.whitespaceCount); this.index += appender.index - appender.whitespaceCount; appender.reset(); } @Override public final void resetWhitespaceCount() { whitespaceCount = 0; } @Override public final char[] getChars() { return chars; } @Override public void fill(char ch, int length) { for (int i = 0; i < length; i++) { chars[index++] = ch; } } /** * Prepends the current accumulated value with a character * * @param ch the character to prepend in front of the current accumulated value. */ @Override public void prepend(char ch) { System.arraycopy(chars, 0, this.chars, 1, index); chars[0] = ch; index++; } @Override public void prepend(char ch1, char ch2) { System.arraycopy(chars, 0, this.chars, 2, index); chars[0] = ch1; chars[1] = ch2; index += 2; } @Override public void prepend(char[] chars) { System.arraycopy(this.chars, 0, this.chars, chars.length, index); System.arraycopy(chars, 0, this.chars, 0, chars.length); index += chars.length; } /** * Updates the internal whitespace count of this appender to trim trailing whitespaces. */ public final void updateWhitespace() { whitespaceCount = 0; for (int i = index - 1; i >= 0 && chars[i] <= ' ' && whitespaceRangeStart < chars[i]; i--, whitespaceCount++) ; } public char appendUntil(char ch, CharInput input, char stop) { for (; ch != stop; ch = input.nextChar()) { chars[index++] = ch; } return ch; } public char appendUntil(char ch, CharInput input, char stop1, char stop2) { for (; ch != stop1 && ch != stop2; ch = input.nextChar()) { chars[index++] = ch; } return ch; } public char appendUntil(char ch, CharInput input, char stop1, char stop2, char stop3) { for (; ch != stop1 && ch != stop2 && ch != stop3; ch = input.nextChar()) { chars[index++] = ch; } return ch; } @Override public void append(char[] ch, int from, int length) { System.arraycopy(ch, from, chars, index, length); index += length; } @Override public final void append(char[] ch) { append(ch, 0, ch.length); } public void append(String string, int from, int to) { string.getChars(from, to, chars, index); index += to - from; } @Override public final void append(String string) { append(string, 0, string.length()); } @Override public final char charAt(int i) { return chars[i]; } @Override public final String subSequence(int from, int to) { return new String(chars, from, to - from); } @Override public final void ignore(int count) { whitespaceCount += count; } @Override public void delete(int count) { index -= count; if (index < 0) { index = 0; } whitespaceCount = 0; } @Override public int indexOf(char[] charSequence, int fromIndex) { if (charSequence.length == 0) { return fromIndex; } if (fromIndex >= index) { return -1; } char first = charSequence[0]; int max = index - charSequence.length; for (int i = fromIndex; i <= max; i++) { if (chars[i] != first) { while (++i <= max && chars[i] != first) ; } if (i <= max) { int j = i + 1; int end = j + charSequence.length - 1; for (int k = 1; j < end && chars[j] == charSequence[k]; j++, k++) ; if (j == end) { return i; } } } return -1; } @Override public int indexOf(CharSequence charSequence, int fromIndex) { if (charSequence.length() == 0) { return fromIndex; } if (fromIndex >= index) { return -1; } char first = charSequence.charAt(0); int max = index - charSequence.length(); for (int i = fromIndex; i <= max; i++) { if (chars[i] != first) { while (++i <= max && chars[i] != first) ; } if (i <= max) { int j = i + 1; int end = j + charSequence.length() - 1; for (int k = 1; j < end && chars[j] == charSequence.charAt(k); j++, k++) ; if (j == end) { return i; } } } return -1; } @Override public boolean isEmpty() { return index > whitespaceCount; } @Override public int lastIndexOf(char ch) { for (int x = index - whitespaceCount -1; x >= 0; x--) { if (chars[x] == ch) { return x; } } return -1; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/DefaultCharInputReader.java000077500000000000000000000077201400120543400337740ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; import java.io.*; /** * A default CharInputReader which only loads batches of characters when requested by the {@link AbstractCharInputReader} through the {@link DefaultCharInputReader#reloadBuffer} method. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class DefaultCharInputReader extends AbstractCharInputReader { private Reader reader; private boolean unwrapping = false; /** * Creates a new instance with the mandatory characters for handling newlines transparently. Line separators will be detected automatically. * * @param normalizedLineSeparator the normalized newline character (as defined in {@link Format#getNormalizedNewline()}) that is used to replace any lineSeparator sequence found in the input. * @param bufferSize the buffer size used to store characters read from the input. * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param closeOnStop indicates whether to automatically close the input when {@link #stop()} is called */ public DefaultCharInputReader(char normalizedLineSeparator, int bufferSize, int whitespaceRangeStart, boolean closeOnStop) { super(normalizedLineSeparator, whitespaceRangeStart, closeOnStop); super.buffer = new char[bufferSize]; } /** * Creates a new instance with the mandatory characters for handling newlines transparently. * * @param lineSeparator the sequence of characters that represent a newline, as defined in {@link Format#getLineSeparator()} * @param normalizedLineSeparator the normalized newline character (as defined in {@link Format#getNormalizedNewline()}) that is used to replace any lineSeparator sequence found in the input. * @param bufferSize the buffer size used to store characters read from the input. * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param closeOnStop indicates whether to automatically close the input when {@link #stop()} is called */ public DefaultCharInputReader(char[] lineSeparator, char normalizedLineSeparator, int bufferSize, int whitespaceRangeStart, boolean closeOnStop) { super(lineSeparator, normalizedLineSeparator, whitespaceRangeStart, closeOnStop); super.buffer = new char[bufferSize]; } @Override public void stop() { try { if (!unwrapping && closeOnStop && reader != null) { reader.close(); } } catch (IOException e) { throw new IllegalStateException("Error closing input", e); } } @Override protected void setReader(Reader reader) { this.reader = reader; unwrapping = false; } /** * Copies a sequence of characters from the input into the {@link DefaultCharInputReader#buffer}, and updates the {@link DefaultCharInputReader#length} to the number of characters read. */ @Override public void reloadBuffer() { try { super.length = reader.read(buffer, 0, buffer.length); } catch (IOException e) { throw new IllegalStateException("Error reading from input", e); } catch (BomInput.BytesProcessedNotification notification) { unwrapping = true; unwrapInputStream(notification); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/EOFException.java000066400000000000000000000022041400120543400317240ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; /** * Internal exception marker to signalize the end of the input. */ public final class EOFException extends RuntimeException { private static final long serialVersionUID = -4064380464076294133L; /** * Creates a new exception */ public EOFException() { super(); } @Override public Throwable fillInStackTrace() { return this; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/ElasticCharAppender.java000066400000000000000000000037531400120543400333070ustar00rootroot00000000000000package com.univocity.parsers.common.input; /** * A character appender that restores its internal buffer size after expanding to accommodate larger contents. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public class ElasticCharAppender extends ExpandingCharAppender { private static final char[] EMPTY_CHAR_ARRAY = new char[0]; private int defaultLength; public ElasticCharAppender(String emptyValue) { this(4096, emptyValue); } public ElasticCharAppender(int defaultLength, String emptyValue) { super(defaultLength, emptyValue, 0); this.defaultLength = defaultLength; } @Override public String getAndReset() { String out = super.getAndReset(); if (chars.length > defaultLength) { chars = new char[defaultLength]; } return out; } @Override public char[] getCharsAndReset() { char[] out = super.getCharsAndReset(); if (chars.length > defaultLength) { chars = new char[defaultLength]; } return out; } @Override public void reset() { if (chars.length > defaultLength) { chars = new char[defaultLength]; } super.reset(); } public String getTrimmedStringAndReset() { int length = index - whitespaceCount; int start = 0; while (start < length && chars[start] <= ' ') { start++; } if (start >= length) { return emptyValue; } while (chars[length - 1] <= ' ') { length--; } length -= start; if (length <= 0) { return emptyValue; } String out = new String(chars, start, length); reset(); return out; } public char[] getTrimmedCharsAndReset() { int length = index - whitespaceCount; int start = 0; while (start < length && chars[start] <= ' ') { start++; } if (start >= length) { return EMPTY_CHAR_ARRAY; } while (chars[length - 1] <= ' ') { length--; } length -= start; if (length <= 0) { return EMPTY_CHAR_ARRAY; } char[] out = new char[length]; System.arraycopy(chars, start, out, 0, length); reset(); return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/ExpandingCharAppender.java000066400000000000000000000137621400120543400336410ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; import java.util.*; /** * An implementation {@link CharAppender} that expands the internal buffer of characters as required. * * @author Univocity Software Pty Ltd - dev@univocity.com */ public class ExpandingCharAppender extends DefaultCharAppender { private static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; /** * Creates an {@code ExpandingCharAppender} a the default value to return when no characters have been accumulated. * The padding character is defaulted to a whitespace character ' '. * * @param emptyValue default value to return when no characters have been accumulated * @param whitespaceRangeStart starting range of characters considered to be whitespace. */ public ExpandingCharAppender(String emptyValue, int whitespaceRangeStart) { this(8192, emptyValue, whitespaceRangeStart); } /** * Creates an {@code ExpandingCharAppender} a the default value to return when no characters have been accumulated. * The padding character is defaulted to a whitespace character ' '. * * @param initialBufferLength the initial length of the internal buffer. * @param emptyValue default value to return when no characters have been accumulated * @param whitespaceRangeStart starting range of characters considered to be whitespace. */ public ExpandingCharAppender(int initialBufferLength, String emptyValue, int whitespaceRangeStart) { super(initialBufferLength, emptyValue, whitespaceRangeStart); } @Override public void appendIgnoringWhitespace(char ch) { try { super.appendIgnoringWhitespace(ch); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); super.appendIgnoringWhitespace(ch); } } @Override public void appendIgnoringPadding(char ch, char padding) { try { super.appendIgnoringPadding(ch, padding); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); super.appendIgnoringPadding(ch, padding); } } @Override public void appendIgnoringWhitespaceAndPadding(char ch, char padding) { try { super.appendIgnoringWhitespaceAndPadding(ch, padding); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); super.appendIgnoringWhitespaceAndPadding(ch, padding); } } @Override public void append(char ch) { try { super.append(ch); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); super.append(ch); } } @Override public final void fill(char ch, int length) { try { super.fill(ch, length); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); super.fill(ch, length); } } final void expandAndRetry() { expand(); index--; } private void expand(int additionalLength, double factor) { if (chars.length == MAX_ARRAY_LENGTH) { throw new TextParsingException(null, "Can't expand internal appender array to over " + MAX_ARRAY_LENGTH + " characters in length."); } chars = Arrays.copyOf(chars, (int) Math.min(((index + additionalLength) * factor), MAX_ARRAY_LENGTH)); } final void expand() { expand(0, 2.0); } final void expand(int additionalLength) { expand(additionalLength, 1.5); } @Override public final void prepend(char ch) { try { super.prepend(ch); } catch (ArrayIndexOutOfBoundsException e) { expand(); super.prepend(ch); } } @Override public final void prepend(char ch1, char ch2) { try { super.prepend(ch1, ch2); } catch (ArrayIndexOutOfBoundsException e) { expand(2); super.prepend(ch1, ch2); } } @Override public final void prepend(char[] chars) { try { super.prepend(chars); } catch (ArrayIndexOutOfBoundsException e) { expand(chars.length); super.prepend(chars); } } public final void append(DefaultCharAppender appender) { try { super.append(appender); } catch (ArrayIndexOutOfBoundsException e) { expand(appender.index); this.append(appender); } } public final char appendUntil(char ch, CharInput input, char stop) { try { return super.appendUntil(ch, input, stop); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); return this.appendUntil(input.getChar(), input, stop); } } public final char appendUntil(char ch, CharInput input, char stop1, char stop2) { try { return super.appendUntil(ch, input, stop1, stop2); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); return this.appendUntil(input.getChar(), input, stop1, stop2); } } public final char appendUntil(char ch, CharInput input, char stop1, char stop2, char stop3) { try { return super.appendUntil(ch, input, stop1, stop2, stop3); } catch (ArrayIndexOutOfBoundsException e) { expandAndRetry(); return this.appendUntil(input.getChar(), input, stop1, stop2, stop3); } } @Override public final void append(char[] ch, int from, int length) { if (index + length <= chars.length) { super.append(ch, from, length); } else { chars = Arrays.copyOf(chars, Math.min(((chars.length + length + index)), MAX_ARRAY_LENGTH)); super.append(ch, from, length); } } public final void append(String string, int from, int to) { try { super.append(string, from, to); } catch (IndexOutOfBoundsException e) { expand(to - from); super.append(string, from, to); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/InputAnalysisProcess.java000066400000000000000000000032501400120543400336000ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; /** * A process to be executed over a sample of data being parsed. When {@link AbstractCharInputReader#reloadBuffer()} loads a batch of characters from the input, * the {@code InputAnalysisProcess} will be executed and then discarded. * *

Parsers can implement their custom analysis processes to identify patterns and attempt to automatically derive configuration options to process the input * by calling {@link AbstractCharInputReader#addInputAnalysisProcess(InputAnalysisProcess)} at any time.

* * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface InputAnalysisProcess { /** * A sequence of characters of the input buffer to be analyzed. * @param characters the input buffer * @param length the last character position loaded into the buffer. */ void execute(char[] characters, int length); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/LineSeparatorDetector.java000066400000000000000000000036061400120543400337050ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; /** * An {@link InputAnalysisProcess} to detect the line separators used in the input. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class LineSeparatorDetector implements InputAnalysisProcess { @Override public void execute(char[] characters, int length) { char separator1 = '\0'; char separator2 = '\0'; for (int c = 0; c < length; c++) { char ch = characters[c]; if (ch == '\n' || ch == '\r') { if (separator1 == '\0') { separator1 = ch; } else { separator2 = ch; break; } } else if (separator1 != '\0') { break; } } char lineSeparator1 = separator1; char lineSeparator2 = separator2; if (separator1 != '\0') { if (separator1 == '\n') { lineSeparator1 = '\n'; lineSeparator2 = '\0'; } else { lineSeparator1 = '\r'; if (separator2 == '\n') { lineSeparator2 = '\n'; } else { lineSeparator2 = '\0'; } } } apply(lineSeparator1, lineSeparator2); } protected abstract void apply(char lineSeparator1, char lineSeparator2); } LookaheadCharInputReader.java000066400000000000000000000174171400120543400342210ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import java.io.*; import java.util.*; /** * A special implementation of {@link CharInputReader} that wraps another {@link CharInputReader} and * collects a sequence of characters from the wrapped input, in order to analyze what the buffer contains * ahead of the current position. */ public class LookaheadCharInputReader implements CharInputReader { private final CharInputReader reader; private char[] lookahead = new char[0]; private int length = 0; private int start = 0; private final char newLine; private char delimiter; private final int whitespaceRangeStart; /** * Creates a lookahead input reader by wrapping a given {@link CharInputReader} implementation * * @param reader the input reader whose characters will read and stored in a limited internal buffer, * in order to allow a parser to query what the characters are available ahead of the current input position. * @param newLine the normalized character that represents a line ending. Used internally as a stop character. * @param whitespaceRangeStart starting range of characters considered to be whitespace. */ public LookaheadCharInputReader(CharInputReader reader, char newLine, int whitespaceRangeStart) { this.reader = reader; this.newLine = newLine; this.whitespaceRangeStart = whitespaceRangeStart; } /** * Matches a sequence of characters against the current lookahead buffer. * * @param current the last character used by the parser, which should match the first character in the lookahead buffer * @param sequence the expected sequence of characters after the current character, that are expected appear in the current lookahead buffer * @param wildcard character used in the sequence as a wildcard (e.g. * or ?), meaning any character is acceptable in its place. * * @return {@code true} if the current character and the sequence characters that follows are present in the lookahead, otherwise {@code false} */ public boolean matches(char current, char[] sequence, char wildcard) { if (sequence.length > length - start) { return false; } if (sequence[0] != current && sequence[0] != wildcard) { return false; } for (int i = 1; i < sequence.length; i++) { char ch = sequence[i]; if (ch != wildcard && ch != lookahead[i - 1 + start]) { return false; } } return true; } /** * Matches a sequence of characters against the current lookahead buffer. * * @param sequence the expected sequence of characters that are expected appear in the current lookahead buffer * @param wildcard character used in the sequence as a wildcard (e.g. * or ?), meaning any character is acceptable in its place. * * @return {@code true} if the given sequence of characters is present in the lookahead, otherwise {@code false} */ public boolean matches(char[] sequence, char wildcard) { if (sequence.length > length - start) { return false; } for (int i = 0; i < sequence.length; i++) { char ch = sequence[i]; if (ch != wildcard && sequence[i] != lookahead[i + start]) { return false; } } return true; } /** * Returns the current lookahead value. * * @return the current lookahead value, or an empty {@code String} if the lookahead buffer is empty. */ public String getLookahead() { if (start >= length) { return ""; } return new String(lookahead, start, length); } /** * Returns the lookahead value prepended with the current character * * @param current the current character obtained by the parser * * @return a {@code String} formed by the given character followed by the lookahead value (if any). */ public String getLookahead(char current) { if (start >= length) { return String.valueOf(current); } return current + new String(lookahead, start, length - 1); } /** * Fills the lookahead buffer with a given number of characters that will be extracted from the wrapped {@link CharInputReader} * * @param numberOfCharacters the number of characters to read from the wrapped {@link CharInputReader}, given in the constructor of this class. */ public void lookahead(int numberOfCharacters) { numberOfCharacters += length - start; if (lookahead.length < numberOfCharacters) { lookahead = Arrays.copyOf(lookahead, numberOfCharacters); } if (start >= length) { start = 0; length = 0; } try { numberOfCharacters -= length; while (numberOfCharacters-- > 0) { lookahead[length] = reader.nextChar(); length++; } } catch (EOFException ex) { //ignore. } } @Override public void start(Reader reader) { this.reader.start(reader); } @Override public void stop() { this.reader.stop(); } @Override public char nextChar() { if (start >= length) { return reader.nextChar(); } else { return lookahead[start++]; } } @Override public long charCount() { return reader.charCount(); } @Override public long lineCount() { return reader.lineCount(); } @Override public void skipLines(long lineCount) { reader.skipLines(lineCount); } @Override public void enableNormalizeLineEndings(boolean escaping) { reader.enableNormalizeLineEndings(escaping); } @Override public String readComment() { return reader.readComment(); } @Override public char[] getLineSeparator() { return reader.getLineSeparator(); } @Override public final char getChar() { if (start != 0 && start >= length) { return reader.getChar(); } else { return lookahead[start - 1]; } } @Override public char skipWhitespace(char ch, char stopChar1, char stopChar2) { while (start < length && ch <= ' ' && ch != stopChar1 && ch != newLine && ch != stopChar2 && whitespaceRangeStart < ch) { ch = lookahead[start++]; } return reader.skipWhitespace(ch, stopChar1, stopChar2); } @Override public String currentParsedContent() { return reader.currentParsedContent(); } @Override public void markRecordStart() { reader.markRecordStart(); } @Override public String getString(char ch, char stop, boolean trim, String nullValue, int maxLength) { return reader.getString(ch, stop, trim, nullValue, maxLength); } @Override public String getQuotedString(char quote, char escape, char escapeEscape, int maxLength, char stop1, char stop2, boolean keepQuotes, boolean keepEscape, boolean trimLeading, boolean trimTrailing) { return reader.getQuotedString(quote, escape, escapeEscape, maxLength, stop1, stop2, keepQuotes, keepEscape, trimLeading, trimTrailing); } @Override public int currentParsedContentLength() { return reader.currentParsedContentLength(); } @Override public boolean skipString(char ch, char stop) { return reader.skipString(ch, stop); } @Override public boolean skipQuotedString(char quote, char escape, char stop1, char stop2) { return reader.skipQuotedString(quote, escape, stop1, stop2); } @Override public int lastIndexOf(char ch) { return reader.lastIndexOf(ch); } public void reloadBuffer() { if (reader instanceof DefaultCharInputReader) { ((DefaultCharInputReader) reader).reloadBuffer(); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/NoopCharAppender.java000077500000000000000000000127361400120543400326420ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; /** * An implementation of {@link CharAppender} that does nothing. Used by {@link ParserOutput} to transparently discard any unwanted input while parsing. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.ParserOutput * @see com.univocity.parsers.common.input.CharAppender */ public class NoopCharAppender implements CharAppender { private static final NoopCharAppender instance = new NoopCharAppender(); /** * Returns the singleton instance of NoopCharAppender * * @return the singleton instance of NoopCharAppender */ public static CharAppender getInstance() { return instance; } /** * This is a singleton class and cannot be instantiated. Use {@link NoopCharAppender#getInstance()}. */ private NoopCharAppender() { } /** * Returns -1 as this appender does nothing. * * @return -1 as this appender does nothing. */ @Override public int length() { return -1; } /** * Returns null as this appender does nothing. * * @return null as this appender does nothing. */ @Override public String getAndReset() { return null; } /** * Does nothing */ @Override public void appendIgnoringWhitespace(char ch) { } /** * Does nothing */ @Override public void append(char ch) { } /** * Returns null as this appender does nothing. * * @return null as this appender does nothing. */ @Override public char[] getCharsAndReset() { return null; } /** * Returns 0 as this appender does nothing. * * @return 0 as this appender does nothing. */ @Override public int whitespaceCount() { return 0; } /** * Does nothing */ @Override public void reset() { } /** * Does nothing */ @Override public void resetWhitespaceCount() { } /** * Does nothing */ @Override public char[] getChars() { return null; } /** * Does nothing */ @Override public void fill(char ch, int length) { } /** * Does nothing */ @Override public void appendIgnoringPadding(char ch, char padding) { } /** * Does nothing */ @Override public void appendIgnoringWhitespaceAndPadding(char ch, char padding) { } /** * Does nothing */ @Override public void prepend(char ch) { } /** * Does nothing */ @Override public void updateWhitespace() { } @Override public char appendUntil(char ch, CharInput input, char stop) { for (; ch != stop; ch = input.nextChar()) ; return ch; } @Override public final char appendUntil(char ch, CharInput input, char stop1, char stop2) { for (; ch != stop1 && ch != stop2; ch = input.nextChar()) ; return ch; } @Override public final char appendUntil(char ch, CharInput input, char stop1, char stop2, char stop3) { for (; ch != stop1 && ch != stop2 && ch != stop3; ch = input.nextChar()) ; return ch; } /** * Does nothing */ @Override public void append(char[] ch, int from, int length) { } /** * Does nothing */ @Override public void prepend(char ch1, char ch2) { } /** * Does nothing */ @Override public void prepend(char[] chars) { } /** * Does nothing */ @Override public void append(char[] ch) { } /** * Does nothing */ @Override public void append(String string) { } /** * Does nothing */ @Override public void append(String string, int from, int to) { } /** * Does nothing */ @Override public char charAt(int i) { return 0; } /** * Does nothing */ @Override public CharSequence subSequence(int i, int i1) { return null; } /** * Does nothing */ @Override public void append(int ch) { } /** * Does nothing */ @Override public void append(int[] ch) { } /** * Does nothing */ @Override public void append(Object obj) { } /** * Does nothing */ @Override public void ignore(int count) { } /** * Does nothing */ @Override public int indexOf(char ch, int from) { return -1; } /** * Does nothing */ @Override public String substring(int from, int length) { return null; } /** * Does nothing */ @Override public void remove(int from, int length) { } /** * Does nothing */ @Override public void delete(int count) { } /** * Does nothing */ @Override public int indexOfAny(char[] chars, int from) { return -1; } /** * Does nothing */ @Override public int indexOf(char[] charSequence, int from) { return -1; } /** * Does nothing */ @Override public int indexOf(CharSequence charSequence, int from) { return -1; } /** * Returns {@code true} as it's always empty. */ @Override public boolean isEmpty() { return true; } @Override public int lastIndexOf(char ch) { return -1; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/WriterCharAppender.java000066400000000000000000000163601400120543400331750ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import com.univocity.parsers.common.*; import java.io.*; /** * Extension of the {@link DefaultCharAppender} class to include facilities for writing to an output. Used by writers extending {@link AbstractWriter}. * *

This class introduces the handling of the normalized newline character defined in {@link Format#getNormalizedNewline()} and converts it to the newline sequence in {@link Format#getLineSeparator()} *

It also introduces methods to write to an instance of {@link java.io.Writer} directly to avoid unnecessary String instantiations. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.Format * @see com.univocity.parsers.common.AbstractWriter */ public class WriterCharAppender extends ExpandingCharAppender { private final char lineSeparator1; private final char lineSeparator2; private final char newLine; private boolean denormalizeLineEndings = true; /** * Creates a WriterCharAppender with: *

    *
  • a maximum limit of characters to append
  • *
  • the default value to return when no characters have been accumulated.
  • *
  • the basic {@link Format} specification for handling newlines
  • *
* * The padding character is defaulted to a whitespace character ' '. * * @param maxLength maximum limit of characters to append * @param emptyValue default value to return when no characters have been accumulated * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param format output format specification used for newline handling */ public WriterCharAppender(int maxLength, String emptyValue, int whitespaceRangeStart, Format format) { super(maxLength == -1 ? 8192 : maxLength, emptyValue, whitespaceRangeStart); char[] lineSeparator = format.getLineSeparator(); this.lineSeparator1 = lineSeparator[0]; this.lineSeparator2 = lineSeparator.length > 1 ? lineSeparator[1] : '\0'; newLine = format.getNormalizedNewline(); } /** * Appends the given character and marks it as ignored if it is a whitespace ({@code ch <= ' '}) * *

If the given character is equal to {@link Format#getNormalizedNewline()}, then the character sequence returned by {@link Format#getLineSeparator()} is going to be appended. * * @param ch character to append */ @Override public final void appendIgnoringWhitespace(char ch) { if (ch == newLine && denormalizeLineEndings) { super.appendIgnoringWhitespace(lineSeparator1); if (lineSeparator2 != '\0') { super.appendIgnoringWhitespace(lineSeparator2); } } else { super.appendIgnoringWhitespace(ch); } } /** * Appends the given character and marks it as ignored if it is a padding character * *

If the given character is equal to {@link Format#getNormalizedNewline()}, then the character sequence returned by {@link Format#getLineSeparator()} is going to be appended. * * @param ch character to append * @param padding the padding character */ @Override public final void appendIgnoringPadding(char ch, char padding) { if (ch == newLine && denormalizeLineEndings) { super.appendIgnoringPadding(lineSeparator1, padding); if (lineSeparator2 != '\0') { super.appendIgnoringPadding(lineSeparator2, padding); } } else { super.appendIgnoringPadding(ch, padding); } } /** * Appends the given character and marks it as ignored if it is a whitespace ({@code ch <= ' '}) or a padding character * *

If the given character is equal to {@link Format#getNormalizedNewline()}, then the character sequence returned by {@link Format#getLineSeparator()} is going to be appended. * * @param ch character to append * @param padding the padding character */ @Override public final void appendIgnoringWhitespaceAndPadding(char ch, char padding) { if (ch == newLine && denormalizeLineEndings) { super.appendIgnoringWhitespaceAndPadding(lineSeparator1, padding); if (lineSeparator2 != '\0') { super.appendIgnoringWhitespaceAndPadding(lineSeparator2, padding); } } else { super.appendIgnoringWhitespaceAndPadding(ch, padding); } } /** * Appends the given character. * *

If the given character is equal to {@link Format#getNormalizedNewline()}, then the character sequence returned by {@link Format#getLineSeparator()} is going to be appended. * * @param ch the character to append */ @Override public final void append(char ch) { if (ch == newLine && denormalizeLineEndings) { appendNewLine(); } else { super.append(ch); } } /** * Writes the accumulated value to the {@link java.io.Writer}, discarding any trailing whitespace characters identified when using * {@link WriterCharAppender#appendIgnoringWhitespace(char)}, {@link WriterCharAppender#appendIgnoringPadding(char, char)} or {@link WriterCharAppender#appendIgnoringWhitespaceAndPadding(char, char)} *

The internal accumulated value is discarded after invoking this method (as in {@link DefaultCharAppender#reset()}) *

If the accumulated value is empty (i.e. no characters were appended, or all appended characters where ignored as whitespace or padding), then the written value will be the {@link DefaultCharAppender#emptyValue} attribute defined in the constructor of this class. * * @param writer the output writer * * @throws IOException if an error occurs while writing to the output. */ public final void writeCharsAndReset(Writer writer) throws IOException { if (index - whitespaceCount > 0) { writer.write(chars, 0, index - whitespaceCount); } else if (emptyChars != null) { writer.write(emptyChars, 0, emptyChars.length); } index = 0; whitespaceCount = 0; } /** * Appends the newline character sequence specified in {@link Format#getLineSeparator()} */ public final void appendNewLine() { if (index + 2 >= chars.length) { expand(); } chars[index++] = lineSeparator1; if (lineSeparator2 != '\0') { chars[index++] = lineSeparator2; } } /** * Configures the appender to allow line ending sequences to be appended as-is, without replacing them by the * normalized line separator character. * * @param enableDenormalizedLineEndings flag indicating whether denormalized line endings are allowed. The writer * won't convert line separators automatically. */ public final void enableDenormalizedLineEndings(boolean enableDenormalizedLineEndings) { this.denormalizeLineEndings = enableDenormalizedLineEndings; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/concurrent/000077500000000000000000000000001400120543400307555ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/concurrent/CharBucket.java000077500000000000000000000050301400120543400336340ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input.concurrent; import java.io.*; import java.util.*; /** * A buffer of characters. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ class CharBucket { /** * The bucket data */ final char[] data; /** * The number of characters this bucket contain. It is modified every time {@link CharBucket#fill(Reader)} is called. */ int length = -1; /** * Creates a bucket capable of holding a fixed number of characters * @param bucketSize the maximum capacity of the bucket */ public CharBucket(int bucketSize) { if (bucketSize > 0) { data = new char[bucketSize]; } else { data = new char[0]; } } /** * Creates a bucket capable of holding a fixed number of characters * @param bucketSize the maximum capacity of the bucket * @param fillWith a character used to fill all positions of the bucket. */ public CharBucket(int bucketSize, char fillWith) { this(bucketSize); if (bucketSize > 0) { Arrays.fill(data, fillWith); } } /** * Fills the bucket with the characters take from a {@link java.io.Reader} *

The {@link CharBucket#length} attribute will be updated with the number of characters extracted * @param reader the source of characters used to fill the bucket * @return the number of characters extracted from the reader * @throws IOException if any error occurs while extracting characters from the reader */ public int fill(Reader reader) throws IOException { length = reader.read(data, 0, data.length); return length; } /** * Returns true if the bucket is empty (i.e. length <= 0), false otherwise. * @return true if the bucket is empty (i.e. length <= 0), false otherwise. */ public boolean isEmpty() { return length <= 0; } } ConcurrentCharInputReader.java000077500000000000000000000130451400120543400366320ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/concurrent/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input.concurrent; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.*; import java.io.*; /** * A concurrent CharInputReader that loads batches of characters in a separate thread and assigns them to buffer in {@link AbstractCharInputReader} when requested. * *

This class loads "buckets" of characters in the background and provides them sequentially to the {@link ConcurrentCharInputReader#buffer} * attribute in {@link AbstractCharInputReader}. *

The bucket loading process will block and wait while all buckets are full. *

Similarly, the reader will block while all buckets are empty. * * This CharInputReader implementation provides a better throughput than {@link DefaultCharInputReader} when reading large inputs ({@code > 100 mb}). * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see CharInputReader * @see ConcurrentCharLoader * @see CharBucket */ public class ConcurrentCharInputReader extends AbstractCharInputReader { private ConcurrentCharLoader bucketLoader; private final int bucketSize; private final int bucketQuantity; private boolean unwrapping = false; /** * Creates a new instance with the mandatory characters for handling newlines transparently. Line separators will be detected automatically. * * @param normalizedLineSeparator the normalized newline character (as defined in {@link Format#getNormalizedNewline()}) * that is used to replace any lineSeparator sequence found in the input. * @param bucketSize the size of an each individual "bucket" used to store characters read from the input. * @param bucketQuantity the number of "buckets" to load in memory. Note the reader will stop if all buckets are full. * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param closeOnStop indicates whether to automatically close the input when {@link #stop()} is called */ public ConcurrentCharInputReader(char normalizedLineSeparator, int bucketSize, int bucketQuantity, int whitespaceRangeStart, boolean closeOnStop) { super(normalizedLineSeparator, whitespaceRangeStart, closeOnStop); this.bucketSize = bucketSize; this.bucketQuantity = bucketQuantity; } /** * Creates a new instance with the mandatory characters for handling newlines transparently. * * @param lineSeparator the sequence of characters that represent a newline, as defined in {@link Format#getLineSeparator()} * @param normalizedLineSeparator the normalized newline character (as defined in {@link Format#getNormalizedNewline()}) * that is used to replace any lineSeparator sequence found in the input. * @param bucketSize the size of an each individual "bucket" used to store characters read from the input. * @param bucketQuantity the number of "buckets" to load in memory. Note the reader will stop if all buckets are full. * @param whitespaceRangeStart starting range of characters considered to be whitespace. * @param closeOnStop indicates whether to automatically close the input when {@link #stop()} is called */ public ConcurrentCharInputReader(char[] lineSeparator, char normalizedLineSeparator, int bucketSize, int bucketQuantity, int whitespaceRangeStart, boolean closeOnStop) { super(lineSeparator, normalizedLineSeparator, whitespaceRangeStart, closeOnStop); this.bucketSize = bucketSize; this.bucketQuantity = bucketQuantity; } /** * Stops the CharInputReader from reading characters from the {@link java.io.Reader} provided in {@link ConcurrentCharInputReader#start(Reader)} and closes it. * Also stops the input reading thread. */ @Override public void stop() { if (!unwrapping && bucketLoader != null) { bucketLoader.stopReading(); bucketLoader.reportError(); } } /** * Starts an input reading thread to load characters from the given reader into "buckets" of characters */ @Override protected void setReader(Reader reader) { if(!unwrapping) { stop(); bucketLoader = new ConcurrentCharLoader(reader, bucketSize, bucketQuantity, closeOnStop); bucketLoader.reportError(); } else { bucketLoader.reader = reader; } unwrapping = false; } /** * Assigns the next "bucket" of characters to the {@link ConcurrentCharInputReader#buffer} attribute, and updates the {@link ConcurrentCharInputReader#length} to the number of characters read. */ @Override protected void reloadBuffer() { try { CharBucket currentBucket = bucketLoader.nextBucket(); bucketLoader.reportError(); super.buffer = currentBucket.data; super.length = currentBucket.length; } catch (BomInput.BytesProcessedNotification e) { unwrapping = true; unwrapInputStream(e); } } } ConcurrentCharLoader.java000077500000000000000000000130131400120543400356110ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/concurrent/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input.concurrent; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.*; import java.io.*; import java.util.concurrent.*; /** * A concurrent character loader for loading a pool of {@link CharBucket} instances using a {@link java.io.Reader} in a separate thread * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see ConcurrentCharInputReader * @see CharBucket * @see Entry */ class ConcurrentCharLoader implements Runnable { private final ArrayBlockingQueue buckets; private final CharBucket end; private final FixedInstancePool instances; private Entry currentBucket; private boolean finished = false; private boolean active; Reader reader; private Thread activeExecution; private Exception error; private final boolean closeOnStop; /** * Creates a {@link FixedInstancePool} with a given amount of {@link CharBucket} instances and starts a thread to fill each one. * * @param reader The source of characters to extract and fill {@link CharBucket} instances * @param bucketSize The size of each individual {@link CharBucket} * @param bucketQuantity The number of {@link CharBucket} instances used to extract characters from the given reader. * @param closeOnStop Indicates whether to automatically close the input when {@link #stopReading()} is called */ public ConcurrentCharLoader(Reader reader, final int bucketSize, int bucketQuantity, boolean closeOnStop) { this.closeOnStop = closeOnStop; this.end = new CharBucket(-1); this.buckets = new ArrayBlockingQueue(bucketQuantity); this.reader = reader; this.instances = new FixedInstancePool(bucketQuantity) { @Override protected CharBucket newInstance() { return new CharBucket(bucketSize); } }; finished = false; active = true; } private int readBucket() throws IOException, InterruptedException { Entry bucket = instances.allocate(); int length = bucket.get().fill(reader); if (length != -1) { buckets.put(bucket); } else { instances.release(bucket); } return length; } /** * The {@link CharBucket} loading process that executes in parallel until the input is completely read. * Once the end of the input is reached, the {@link java.io.Reader} instance provided in the constructor is closed. */ @Override public void run() { try { try { while (active && readBucket() != -1) ; } finally { buckets.put(end); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { finished = true; setError(e); } finally { stopReading(); } } private void setError(Exception e){ if(active) { error = e; } //else if not active then input was closed externally - we can ignore the exception. } /** * Returns the next available bucket. Blocks until a bucket is made available or the reading process stops. * * @return the next available bucket. */ @SuppressWarnings("unchecked") public synchronized CharBucket nextBucket() { if (activeExecution == null && !finished) { int length = -1; try { length = readBucket(); if (length >= 0 && length <= 4) { length = readBucket(); } } catch (BomInput.BytesProcessedNotification e) { throw e; } catch (Exception e) { setError(e); } if(length != -1) { activeExecution = new Thread(this, "unVocity-parsers input reading thread"); activeExecution.start(); } else { finished = true; try { buckets.put(end); } catch(InterruptedException e){ Thread.currentThread().interrupt(); } finally { stopReading(); } } } try { if (finished) { if (buckets.size() <= 1) { return end; } } if (currentBucket != null) { instances.release(currentBucket); } Object element = buckets.take(); if (element == end) { finished = true; return end; } else { currentBucket = (Entry) element; } return currentBucket.get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); finished = true; return end; } } /** * Stops the {@link CharBucket} loading process and closes the reader provided in the constructor of this class */ public void stopReading() { active = false; try { if(closeOnStop) { reader.close(); } } catch (IOException e) { throw new IllegalStateException("Error closing input", e); } finally { try { if (activeExecution != null) { activeExecution.interrupt(); } } catch (Throwable ex) { throw new IllegalStateException("Error stopping input reader thread", ex); } } } void reportError() { if (error != null) { ArgumentUtils.throwUnchecked(error); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/concurrent/Entry.java000077500000000000000000000030761400120543400327320ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input.concurrent; /** * An entry used by the {@link FixedInstancePool} * @param the type of this entry. * * @see FixedInstancePool * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ class Entry { final T entry; final int index; /** * Creates a new entry with an object and its position in the {@link FixedInstancePool} * @param entry the value in this entry * @param index the position of this entry in the {@link FixedInstancePool} */ Entry(T entry, int index) { this.entry = entry; this.index = index; } /** * Returns the object stored in this {@link FixedInstancePool} entry. * @return the object stored in this {@link FixedInstancePool} entry. */ public T get() { return this.entry; } } FixedInstancePool.java000077500000000000000000000075321400120543400351310ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/input/concurrent/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input.concurrent; import java.util.*; /** * A very simple object instance pool with a fixed size. * *

This is essentially an immutable circular queue. Elements are not added nor removed. Pointers to the head and tail of the queue identify what is the next available entry. *

Use {@link FixedInstancePool#allocate()} to get an available {@link Entry} from the pool. If all objects are allocated then the thread will block until an element is released. *

{@link FixedInstancePool#release(Entry)} releases an allocated {@link Entry} for reuse. * * @param the class of objects stored in the instance pool * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see Entry */ abstract class FixedInstancePool { final Entry[] instancePool; private final int[] instanceIndexes; private int head = 0; private int tail = 0; int count = 0; private int lastInstanceIndex = 0; /** * Creates a new instance pool with the given size. Upon instantiation, the {@link FixedInstancePool#newInstance()} method will be called to fill in the instance pool, and the pool * can then have its entries allocated for use (and reuse). * * @param size the size of the fixed instance pool. */ @SuppressWarnings("unchecked") FixedInstancePool(int size) { instancePool = new Entry[size]; instanceIndexes = new int[size]; Arrays.fill(instanceIndexes, -1); instancePool[0] = new Entry(newInstance(), 0); instanceIndexes[0] = 0; } /** * Creates a new instance of the given type of objects stored as entries of this instance pool * This method is called in the constructor of this class for initialization of the instance array and must always return a new instance. * * @return returns a new instance to use in the pool */ protected abstract T newInstance(); /** * Retrieves the next available entry in this instance pool. Blocks until an entry becomes available (through {@link FixedInstancePool#release(Entry)}). * * @return the next available entry in this instance pool */ public synchronized Entry allocate() { while (count == instancePool.length) { try { wait(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return new Entry(newInstance(), -1); } } int index = instanceIndexes[head]; if (index == -1) { index = ++lastInstanceIndex; instanceIndexes[index] = index; instancePool[index] = new Entry(newInstance(), index); } Entry out = instancePool[index]; // instanceIndexes[head] = -1; //enable to print the queue's contents for debugging purposes head++; if (head == instancePool.length) { head = 0; } count++; return out; } /** * Releases the given entry and makes it available for allocation (by {@link FixedInstancePool#allocate()}) * * @param e the entry to be released and made available for reuse. */ public synchronized void release(Entry e) { if (e.index != -1) { instanceIndexes[tail++] = e.index; if (tail == instancePool.length) { tail = 0; } count--; } notify(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/iterators/000077500000000000000000000000001400120543400274505ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/iterators/ParserIterator.java000066400000000000000000000062011400120543400332600ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.iterators; import com.univocity.parsers.common.*; import com.univocity.parsers.common.record.*; import java.util.*; /** * An {@link Iterator} over the parser enabling easy iteration against rows and records * Multiple iterations are possible if Files are being fed into the parser, * but other forms of input (such as {@code InputStream}s and {@code Reader}s) can not be iterated over more than once. * * @author Univocity Software Pty Ltd - dev@univocity.com */ abstract class ParserIterator implements IterableResult { protected final AbstractParser parser; /** * Creates a {@code ParserIterator} using the provided {@code parser} * * @param parser the {@code parser} to iterate over */ protected ParserIterator(AbstractParser parser) { this.parser = parser; } @Override public final ParsingContext getContext() { if (parser.getContext() == null) { beginParsing(); } return parser.getContext(); } /** * This method is called whenever the {@code iterator} is starting to iterate over the * results. * an example implementation of this is: *


*
	 *     {@code
	 *     @Override
	 *     public void beginParsing(){
	 *         parser.beginParsing(input);
	 *     }}
	 * 
*
* This is to allow for different input types such as {@code Reader, File, or InputStream} without large code * reuse. */ protected abstract void beginParsing(); @Override public final ResultIterator iterator() { return new ResultIterator() { T next; boolean started; @Override public ParsingContext getContext() { return parser.getContext(); } @Override public boolean hasNext() { if (started) { return next != null; } else { started = true; if (parser.getContext() == null) { beginParsing(); } next = nextResult(); return next != null; } } @Override public T next() { if (!started) { hasNext(); } T out = next; next = nextResult(); return out; } @Override public void remove() { throw new UnsupportedOperationException("Can't remove row"); } }; } /** * Returns the next record (either a String[] or a {@link Record}) * * @return the next record if available. */ protected abstract T nextResult(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/iterators/RecordIterator.java000066400000000000000000000025601400120543400332460ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.iterators; import com.univocity.parsers.common.*; import com.univocity.parsers.common.record.*; import java.io.*; /** * An iterator of {@link Record}s. Created when {@link AbstractParser#iterateRecords(File)} * (and its overloaded counterparts) is called * * @author Univocity Software Pty Ltd - dev@univocity.com */ public abstract class RecordIterator extends ParserIterator { public RecordIterator(AbstractParser parser) { super(parser); } @Override protected final Record nextResult() { return parser.parseNextRecord(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/iterators/RowIterator.java000066400000000000000000000024641400120543400326020ustar00rootroot00000000000000/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.iterators; import com.univocity.parsers.common.*; import java.io.*; /** * An iterator of {@code String[]}. Created when {@link AbstractParser#iterate(File)} * (and its overloaded counterparts) is called * * @author Univocity Software Pty Ltd - dev@univocity.com */ public abstract class RowIterator extends ParserIterator { public RowIterator(AbstractParser parser) { super(parser); } @Override protected final String[] nextResult() { return parser.parseNext(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/000077500000000000000000000000001400120543400274535ustar00rootroot00000000000000AbstractRowProcessor.java000077500000000000000000000023131400120543400343740ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A {@link RowProcessor} implementation that just implements all methods defined by the interface. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class AbstractRowProcessor extends AbstractProcessor implements RowProcessor { } BatchedColumnProcessor.java000066400000000000000000000046121400120543400346520ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A {@link RowProcessor} implementation that stores values of columns in batches. Use this implementation in favor of {@link ColumnProcessor} * when processing large inputs to avoid running out of memory. * * Values parsed in each row will be split into columns of Strings. Each column has its own list of values. * *

During the execution of the process, the {@link #batchProcessed(int)} method will be invoked after a given number of rows has been processed.

*

The user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

*

After {@link #batchProcessed(int)} is invoked, all values will be discarded and the next batch of column values will be accumulated. * This process will repeat until there's no more rows in the input. * * @author Univocity Software Pty Ltd - parsers@univocity.com * * @see AbstractParser * @see RowProcessor * @see AbstractBatchedColumnProcessor */ public abstract class BatchedColumnProcessor extends AbstractBatchedColumnProcessor implements RowProcessor { /** * Constructs a batched column processor configured to invoke the {@link #batchesProcessed} method after a given number of rows has been processed. * @param rowsPerBatch the number of rows to process in each batch. */ public BatchedColumnProcessor(int rowsPerBatch) { super(rowsPerBatch); } } BatchedObjectColumnProcessor.java000066400000000000000000000051701400120543400360010ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.conversions.*; /** * * A {@link RowProcessor} implementation for converting batches of rows extracted from any implementation of {@link AbstractParser} into columns of objects. *

This uses the value conversions provided by {@link Conversion} instances.

* *

For each row processed, a sequence of conversions will be executed to generate the appropriate object. Each resulting object will then be stored in * a list that contains the values of the corresponding column.

* *

During the execution of the process, the {@link #batchProcessed(int)} method will be invoked after a given number of rows has been processed.

*

The user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

*

After {@link #batchProcessed(int)} is invoked, all values will be discarded and the next batch of column values will be accumulated. * This process will repeat until there's no more rows in the input. * * @see AbstractParser * @see RowProcessor * @see AbstractBatchedColumnProcessor * @see Conversion * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class BatchedObjectColumnProcessor extends AbstractBatchedObjectColumnProcessor implements RowProcessor { /** * Constructs a batched column processor configured to invoke the {@link #batchesProcessed} method after a given number of rows has been processed. * @param rowsPerBatch the number of rows to process in each batch. */ public BatchedObjectColumnProcessor(int rowsPerBatch) { super(rowsPerBatch); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/BeanListProcessor.java000066400000000000000000000052741400120543400337270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * A convenience {@link BeanProcessor} implementation for storing all java objects generated form the parsed input into a list. * A typical use case of this class will be: * *


{@code
 *
 * parserSettings.setRowProcessor(new BeanListProcessor(MyObject.class));
 * parser.parse(reader); // will invoke the {@link BeanListProcessor#beanProcessed(Object, Context)} method for each generated object.
 *
 * List<T> beans = rowProcessor.getBeans();
 * }

* * @param the annotated class type. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see BeanProcessor * @see RowProcessor * @see AbstractParser * @see AbstractBeanListProcessor */ public class BeanListProcessor extends AbstractBeanListProcessor implements RowProcessor { /** * Creates a processor that stores java beans of a given type into a list * * @param beanType the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public BeanListProcessor(Class beanType) { super(beanType); } /** * Creates a processor that stores java beans of a given type into a list * * @param beanType the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * returned by {@link #getBeans()} */ public BeanListProcessor(Class beanType, int expectedBeanCount) { super(beanType, expectedBeanCount); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/BeanProcessor.java000066400000000000000000000041341400120543400330650ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * * A {@link RowProcessor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects. *

The class type of the object must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For each row processed, a java bean instance of a given class will be created with its fields populated. *

This instance will then be sent to the {@link BeanProcessor#beanProcessed(Object, Context)} method, where the user can access it. * * @see AbstractParser * @see RowProcessor * @see AbstractBeanProcessor * * @param the annotated class type. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class BeanProcessor extends AbstractBeanProcessor implements RowProcessor{ /** * Creates a processor for java beans of a given type. * @param beanType the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public BeanProcessor(Class beanType) { super(beanType, MethodFilter.ONLY_SETTERS); } } BeanWriterProcessor.java000066400000000000000000000077471400120543400342200ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.conversions.*; /** * * A {@link RowWriterProcessor} implementation for converting annotated java objects into object arrays suitable for writing in any implementation of {@link AbstractWriter}. *

The class type of the object must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For any given java bean instance, this processor will read and convert annotated fields into an object array. * * * @param the annotated class type. * * @see AbstractWriter * @see RowWriterProcessor * @see BeanConversionProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class BeanWriterProcessor extends BeanConversionProcessor implements RowWriterProcessor { private NormalizedString[] normalizedHeaders; private String[] previousHeaders; /** * Initializes the BeanWriterProcessor with the annotated bean class * @param beanType the class annotated with one or more of the annotations provided in {@link com.univocity.parsers.annotations}. */ public BeanWriterProcessor(Class beanType) { super(beanType, MethodFilter.ONLY_GETTERS); } /** * Converts the java bean instance into a sequence of values for writing. * * @param input an instance of the type defined in this class constructor. * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @return a row of objects containing the values extracted from the java bean */ public Object[] write(T input, String[] headers, int[] indexesToWrite) { if (previousHeaders != headers) { previousHeaders = headers; normalizedHeaders = NormalizedString.toArray(headers); } return write(input, normalizedHeaders, indexesToWrite); } /** * Converts the java bean instance into a sequence of values for writing. * * @param input an instance of the type defined in this class constructor. * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @return a row of objects containing the values extracted from the java bean */ @Override public Object[] write(T input, NormalizedString[] headers, int[] indexesToWrite) { if (!initialized) { super.initialize(headers); } return reverseConversions(input, headers, indexesToWrite); } @Override protected FieldConversionMapping cloneConversions(){ return null; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/ColumnProcessor.java000066400000000000000000000041451400120543400334570ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A simple {@link RowProcessor} implementation that stores values of columns. * Values parsed in each row will be split into columns of Strings. Each column has its own list of values. * *

At the end of the process, the user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

* * *

Note: Storing the values of all columns may be memory intensive. For large inputs, use a {@link BatchedColumnProcessor} instead

* * @author Univocity Software Pty Ltd - parsers@univocity.com * * @see AbstractParser * @see RowProcessor */ public class ColumnProcessor extends AbstractColumnProcessor implements RowProcessor { /** * Constructs a column processor, pre-allocating room for 1000 rows. */ public ColumnProcessor() { super(1000); } /** * Constructs a column processor pre-allocating room for the expected number of rows to be processed * @param expectedRowCount the expected number of rows to be processed */ public ColumnProcessor(int expectedRowCount) { super(expectedRowCount); } } CompositeRowProcessor.java000077500000000000000000000026471400120543400346050ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A utility {@link RowProcessor} implementation that facilitates using multiple implementations of {@link RowProcessor} at the * same time. */ public class CompositeRowProcessor extends CompositeProcessor implements RowProcessor { /** * Creates a new {@code CompositeProcessor} with the list of {@link Processor} implementations to be used. * * @param processors the sequence of {@link Processor} implementations to be used. */ public CompositeRowProcessor(Processor... processors) { super(processors); } }ConcurrentRowProcessor.java000066400000000000000000000051041400120543400347510ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A {@link RowProcessor} implementation to perform row processing tasks in parallel. The {@code ConcurrentRowProcessor} wraps another {@link RowProcessor}, and collects rows read from the input. * The actual row processing is performed in by wrapped {@link RowProcessor} in a separate thread. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see RowProcessor */ public class ConcurrentRowProcessor extends AbstractConcurrentProcessor implements RowProcessor { /** * Creates a non-blocking {@code ConcurrentRowProcessor}, to perform processing of rows parsed from the input in a separate thread. * * @param rowProcessor a regular {@link RowProcessor} implementation which will be executed in a separate thread. */ public ConcurrentRowProcessor(RowProcessor rowProcessor) { super(rowProcessor); } /** * Creates a blocking {@code ConcurrentRowProcessor}, to perform processing of rows parsed from the input in a separate thread. * * @param rowProcessor a regular {@link RowProcessor} implementation which will be executed in a separate thread. * @param limit the limit of rows to be kept in memory before the input parsing process is blocked. */ public ConcurrentRowProcessor(RowProcessor rowProcessor, int limit) { super(rowProcessor, limit); } @Override protected ParsingContext copyContext(ParsingContext context) { return new ParsingContextSnapshot(context); } @Override protected ParsingContext wrapContext(ParsingContext context) { return new ParsingContextWrapper(context) { @Override public long currentRecord() { return getRowCount(); } }; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/CustomMatcher.java000066400000000000000000000021341400120543400330740ustar00rootroot00000000000000/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor; /** * Interface used by {@link InputValueSwitch} to allow users to to provide custom matching rules against input values. */ public interface CustomMatcher { /** * Matches a parsed value against a user provided rule (implementation provided by the user) * @param value the value to be matched * @return {@code true} if the given value matches the user provided rule, otherwise {@code false} */ boolean matches(String value); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/InputValueSwitch.java000066400000000000000000000051341400120543400335770ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A concrete implementation of {@link RowProcessorSwitch} that allows switching among different implementations of * {@link RowProcessor} based on values found on the rows parsed from the input. */ public class InputValueSwitch extends AbstractInputValueSwitch implements RowProcessor{ /** * Creates a switch that will analyze the first column of rows found in the input to determine which * {@link RowProcessor} to use for each parsed row */ public InputValueSwitch() { this(0); } /** * Creates a switch that will analyze a column of rows parsed from the input to determine which * {@link RowProcessor} to use. * * @param columnIndex the column index whose value will be used to determine which {@link RowProcessor} to use for each parsed row. */ public InputValueSwitch(int columnIndex) { super(columnIndex); } /** * Creates a switch that will analyze a column in rows parsed from the input to determine which * {@link RowProcessor} to use. * * @param columnName name of the column whose values will be used to determine which {@link RowProcessor} to use for each parsed row. */ public InputValueSwitch(String columnName) { super(columnName); } @Override protected final ParsingContext wrapContext(ParsingContext context) { return new ParsingContextWrapper(context) { private final String[] fieldNames = getHeaders(); private final int[] indexes = getIndexes(); @Override public String[] headers() { return fieldNames == null || fieldNames.length == 0 ? context.headers() : fieldNames; } @Override public int[] extractedFieldIndexes() { return indexes == null || indexes.length == 0 ? context.extractedFieldIndexes() : indexes; } }; } } MasterDetailListProcessor.java000066400000000000000000000042411400120543400353520ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * * A convenience {@link MasterDetailProcessor} implementation for storing all {@link MasterDetailRecord} generated form the parsed input into a list. * A typical use case of this class will be: * *


{@code
 *
 * ObjectRowListProcessor detailProcessor = new ObjectRowListProcessor();
 * MasterDetailListProcessor masterRowProcessor = new MasterDetailListProcessor(detailProcessor) {
 *      protected boolean isMasterRecord(String[] row, ParsingContext context) {
 *          return "Total".equals(row[0]);
 *      }
 * };
 *
 * parserSettings.setRowProcessor(masterRowProcessor);
 *
 * List<MasterDetailRecord> rows = masterRowProcessor.getRecords();
 * }

* * @see MasterDetailProcessor * @see RowProcessor * @see AbstractParser * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class MasterDetailListProcessor extends AbstractMasterDetailListProcessor implements RowProcessor { public MasterDetailListProcessor(RowPlacement rowPlacement, AbstractObjectListProcessor detailProcessor) { super(rowPlacement, detailProcessor); } public MasterDetailListProcessor(AbstractObjectListProcessor detailProcessor) { super(detailProcessor); } } MasterDetailProcessor.java000066400000000000000000000057121400120543400345220ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.conversions.*; /** * * A {@link RowProcessor} implementation for associating rows extracted from any implementation of {@link AbstractParser} into {@link MasterDetailRecord} instances. * *

For each row processed, a call to {@link MasterDetailProcessor#isMasterRecord(String[], Context)} will be made to identify whether or not it is a master row. *

The detail rows are automatically associated with the master record in an instance of {@link MasterDetailRecord}. *

When the master record is fully processed (i.e. {@link MasterDetailRecord} contains a master row and all associated detail rows), * it is sent to the user for processing in {@link MasterDetailProcessor#masterDetailRecordProcessed(MasterDetailRecord, Context)}. * *

Note this class extends {@link ObjectRowProcessor} and value conversions provided by {@link Conversion} instances are fully supported. * * @see MasterDetailRecord * @see RowPlacement * @see AbstractParser * @see ObjectRowListProcessor * @see RowProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class MasterDetailProcessor extends AbstractMasterDetailProcessor { /** * Creates a MasterDetailProcessor * * @param rowPlacement indication whether the master records are placed in relation its detail records in the input. * *


	 *
	 * Master record (Totals)       Master record (Totals)
	 *  above detail records         under detail records
	 *
	 *    Totals | 100                 Item   | 60
	 *    Item   | 60                  Item   | 40
	 *    Item   | 40                  Totals | 100
	 * 

* @param detailProcessor the {@link ObjectRowListProcessor} that processes detail rows. */ public MasterDetailProcessor(RowPlacement rowPlacement, ObjectRowListProcessor detailProcessor) { super(rowPlacement, detailProcessor); } public MasterDetailProcessor(ObjectRowListProcessor detailProcessor) { super(RowPlacement.TOP, detailProcessor); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/MasterDetailRecord.java000066400000000000000000000050611400120543400340350ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import java.util.*; /** * An utility class to store data of a master row and its detail records. * * Instances of this class are typically generated by an instance of {@link AbstractParser} during the parsing of an input using a {@link MasterDetailProcessor}. * * @see MasterDetailProcessor * @see RowProcessor * @see AbstractParser * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class MasterDetailRecord implements Cloneable { private Object[] masterRow = null; private List detailRows = Collections.emptyList(); /** * Returns the master row as identified by a {@link MasterDetailProcessor} * @return the master row */ public Object[] getMasterRow() { return masterRow; } /** * Sets the master row data. * @param masterRow the data of a master row */ public void setMasterRow(Object[] masterRow) { this.masterRow = masterRow; } /** * Returns the detail rows which are associated with the master row * @return the detail rows which are associated with the master row */ public List getDetailRows() { return detailRows; } /** * Sets the detail rows associated with the master row * @param detailRows the list of rows associated with the master row */ public void setDetailRows(List detailRows) { this.detailRows = detailRows; } /** * Empties the detail rows and sets the master row to null. */ public void clear() { this.detailRows = Collections.emptyList(); this.masterRow = null; } @Override public MasterDetailRecord clone() { try { return (MasterDetailRecord) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.getMessage()); } } } MultiBeanListProcessor.java000066400000000000000000000053161400120543400346600ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * A {@link RowProcessor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects, storing * them into lists. This processor stores beans in separate lists, one for each type of bean processed. * All lists of all types will have the same number of entries as the number of records in the input. * When an object of a particular type can't be generated from a row, {@code null} will be added to the list. This ensures all lists are the same size, * and each element of each list contains the exact information parsed from each row. * *

The class types passed to the constructor of this class must contain the annotations provided in {@link com.univocity.parsers.annotations}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see RowProcessor * @see BeanProcessor * @see MultiBeanProcessor */ public class MultiBeanListProcessor extends AbstractMultiBeanListProcessor implements RowProcessor { /** * Creates a processor for java beans of multiple types * * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} returned by {@link #getBeans()} * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public MultiBeanListProcessor(int expectedBeanCount, Class... beanTypes) { super(expectedBeanCount, beanTypes); } /** * Creates a processor for java beans of multiple types * * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public MultiBeanListProcessor(Class... beanTypes) { super(beanTypes); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/MultiBeanProcessor.java000066400000000000000000000037051400120543400341030ustar00rootroot00000000000000/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * * A {@link RowProcessor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects. * *

The class types passed to the constructor of this class must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For each row processed, one or more java bean instances of any given class will be created with their fields populated. *

Each individual instance will then be sent to the {@link MultiBeanProcessor#beanProcessed(Class, Object, Context)} method, where the user can access the * beans parsed for each row. * * @see AbstractParser * @see RowProcessor * @see BeanProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class MultiBeanProcessor extends AbstractMultiBeanProcessor implements RowProcessor{ /** * Creates a processor for java beans of multiple types * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public MultiBeanProcessor(Class ... beanTypes){ super(beanTypes); } } MultiBeanRowProcessor.java000066400000000000000000000040521400120543400345100ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * * A {@link RowProcessor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects. * *

The class types passed to the constructor of this class must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For each row processed, one or more java bean instances of any given class will be created with their fields populated. *

Once all beans are populated from an individual input record, they will be sent to through the {@link AbstractMultiBeanRowProcessor#rowProcessed(Map, Context)} method, * where the user can access all beans parsed for that row. * * @see AbstractParser * @see RowProcessor * @see BeanProcessor * @see MultiBeanProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class MultiBeanRowProcessor extends AbstractMultiBeanRowProcessor implements RowProcessor{ /** * Creates a processor for java beans of multiple types * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public MultiBeanRowProcessor(Class... beanTypes) { super(beanTypes); } }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/NoopRowProcessor.java000066400000000000000000000021541400120543400336230ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; /** * A singleton instance of a {@link RowProcessor} that does nothing. */ public final class NoopRowProcessor extends AbstractRowProcessor { /** * The singleton instance of the no-op {@link RowProcessor} */ public static final RowProcessor instance = new NoopRowProcessor(); private NoopRowProcessor() { } } ObjectColumnProcessor.java000066400000000000000000000047521400120543400345330ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.conversions.*; /** * * A {@link RowProcessor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into columns of objects. *

This uses the value conversions provided by {@link Conversion} instances.

* *

For each row processed, a sequence of conversions will be executed to generate the appropriate object. Each resulting object will then be stored in * a list that contains the values of the corresponding column.

* *

At the end of the process, the user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

* *

Note: Storing the values of all columns may be memory intensive. For large inputs, use a {@link BatchedObjectColumnProcessor} instead

* * @see AbstractParser * @see RowProcessor * @see ColumnProcessor * @see Conversion * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ObjectColumnProcessor extends AbstractObjectColumnProcessor implements RowProcessor { /** * Constructs a column processor, pre-allocating room for 1000 rows. */ public ObjectColumnProcessor() { this(1000); } /** * Constructs a column processor pre-allocating room for the expected number of rows to be processed * @param expectedRowCount the expected number of rows to be processed */ public ObjectColumnProcessor(int expectedRowCount) { super(expectedRowCount); } } ObjectRowListProcessor.java000077500000000000000000000047061400120543400347030ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * A convenience {@link RowProcessor} implementation for storing all rows parsed and converted to Object arrays into a list. * A typical use case of this class will be: * *
{@code
 *
 * ObjectRowListProcessor processor = new ObjectRowListProcessor();
 * processor.convertIndexes(Conversions.toBigDecimal()).set(4, 6);
 * parserSettings.setRowProcessor(new ObjectRowListProcessor());
 * parser.parse(reader); // will invoke the {@link AbstractObjectListProcessor#rowProcessed(Object[], Context)} method for each parsed record.
 *
 * String[] headers = rowProcessor.getHeaders();
 * List<Object[]> rows = rowProcessor.getRows();
 * BigDecimal value1 = (BigDecimal) row.get(4);
 * BigDecimal value2 = (BigDecimal) row.get(6);
 * }

* * @author Univocity Software Pty Ltd - parsers@univocity.com * @see RowProcessor * @see ObjectRowProcessor * @see AbstractParser */ public class ObjectRowListProcessor extends AbstractObjectListProcessor implements RowProcessor { /** * Creates a new processor of {@code Object[]} rows with varying types. */ public ObjectRowListProcessor() { } /** * Creates a new processor of {@code Object[]} rows with varying types. * * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} returned by {@link #getRows()} */ public ObjectRowListProcessor(int expectedRowCount) { super(expectedRowCount); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/ObjectRowProcessor.java000066400000000000000000000033501400120543400341150ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.conversions.*; /** * * A {@link RowProcessor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into arrays of objects. *

This uses the value conversions provided by {@link Conversion} instances. * *

For each row processed, a sequence of conversions will be executed and stored in an object array, at its original position. *

The row with the result of these conversions will then be sent to the {@link ObjectRowProcessor#rowProcessed(Object[], Context)} method, where the user can access it. * * @see AbstractParser * @see RowProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class ObjectRowProcessor extends AbstractObjectProcessor implements RowProcessor { } ObjectRowWriterProcessor.java000066400000000000000000000107171400120543400352400ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; /** * * A {@link RowWriterProcessor} implementation for executing conversion sequences in object arrays before for writing them using any implementation of {@link AbstractWriter}. * * @see AbstractWriter * @see RowWriterProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ObjectRowWriterProcessor extends DefaultConversionProcessor implements RowWriterProcessor { private NormalizedString[] normalizedHeaders; private String[] previousHeaders; /** * Executes the sequences of conversions defined using {@link DefaultConversionProcessor#convertFields(Conversion...)}, {@link DefaultConversionProcessor#convertIndexes(Conversion...)} and {@link DefaultConversionProcessor#convertAll(Conversion...)}, for every field in the given row. * *

Each field will be transformed using the {@link Conversion#execute(Object)} method. *

In general the conversions will process a String and convert it to some object value (such as booleans, dates, etc). * * @param input the object array that represents a record with its individual fields. * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @return an row of Object instances containing the values obtained after the execution of all conversions. *

Fields that do not have any conversion defined will just be copied to the object array into their original positions. */ public Object[] write(Object[] input, String[] headers, int[] indexesToWrite) { if (previousHeaders != headers) { previousHeaders = headers; normalizedHeaders = NormalizedString.toArray(headers); } return write(input, normalizedHeaders, indexesToWrite); } /** * Executes the sequences of conversions defined using {@link DefaultConversionProcessor#convertFields(Conversion...)}, {@link DefaultConversionProcessor#convertIndexes(Conversion...)} and {@link DefaultConversionProcessor#convertAll(Conversion...)}, for every field in the given row. * *

Each field will be transformed using the {@link Conversion#execute(Object)} method. *

In general the conversions will process a String and convert it to some object value (such as booleans, dates, etc). * * @param input the object array that represents a record with its individual fields. * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @return an row of Object instances containing the values obtained after the execution of all conversions. *

Fields that do not have any conversion defined will just be copied to the object array into their original positions. */ @Override public Object[] write(Object[] input, NormalizedString[] headers, int[] indexesToWrite) { if (input == null) { return null; } Object[] output = new Object[input.length]; System.arraycopy(input, 0, output, 0, input.length); if (reverseConversions(false, output, headers, indexesToWrite)) { return output; } return null; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/OutputValueSwitch.java000066400000000000000000000330411400120543400337760ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; /** * A concrete implementation of {@link RowWriterProcessorSwitch} that allows switching among different implementations of * {@link RowWriterProcessor} based on values found on rows to be written to an output */ public class OutputValueSwitch extends RowWriterProcessorSwitch { private Switch defaultSwitch; private Switch[] switches = new Switch[0]; private Switch selectedSwitch; private Class[] types = new Class[0]; private final int columnIndex; private final String headerName; @SuppressWarnings("rawtypes") private Comparator comparator = new Comparator() { @Override public int compare(Object o1, Object o2) { return o1 == o2 ? 0 : o1 != null && o1.equals(o2) ? 0 : 1; } }; /** * Creates a switch that will analyze the first column of output rows to determine which * {@link RowWriterProcessor} to use for each output row */ public OutputValueSwitch() { this(0); } /** * Creates a switch that will analyze a column of output rows to determine which * {@link RowWriterProcessor} to use. * * @param columnIndex the column index whose value will be used to determine which {@link RowWriterProcessor} to use for each output row. */ public OutputValueSwitch(int columnIndex) { this.columnIndex = getValidatedIndex(columnIndex); this.headerName = null; } /** * Creates a switch that will analyze a column of output rows to determine which {@link RowWriterProcessor} to use. * When no column index is defined, the switch will use the first column of any input rows sent for writing. * * @param headerName the column name whose value will be used to determine which {@link RowWriterProcessor} to use for each output row. */ public OutputValueSwitch(String headerName) { this.headerName = getValidatedHeaderName(headerName); this.columnIndex = 0; } /** * Creates a switch that will analyze a column of output rows to determine which * {@link RowWriterProcessor} to use. * * @param headerName the column name whose value will be used to determine which {@link RowWriterProcessor} to use for each output row. * @param columnIndex the position of an input column to use to perform the switch when no headers are available. */ public OutputValueSwitch(String headerName, int columnIndex) { this.columnIndex = getValidatedIndex(columnIndex); this.headerName = getValidatedHeaderName(headerName); } private int getValidatedIndex(int columnIndex) { if (columnIndex < 0) { throw new IllegalArgumentException("Column index must be positive"); } return columnIndex; } private String getValidatedHeaderName(String headerName) { if (headerName == null || headerName.trim().length() == 0) { throw new IllegalArgumentException("Header name cannot be blank"); } return headerName; } /** * Configures the switch to use a custom {@link Comparator} to compare values in the column to analyze which is given in the constructor of this class. * * @param comparator the comparator to use for matching values in the output column with the values provided in {@link #addSwitchForValue(Object, RowWriterProcessor)} */ public void setComparator(Comparator comparator) { if (comparator == null) { throw new IllegalArgumentException("Comparator must not be null"); } this.comparator = comparator; } /** * Defines a default {@link RowWriterProcessor} implementation to use when no matching value is found in the output row. * * @param rowProcessor the default row writer processor implementation * @param headersToUse the (optional) sequence of headers to assign to the given row writer processor */ public void setDefaultSwitch(RowWriterProcessor rowProcessor, String... headersToUse) { defaultSwitch = new Switch(rowProcessor, headersToUse, null, null); } /** * Defines a default {@link RowWriterProcessor} implementation to use when no matching value is found in the output row. * * @param rowProcessor the default row writer processor implementation * @param indexesToUse the (optional) sequence of column indexes to assign to the given row writer processor */ public void setDefaultSwitch(RowWriterProcessor rowProcessor, int... indexesToUse) { defaultSwitch = new Switch(rowProcessor, null, indexesToUse, null); } @Override protected NormalizedString[] getHeaders() { return selectedSwitch != null ? selectedSwitch.headers : null; } @Override protected int[] getIndexes() { return selectedSwitch != null ? selectedSwitch.indexes : null; } private Switch getSwitch(Object value) { if (value instanceof Object[]) { Object[] row = (Object[]) value; if (row.length < columnIndex) { return defaultSwitch; } value = row[columnIndex]; } for (int i = 0; i < switches.length; i++) { Switch s = switches[i]; Class type = types[i]; if (type != null) { if (type.isAssignableFrom(value.getClass())) { return s; } } else if (comparator.compare(value, s.value) == 0) { return s; } } return defaultSwitch; } @SuppressWarnings("unchecked") @Override protected RowWriterProcessor switchRowProcessor(Object row) { selectedSwitch = getSwitch(row); return selectedSwitch != null ? selectedSwitch.processor : null; } /** * Associates a {@link RowWriterProcessor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param value the value to match against the column of incoming output rows and trigger the usage of the given row writer processor implementation. * @param rowProcessor the row writer processor implementation to use when the given value matches with the contents of the column provided in the constructor of this class. * @param headersToUse the (optional) sequence of headers to assign to the given row writer processor */ public void addSwitchForValue(Object value, RowWriterProcessor rowProcessor, String... headersToUse) { addSwitch(new Switch(rowProcessor, headersToUse, null, value)); } /** * Associates a {@link RowWriterProcessor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param value the value to match against the column of incoming output rows and trigger the usage of the given row writer processor implementation. * @param rowProcessor the row writer processor implementation to use when the given value matches with the contents of the column provided in the constructor of this class. */ public void addSwitchForValue(Object value, RowWriterProcessor rowProcessor) { addSwitch(new Switch(rowProcessor, null, null, value)); } /** * Associates a {@link RowWriterProcessor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param value the value to match against the column of incoming output rows and trigger the usage of the given row writer processor implementation. * @param rowProcessor the row writer processor implementation to use when the given value matches with the contents of the column provided in the constructor of this class. * @param indexesToUse the (optional) sequence of column indexes to assign to the given row writer processor */ public void addSwitchForValue(Object value, RowWriterProcessor rowProcessor, int... indexesToUse) { addSwitch(new Switch(rowProcessor, null, indexesToUse, value)); } /** * Associates a {@link RowWriterProcessor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param beanType the type of annotated java beans whose instances will be used to trigger the usage of a different format. * @param headersToUse the (optional) sequence of headers to assign to the given row writer processor * @param the type of annotated java beans whose instances will be used to trigger the usage of a different format. */ public void addSwitchForType(Class beanType, String... headersToUse) { addSwitch(new Switch(headersToUse, null, beanType)); } /** * Associates a {@link RowWriterProcessor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param beanType the type of annotated java beans whose instances will be used to trigger the usage of a different format. * @param indexesToUse the (optional) sequence of column indexes to assign to the given row writer processor * @param the type of annotated java beans whose instances will be used to trigger the usage of a different format. */ public void addSwitchForType(Class beanType, int... indexesToUse) { addSwitch(new Switch(null, indexesToUse, beanType)); } /** * Associates a {@link RowWriterProcessor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param beanType the type of annotated java beans whose instances will be used to trigger the usage of a different format. * @param the type of annotated java beans whose instances will be used to trigger the usage of a different format. */ public void addSwitchForType(Class beanType) { addSwitch(new Switch(null, null, beanType)); } private void addSwitch(Switch newSwitch) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = newSwitch; types = Arrays.copyOf(types, types.length + 1); if (newSwitch.value != null && newSwitch.value.getClass() == Class.class) { types[types.length - 1] = (Class) newSwitch.value; } } private V getValue(Map map, int index) { int i = 0; for (Map.Entry e : map.entrySet()) { if (i == index) { return e.getValue(); } } return null; } private NormalizedString[] getHeadersFromSwitch(Object value) { for (int i = 0; i < switches.length; i++) { Switch s = getSwitch(value); if (s != null) { return s.headers; } } return null; } @Override public NormalizedString[] getHeaders(Object input) { if (input instanceof Object[]) { Object[] row = (Object[]) input; if (columnIndex < row.length) { return getHeadersFromSwitch(row[columnIndex]); } return null; } else { return getHeadersFromSwitch(input); } } @Override public NormalizedString[] getHeaders(Map headerMapping, Map mapInput) { Object mapValue = null; if (mapInput != null && !mapInput.isEmpty()) { String headerToUse = headerName; if (headerMapping != null) { if (headerName != null) { Object value = headerMapping.get(headerName); headerToUse = value == null ? null : value.toString(); } else if (columnIndex != -1) { Object value = getValue(headerMapping, columnIndex); headerToUse = value == null ? null : value.toString(); } } if (headerToUse != null) { mapValue = mapInput.get(headerToUse); } else { mapValue = getValue(mapInput, columnIndex); } } return getHeadersFromSwitch(mapValue); } /** * Returns the column index whose values will be used to switching from a row processor to another. * * @return the column index targeted by this switch. */ public int getColumnIndex() { return columnIndex; } private List getSwitchValues() { List values = new ArrayList(switches.length); for (Switch s : switches) { values.add(s.value); } return values; } @Override protected String describeSwitch() { return "Expecting one of values: " + getSwitchValues() + " at column index " + getColumnIndex(); } private static class Switch { final RowWriterProcessor processor; final NormalizedString[] headers; final int[] indexes; final Object value; Switch(RowWriterProcessor processor, String[] headers, int[] indexes, Object value) { this(processor, headers, indexes, value, null); } Switch(String[] headers, int[] indexes, Class type) { this(null, headers, indexes, type, type); } private Switch(RowWriterProcessor processor, String[] headers, int[] indexes, Object value, Class type) { if (type != null) { processor = new BeanWriterProcessor(type); if (headers == null && indexes == null) { headers = AnnotationHelper.deriveHeaderNamesFromFields(type, MethodFilter.ONLY_GETTERS); indexes = ArgumentUtils.toIntArray(Arrays.asList(AnnotationHelper.getSelectedIndexes(type, MethodFilter.ONLY_GETTERS))); } } this.processor = processor; this.headers = headers == null || headers.length == 0 ? null : NormalizedString.toIdentifierGroupArray(headers); this.indexes = indexes == null || indexes.length == 0 ? null : indexes; this.value = value; } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/RowListProcessor.java000077500000000000000000000040651400120543400336310ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import java.util.*; /** * * A convenience {@link RowProcessor} implementation for storing all rows parsed into a list. * A typical use case of this class will be: * *
 *
 * parserSettings.setRowProcessor(new RowListProcessor());
 * parser.parse(reader); // will invoke the {@link RowListProcessor#rowProcessed(String[], ParsingContext)} method for each parsed record.
 *
 * String[] headers = rowProcessor.getHeaders();
 * List<String[]> rows = rowProcessor.getRows();
 *
 * 

* * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class RowListProcessor extends AbstractListProcessor implements RowProcessor{ /** * Creates a new processor of {@code String[]} rows. */ public RowListProcessor() { } /** * Creates a new processor of {@code String[]} rows. * * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} returned by {@link #getRows()} */ public RowListProcessor(int expectedRowCount) { super(expectedRowCount); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/RowPlacement.java000066400000000000000000000022551400120543400327220ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; /** * An indicator of where the input a row is placed in relation to others. Currently used by {@link MasterDetailProcessor}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public enum RowPlacement { /** * Indicates a row is above other rows. */ TOP, /** * Indicates a row is below other rows. */ BOTTOM } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/RowProcessor.java000077500000000000000000000114131400120543400327700ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; import com.univocity.parsers.conversions.*; /** * The essential callback interface to handle records parsed by any parser that extends {@link AbstractParser}. * *

When parsing an input, univocity-parsers will obtain the RowProcessor from {@link CommonParserSettings#getRowProcessor()}, and * delegate each parsed row to {@link RowProcessor#rowProcessed(String[], ParsingContext)}. * *

Before parsing the first row, the parser will invoke the {@link RowProcessor#processStarted(ParsingContext)} method. * By this time the input buffer will be already loaded and ready to be consumed. * *

After parsing the last row, all resources are closed and the processing stops. Only after the {@link RowProcessor#processEnded(ParsingContext)} is called so you * can perform any additional housekeeping you might need. * *

More control and information over the parsing process are provided by the {@link ParsingContext} object. * *

univocity-parsers provides many useful default implementations of this interface in the package {@link com.univocity.parsers.common.processor}, namely: * *

    *
  • {@link RowListProcessor}: convenience class for storing the processed rows into a list.
  • *
  • {@link ObjectRowProcessor}: used for processing rows and executing conversions of parsed values to objects using instances of {@link Conversion}
  • *
  • {@link ObjectRowListProcessor}: convenience class for rows of converted objects using {@link ObjectRowProcessor} into a list.
  • *
  • {@link MasterDetailProcessor}: used for reading inputs where records are organized in a master-detail fashion (with a master element that contains a list of associated elements)
  • *
  • {@link MasterDetailListProcessor}: convenience class for storing {@link MasterDetailRecord} created by instances created by {@link MasterDetailProcessor} into a list
  • *
  • {@link BeanProcessor}: used for automatically create and populate javabeans annotated with the annotations provided in package {@link com.univocity.parsers.annotations}
  • *
  • {@link BeanListProcessor}: convenience class for storing all javabeans created by {@link BeanProcessor} into a list
  • *
* * @see com.univocity.parsers.common.AbstractParser * @see com.univocity.parsers.common.CommonParserSettings * @see com.univocity.parsers.common.ParsingContext * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface RowProcessor extends Processor { /** * This method will by invoked by the parser once, when it is ready to start processing the input. * * @param context A contextual object with information and controls over the current state of the parsing process */ void processStarted(ParsingContext context); /** * Invoked by the parser after all values of a valid record have been processed. * * @param row the data extracted by the parser for an individual record. Note that: *
    *
  • it will never by null.
  • *
  • it will never be empty unless explicitly configured using {@link CommonSettings#setSkipEmptyLines(boolean)}
  • *
  • it won't contain lines identified by the parser as comments. To disable comment processing set {@link Format#setComment(char)} to '\0'
  • *
* @param context A contextual object with information and controls over the current state of the parsing process */ void rowProcessed(String[] row, ParsingContext context); /** * This method will by invoked by the parser once, after the parsing process stopped and all resources were closed. *

It will always be called by the parser: in case of errors, if the end of the input us reached, or if the user stopped the process manually using {@link ParsingContext#stop()}. * * @param context A contextual object with information and controls over the state of the parsing process */ void processEnded(ParsingContext context); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/RowProcessorSwitch.java000066400000000000000000000031141400120543400341460ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.core.*; /** * A special {@link RowProcessor} implementation that combines and allows switching among different * RowProcessors. Each RowProcessor will have its own {@link ParsingContext}. Concrete implementations of this class * are expected to implement the {@link #switchRowProcessor(String[], Context)} method and analyze the input row * to determine whether or not the current {@link RowProcessor} implementation must be changed to handle a special * circumstance (determined by the concrete implementation) such as a different row format. * * When the row processor is switched, the {@link #rowProcessorSwitched(RowProcessor, RowProcessor)} will be called, and * must be overridden, to notify the change to the user. */ public abstract class RowProcessorSwitch extends AbstractProcessorSwitch implements RowProcessor { }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/RowWriterProcessor.java000066400000000000000000000067261400120543400341750ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import com.univocity.parsers.fixed.*; /** * The essential callback interface to convert input objects into a manageable format for writing. Used by any writer that extends {@link AbstractWriter}. * *

When writing to an output, the writer will obtain the RowWriterProcessor from {@link CommonWriterSettings#getRowWriterProcessor()}, and * invoke {@link RowWriterProcessor#write(Object, NormalizedString[], int[])} to convert the input to an array of objects. This array of objects will in turn be handed to the writer to produce a record in the expected format. * *

univocity-parsers provides some useful default implementations of this interface in the package {@link com.univocity.parsers.common.processor}, namely: * *

    *
  • {@link ObjectRowWriterProcessor}: used for executing conversions of Object values on input rows using instances of {@link Conversion} before writing to the output
  • *
  • {@link BeanWriterProcessor}: used for converting javabeans annotated with the annotations provided in package {@link com.univocity.parsers.annotations} into an object row before writing to the output
  • *
* * @see com.univocity.parsers.common.AbstractWriter * @see com.univocity.parsers.common.CommonWriterSettings * * @param the type that is converted by this implementation into an Object array, suitable for writing to the output. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface RowWriterProcessor { /** * * Converts the given input into an Object array that is suitable for writing. Used by format-specific writers that extend {@link AbstractWriter}. * * @param input The original input record that must be converted into an Object array before writing to an output. * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @return an Object array that is suitable for writing. If null or an empty array is returned then the writer might either skip this value or write an empty record (if {@link CommonSettings#getSkipEmptyLines()} is false) * * @see CsvWriter * @see FixedWidthWriter * @see CommonSettings * @see AbstractWriter */ Object[] write(T input, NormalizedString[] headers, int[] indexesToWrite); } RowWriterProcessorSwitch.java000066400000000000000000000142341400120543400352710ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import java.util.Arrays; import java.util.Map; /** * A special {@link RowWriterProcessor} implementation that combines and allows switching among different * RowWriterProcessors. Concrete implementations of this class * are expected to implement the {@code switchRowProcessor(T)} method and analyze the input row * to determine whether or not the current {@link RowWriterProcessor} implementation must be changed to handle a special * circumstance (determined by the concrete implementation) such as a different row format. * * When the row writer processor is switched, the {@link #rowProcessorSwitched(RowWriterProcessor, RowWriterProcessor)} * will be called, and must be overridden, to notify the change to the user. */ public abstract class RowWriterProcessorSwitch implements RowWriterProcessor { private RowWriterProcessor selectedRowWriterProcessor = null; private int minimumRowLength = Integer.MIN_VALUE; private NormalizedString[] normalizedHeaders; private String[] previousHeaders; /** * Analyzes an output row to determine whether or not the row writer processor implementation must be changed * * @param row a record with data to be written to the output * * @return the row writer processor implementation to use. If it is not the same as the one used by the previous row, * the returned row writer processor will be used, and the {@link #rowProcessorSwitched(RowWriterProcessor, RowWriterProcessor)} method * will be called. */ protected abstract RowWriterProcessor switchRowProcessor(Object row); /** * Returns the headers in use by the current row writer processor implementation, which can vary among row writer processors. * If {@code null}, the headers defined in {@link CommonWriterSettings#getHeaders()} will be returned. * * @return the current sequence of headers to use. */ protected NormalizedString[] getHeaders() { return null; } /** * Returns the indexes in use by the current row writer processor implementation, which can vary among row writer processors. * If {@code null}, the indexes of fields that have been selected using {@link CommonSettings#selectFields(String...)} * or {@link CommonSettings#selectIndexes(Integer...)} will be returned. * * @return the current sequence of indexes to use. */ protected int[] getIndexes() { return null; } /** * Notifies a change of row writer processor implementation. Users are expected to override this method to receive the notification. * * @param from the row writer processor previously in use * @param to the new row writer processor to use to continue processing the output rows. */ public void rowProcessorSwitched(RowWriterProcessor from, RowWriterProcessor to) { } /** * Returns the sequence of headers to use for processing an input record represented by a map * * A map of headers can be optionally provided to assign a name to the keys of the input map. This is useful when * the input map has keys will generate unwanted header names. * * @param headerMapping an optional map associating keys of the rowData map with expected header names * @param mapInput the record data * * @return the sequence of headers to use when processing the given input map. */ public abstract NormalizedString[] getHeaders(Map headerMapping, Map mapInput); /** * Returns the sequence of headers to use for processing an input record. * * @param input the record data * * @return the sequence of headers to use when processing the given record. */ public abstract NormalizedString[] getHeaders(Object input); protected abstract String describeSwitch(); /** * Returns the minimum row length based on the number of headers and index sizes * * @return the minimum length a row must have in order to be sent to the output */ public final int getMinimumRowLength() { if (minimumRowLength == Integer.MIN_VALUE) { minimumRowLength = 0; if (getHeaders() != null) { minimumRowLength = getHeaders().length; } if (getIndexes() != null) { for (int index : getIndexes()) { if (index + 1 > minimumRowLength) { minimumRowLength = index + 1; } } } } return minimumRowLength; } public Object[] write(Object input, String[] headers, int[] indicesToWrite) { if (previousHeaders != headers) { previousHeaders = headers; normalizedHeaders = NormalizedString.toArray(headers); } return write(input, normalizedHeaders, indicesToWrite); } @Override public Object[] write(Object input, NormalizedString[] headers, int[] indicesToWrite) { RowWriterProcessor processor = switchRowProcessor(input); if (processor == null) { DataProcessingException ex = new DataProcessingException("Cannot find switch for input. Headers: {headers}, indices to write: " + Arrays.toString(indicesToWrite) + ". " + describeSwitch()); ex.setValue("headers", Arrays.toString(headers)); ex.setValue(input); throw ex; } if (processor != selectedRowWriterProcessor) { rowProcessorSwitched(selectedRowWriterProcessor, processor); selectedRowWriterProcessor = processor; } NormalizedString[] headersToUse = getHeaders(); int[] indexesToUse = getIndexes(); headersToUse = headersToUse == null ? headers : headersToUse; indexesToUse = indexesToUse == null ? indicesToWrite : indexesToUse; return selectedRowWriterProcessor.write(input, headersToUse, indexesToUse); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/000077500000000000000000000000001400120543400304035ustar00rootroot00000000000000AbstractBatchedColumnProcessor.java000066400000000000000000000104001400120543400372560ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * A {@link Processor} implementation that stores values of columns in batches. Use this implementation in favor of {@link AbstractColumnProcessor} * when processing large inputs to avoid running out of memory. * * Values parsed in each row will be split into columns of Strings. Each column has its own list of values. * *

During the execution of the process, the {@link #batchProcessed(int)} method will be invoked after a given number of rows has been processed.

*

The user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

*

After {@link #batchProcessed(int)} is invoked, all values will be discarded and the next batch of column values will be accumulated. * This process will repeat until there's no more rows in the input. * * @author Univocity Software Pty Ltd - parsers@univocity.com * * @see AbstractParser * @see BatchedColumnReader * @see Processor */ public abstract class AbstractBatchedColumnProcessor implements Processor, BatchedColumnReader { private final ColumnSplitter splitter; private final int rowsPerBatch; private int batchCount; private int batchesProcessed; /** * Constructs a batched column processor configured to invoke the {@link #batchesProcessed} method after a given number of rows has been processed. * @param rowsPerBatch the number of rows to process in each batch. */ public AbstractBatchedColumnProcessor(int rowsPerBatch) { splitter = new ColumnSplitter(rowsPerBatch); this.rowsPerBatch = rowsPerBatch; } @Override public void processStarted(T context) { splitter.reset(); batchCount = 0; batchesProcessed = 0; } @Override public void rowProcessed(String[] row, T context) { splitter.addValuesToColumns(row, context); batchCount++; if (batchCount >= rowsPerBatch) { batchProcessed(batchCount); batchCount = 0; splitter.clearValues(); batchesProcessed++; } } @Override public void processEnded(T context) { if (batchCount > 0) { batchProcessed(batchCount); } } @Override public final String[] getHeaders() { return splitter.getHeaders(); } @Override public final List> getColumnValuesAsList() { return splitter.getColumnValues(); } @Override public final void putColumnValuesInMapOfNames(Map> map) { splitter.putColumnValuesInMapOfNames(map); } @Override public final void putColumnValuesInMapOfIndexes(Map> map) { splitter.putColumnValuesInMapOfIndexes(map); } @Override public final Map> getColumnValuesAsMapOfNames() { return splitter.getColumnValuesAsMapOfNames(); } @Override public final Map> getColumnValuesAsMapOfIndexes() { return splitter.getColumnValuesAsMapOfIndexes(); } @Override public List getColumn(String columnName) { return splitter.getColumnValues(columnName, String.class); } @Override public List getColumn(int columnIndex) { return splitter.getColumnValues(columnIndex, String.class); } @Override public int getRowsPerBatch() { return rowsPerBatch; } @Override public int getBatchesProcessed() { return batchesProcessed; } @Override public abstract void batchProcessed(int rowsInThisBatch); } AbstractBatchedObjectColumnProcessor.java000066400000000000000000000126471400120543400404240ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import java.util.*; /** * * A {@link Processor} implementation for converting batches of rows extracted from any implementation of {@link AbstractParser} into columns of objects. *

This uses the value conversions provided by {@link Conversion} instances.

* *

For each row processed, a sequence of conversions will be executed to generate the appropriate object. Each resulting object will then be stored in * a list that contains the values of the corresponding column.

* *

During the execution of the process, the {@link #batchProcessed(int)} method will be invoked after a given number of rows has been processed.

*

The user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

*

After {@link #batchProcessed(int)} is invoked, all values will be discarded and the next batch of column values will be accumulated. * This process will repeat until there's no more rows in the input. * * @see AbstractParser * @see Processor * @see BatchedColumnReader * @see Conversion * @see AbstractObjectProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractBatchedObjectColumnProcessor extends AbstractObjectProcessor implements Processor, BatchedColumnReader { private final ColumnSplitter splitter; private final int rowsPerBatch; private int batchCount; private int batchesProcessed; /** * Constructs a abstract batched column processor configured to invoke the {@link #batchesProcessed} method after a given number of rows has been processed. * @param rowsPerBatch the number of rows to process in each batch. */ public AbstractBatchedObjectColumnProcessor(int rowsPerBatch) { splitter = new ColumnSplitter(rowsPerBatch); this.rowsPerBatch = rowsPerBatch; } @Override public void processStarted(T context) { super.processStarted(context); splitter.reset(); batchCount = 0; batchesProcessed = 0; } @Override public void rowProcessed(Object[] row, T context) { splitter.addValuesToColumns(row, context); batchCount++; if (batchCount >= rowsPerBatch) { batchProcessed(batchCount); batchCount = 0; splitter.clearValues(); batchesProcessed++; } } @Override public void processEnded(T context) { super.processEnded(context); if (batchCount > 0) { batchProcessed(batchCount); } } @Override public final String[] getHeaders() { return splitter.getHeaders(); } @Override public final List> getColumnValuesAsList() { return splitter.getColumnValues(); } @Override public final void putColumnValuesInMapOfNames(Map> map) { splitter.putColumnValuesInMapOfNames(map); } @Override public final void putColumnValuesInMapOfIndexes(Map> map) { splitter.putColumnValuesInMapOfIndexes(map); } @Override public final Map> getColumnValuesAsMapOfNames() { return splitter.getColumnValuesAsMapOfNames(); } @Override public final Map> getColumnValuesAsMapOfIndexes() { return splitter.getColumnValuesAsMapOfIndexes(); } @Override public List getColumn(String columnName) { return splitter.getColumnValues(columnName, Object.class); } @Override public List getColumn(int columnIndex) { return splitter.getColumnValues(columnIndex, Object.class); } /** * Returns the values of a given column. * @param columnName the name of the column in the input. * @param columnType the type of data in that column * @param the type of data in that column * @return a list with all data stored in the given column */ public List getColumn(String columnName, Class columnType){ return splitter.getColumnValues(columnName, columnType); } /** * Returns the values of a given column. * @param columnIndex the position of the column in the input (0-based). * @param columnType the type of data in that column * @param the type of data in that column * @return a list with all data stored in the given column */ public List getColumn(int columnIndex, Class columnType){ return splitter.getColumnValues(columnIndex, columnType); } @Override public int getRowsPerBatch() { return rowsPerBatch; } @Override public int getBatchesProcessed() { return batchesProcessed; } @Override public abstract void batchProcessed(int rowsInThisBatch); } AbstractBeanListProcessor.java000066400000000000000000000101611400120543400362530ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import java.util.*; /** * A convenience {@link Processor} implementation for storing all java objects generated form the parsed input into a list. * A typical use case of this class will be: * *
{@code
 *
 * parserSettings.setRowProcessor(new BeanListProcessor(MyObject.class));
 * parser.parse(reader); // will invoke the {@link AbstractBeanListProcessor#beanProcessed(Object, C)} method for each generated object.
 *
 * List<T> beans = rowProcessor.getBeans();
 * }

* * @param the annotated class type. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see Processor * @see AbstractParser * @see AbstractBeanProcessor * @see BeanConversionProcessor */ public abstract class AbstractBeanListProcessor extends AbstractBeanProcessor { private List beans; private String[] headers; private final int expectedBeanCount; /** * Creates a processor that stores java beans of a given type into a list * * @param beanType the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public AbstractBeanListProcessor(Class beanType) { this(beanType, 0); } /** * Creates a processor that stores java beans of a given type into a list * * @param beanType the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} returned by {@link #getBeans()} */ public AbstractBeanListProcessor(Class beanType, int expectedBeanCount) { super(beanType, MethodFilter.ONLY_SETTERS); this.expectedBeanCount = expectedBeanCount <= 0 ? 10000 : expectedBeanCount; } /** * Stores the generated java bean produced with a parsed record into a list. * * @param bean java bean generated with the information extracted by the parser for an individual record * @param context A contextual object with information and controls over the current state of the parsing process * * @see com.univocity.parsers.common.processor.BeanProcessor */ @Override public void beanProcessed(T bean, C context) { beans.add(bean); } /** * Returns the list of generated java beans at the end of the parsing process. * * @return the list of generated java beans at the end of the parsing process. */ public List getBeans() { return beans == null ? Collections.emptyList() : beans; } @Override public void processStarted(C context) { super.processStarted(context); beans = new ArrayList(expectedBeanCount); } @Override public void processEnded(C context) { headers = context.headers(); super.processEnded(context); } /** * Returns the record headers. This can be either the headers defined in {@link CommonSettings#getHeaders()} or the headers parsed in the file when {@link CommonSettings#getHeaders()} equals true * * @return the headers of all records parsed. */ public String[] getHeaders() { return headers; } } AbstractBeanProcessor.java000066400000000000000000000063731400120543400354310ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; /** * A {@link Processor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects. *

The class type of the object must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For each row processed, a java bean instance of a given class will be created with its fields populated. *

This instance will then be sent to the {@link AbstractBeanProcessor#beanProcessed(Object, Context)} method, where the user can access it. * * @param the annotated class type. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see Processor */ public abstract class AbstractBeanProcessor extends BeanConversionProcessor implements Processor { /** * Creates a processor for java beans of a given type. * * @param beanType the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. * @param methodFilter filter to apply over annotated methods when the processor is reading data from beans (to write values to an output) * or writing values into beans (when parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. */ public AbstractBeanProcessor(Class beanType, MethodFilter methodFilter) { super(beanType, methodFilter); } /** * Converts a parsed row to a java object */ @Override public final void rowProcessed(String[] row, C context) { T instance = createBean(row, context); if (instance != null) { beanProcessed(instance, context); } } /** * Invoked by the processor after all values of a valid record have been processed and converted into a java object. * * @param bean java object created with the information extracted by the parser for an individual record. * @param context A contextual object with information and controls over the current state of the parsing process */ public abstract void beanProcessed(T bean, C context); @Override public void processStarted(C context) { super.initialize(NormalizedString.toArray(context.headers())); } @Override public void processEnded(C context) { } } AbstractColumnProcessor.java000066400000000000000000000066601400120543400360200ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * A simple {@link Processor} implementation that stores values of columns. * Values parsed in each row will be split into columns of Strings. Each column has its own list of values. * *

At the end of the process, the user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

* * *

Note: Storing the values of all columns may be memory intensive. For large inputs, use a {@link AbstractBatchedColumnProcessor} instead

* * @author Univocity Software Pty Ltd - parsers@univocity.com * * @see AbstractParser * @see Processor * @see ColumnReader */ public abstract class AbstractColumnProcessor implements Processor, ColumnReader { private final ColumnSplitter splitter; /** * Constructs a column processor, pre-allocating room for 1000 rows. */ public AbstractColumnProcessor() { this(1000); } /** * Constructs a column processor pre-allocating room for the expected number of rows to be processed * @param expectedRowCount the expected number of rows to be processed */ public AbstractColumnProcessor(int expectedRowCount) { splitter = new ColumnSplitter(expectedRowCount); } @Override public void processStarted(T context) { splitter.reset(); } @Override public void rowProcessed(String[] row, T context) { splitter.addValuesToColumns(row, context); } @Override public void processEnded(T context) { } @Override public final String[] getHeaders() { return splitter.getHeaders(); } @Override public final List> getColumnValuesAsList() { return splitter.getColumnValues(); } @Override public final void putColumnValuesInMapOfNames(Map> map) { splitter.putColumnValuesInMapOfNames(map); } @Override public final void putColumnValuesInMapOfIndexes(Map> map) { splitter.putColumnValuesInMapOfIndexes(map); } @Override public final Map> getColumnValuesAsMapOfNames() { return splitter.getColumnValuesAsMapOfNames(); } @Override public final Map> getColumnValuesAsMapOfIndexes() { return splitter.getColumnValuesAsMapOfIndexes(); } @Override public List getColumn(String columnName) { return splitter.getColumnValues(columnName, String.class); } @Override public List getColumn(int columnIndex) { return splitter.getColumnValues(columnIndex, String.class); } } AbstractConcurrentProcessor.java000066400000000000000000000155471400120543400367110ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.concurrent.*; /** * A {@link Processor} implementation to perform row processing tasks in parallel. The {@code ConcurrentRowProcessor} * wraps another {@link Processor}, and collects rows read from the input. * The actual row processing is performed in by wrapped {@link Processor} in a separate thread. * * Note: by default the {@link Context} object passed on to the wrapped {@link Processor} will not reflect the * state of the parser at the time the row as generated, but the current state of the parser instead. You can enable the * {@link #contextCopyingEnabled} flag to generate copies of the {@link Context} at the time each row was generated. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see Processor */ public abstract class AbstractConcurrentProcessor implements Processor { private final Processor processor; private boolean ended = false; private static class Node { public Node(String[] row, T context) { this.row = row; this.context = context; } public final T context; public final String[] row; public Node next; } private final ExecutorService executor = Executors.newSingleThreadExecutor(); private volatile long rowCount; private Future process; private T currentContext; private Node inputQueue; private volatile Node outputQueue; private final int limit; private volatile long input; private volatile long output; private final Object lock; private boolean contextCopyingEnabled = false; /** * Creates a non-blocking {@code AbstractConcurrentProcessor}, to perform processing of rows parsed from the input in a separate thread. * * @param processor a regular {@link Processor} implementation which will be executed in a separate thread. */ public AbstractConcurrentProcessor(Processor processor) { this(processor, -1); } /** * Creates a blocking {@code ConcurrentProcessor}, to perform processing of rows parsed from the input in a separate thread. * * @param processor a regular {@link Processor} implementation which will be executed in a separate thread. * @param limit the limit of rows to be kept in memory before blocking the input parsing process. */ public AbstractConcurrentProcessor(Processor processor, int limit) { if (processor == null) { throw new IllegalArgumentException("Row processor cannot be null"); } this.processor = processor; input = 0; output = 0; lock = new Object(); this.limit = limit; } /** * Indicates whether this processor should persist the {@link Context} object that is sent to the wrapped {@link Processor} * given in the constructor of this class, so all methods of {@link Context} reflect the parser state at the time * each row was parsed. * * Defaults to {@code false} * * @return a flag indicating whether the parsing context must be persisted along with the parsed row * so its methods reflect the state of the parser at the time the record was produced. */ public boolean isContextCopyingEnabled() { return contextCopyingEnabled; } /** * Configures this processor to persist the {@link Context} object that is sent to the wrapped {@link Processor} * given in the constructor of this class, so all methods of {@link Context} reflect the parser state at the time * each row was parsed. * * Defaults to {@code false} * * @param contextCopyingEnabled a flag indicating whether the parsing context must be persisted along with the parsed row * so its methods reflect the state of the parser at the time the record was produced. */ public void setContextCopyingEnabled(boolean contextCopyingEnabled) { this.contextCopyingEnabled = contextCopyingEnabled; } @Override public final void processStarted(T context) { currentContext = wrapContext(context); processor.processStarted(currentContext); startProcess(); } private void startProcess() { ended = false; rowCount = 0; process = executor.submit(new Callable() { @Override public Void call() { while (outputQueue == null && !ended) { Thread.yield(); } while (!ended) { rowCount++; processor.rowProcessed(outputQueue.row, outputQueue.context); while (outputQueue.next == null) { if (ended && outputQueue.next == null) { return null; } Thread.yield(); } outputQueue = outputQueue.next; output++; if (limit > 1) { synchronized (lock) { lock.notify(); } } } while (outputQueue != null) { rowCount++; processor.rowProcessed(outputQueue.row, outputQueue.context); outputQueue = outputQueue.next; } return null; } }); } @Override public final void rowProcessed(String[] row, T context) { if (inputQueue == null) { inputQueue = new Node(row, grabContext(context)); outputQueue = inputQueue; } else { if (limit > 1) { synchronized (lock) { try { if (input - output >= limit) { lock.wait(); } } catch (InterruptedException e) { ended = true; Thread.currentThread().interrupt(); return; } } } inputQueue.next = new Node(row, grabContext(context)); inputQueue = inputQueue.next; } input++; } @Override public final void processEnded(T context) { ended = true; if (limit > 1) { synchronized (lock) { lock.notify(); } } try { process.get(); } catch (ExecutionException e) { throw new DataProcessingException("Error executing process", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { try { processor.processEnded(grabContext(context)); } finally{ executor.shutdown(); } } } private T grabContext(T context) { if (contextCopyingEnabled) { return copyContext(context); } return currentContext; } protected final long getRowCount(){ return rowCount; } protected abstract T copyContext(T context); protected abstract T wrapContext(T context); } AbstractInputValueSwitch.java000066400000000000000000000314551400120543400361410ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import java.util.*; /** * A concrete implementation of {@link Processor} that allows switching among different implementations of * {@link Processor} based on values found on the rows parsed from the input. */ public abstract class AbstractInputValueSwitch extends AbstractProcessorSwitch { private int columnIndex = -1; private NormalizedString columnName = null; private Switch[] switches = new Switch[0]; private Switch defaultSwitch = null; private String[] headers; private int[] indexes; private static final Comparator caseSensitiveComparator = new Comparator() { @Override public int compare(String o1, String o2) { return (o1 == o2 || o1 != null && o1.equals(o2)) ? 0 : 1; //strings are interned, no issues here } }; private static final Comparator caseInsensitiveComparator = new Comparator() { @Override public int compare(String o1, String o2) { return (o1 == o2 || o1 != null && o1.equalsIgnoreCase(o2)) ? 0 : 1; //strings are interned, no issues here } }; private Comparator comparator = caseInsensitiveComparator; /** * Creates a switch that will analyze the first column of rows found in the input to determine which * {@link Processor} to use for each parsed row */ public AbstractInputValueSwitch() { this(0); } /** * Creates a switch that will analyze a column of rows parsed from the input to determine which * {@link Processor} to use. * * @param columnIndex the column index whose value will be used to determine which {@link Processor} to use for each parsed row. */ public AbstractInputValueSwitch(int columnIndex) { if (columnIndex < 0) { throw new IllegalArgumentException("Column index must be positive"); } this.columnIndex = columnIndex; } /** * Creates a switch that will analyze a column in rows parsed from the input to determine which * {@link Processor} to use. * * @param columnName name of the column whose values will be used to determine which {@link Processor} to use for each parsed row. */ public AbstractInputValueSwitch(String columnName) { if (columnName == null || columnName.trim().isEmpty()) { throw new IllegalArgumentException("Column name cannot be blank"); } this.columnName = NormalizedString.valueOf(columnName); } /** * Configures the switch to be case sensitive when comparing values provided in {@link #addSwitchForValue(String, Processor, String...)} * with the column given in the constructor of this class. * * @param caseSensitive a flag indicating whether the switch should compare values not considering the character case. */ public void setCaseSensitive(boolean caseSensitive) { this.comparator = caseSensitive ? caseSensitiveComparator : caseInsensitiveComparator; } /** * Configures the switch to use a custom {@link Comparator} to compare values in the column to analyze which is given in the constructor of this class. * * @param comparator the comparator to use for matching values in the input column with the values provided in {@link #addSwitchForValue(String, Processor, String...)} */ public void setComparator(Comparator comparator) { if (comparator == null) { throw new IllegalArgumentException("Comparator must not be null"); } this.comparator = comparator; } /** * Defines a default {@link Processor} implementation to use when no matching value is found in the input row. * * @param processor the default processor implementation * @param headersToUse the (optional) sequence of headers to assign to the {@link ParsingContext} of the given processor */ public void setDefaultSwitch(Processor processor, String... headersToUse) { defaultSwitch = new Switch(processor, headersToUse, null, null, null); } /** * Defines a default {@link Processor} implementation to use when no matching value is found in the input row. * * @param processor the default processor implementation */ public void setDefaultSwitch(Processor processor) { defaultSwitch = new Switch(processor, null, null, null, null); } /** * Defines a default {@link Processor} implementation to use when no matching value is found in the input row. * * @param processor the default processor implementation * @param indexesToUse the (optional) sequence of column indexes to assign to the {@link ParsingContext} of the given processor */ public void setDefaultSwitch(Processor processor, int... indexesToUse) { defaultSwitch = new Switch(processor, null, indexesToUse, null, null); } /** * Returns a flag indicating whether this switch contains a default {@link Processor} implementation to use when no matching value is found in the input row. * @return {@code true} if a {@link Processor} implementation has been provided to process input rows that doesn't have any matching value. */ public boolean hasDefaultSwitch() { return defaultSwitch != null; } /** * Associates a {@link Processor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param value the value to match against the column of the current input row and trigger the usage of the given processor implementation. * @param processor the processor implementation when the given value matches with the contents in the column provided in the constructor of this class. */ public void addSwitchForValue(String value, Processor processor) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = new Switch(processor, null, null, value, null); } /** * Associates a {@link Processor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param value the value to match against the column of the current input row and trigger the usage of the given processor implementation. * @param processor the processor implementation when the given value matches with the contents in the column provided in the constructor of this class. * @param headersToUse the (optional) sequence of headers to assign to the {@link ParsingContext} of the given processor */ public void addSwitchForValue(String value, Processor processor, String... headersToUse) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = new Switch(processor, headersToUse, null, value, null); } /** * Associates a {@link Processor} implementation with a custom matching algorithm to be executed in the column provided in the constructor of this class. * * @param matcher a user defined matching implementation to execute against the values in the column of the current input row and trigger the usage of the given processor implementation. * @param processor the processor implementation when the given value matches with the contents in the column provided in the constructor of this class. */ public void addSwitchForValue(CustomMatcher matcher, Processor processor) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = new Switch(processor, null, null, null, matcher); } /** * Associates a {@link Processor} implementation with a custom matching algorithm to be executed in the column provided in the constructor of this class. * * @param matcher a user defined matching implementation to execute against the values in the column of the current input row and trigger the usage of the given processor implementation. * @param processor the processor implementation when the given value matches with the contents in the column provided in the constructor of this class. * @param headersToUse the (optional) sequence of headers to assign to the {@link ParsingContext} of the given processor */ public void addSwitchForValue(CustomMatcher matcher, Processor processor, String... headersToUse) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = new Switch(processor, headersToUse, null, null, matcher); } /** * Associates a {@link Processor} implementation with an expected value to be matched in the column provided in the constructor of this class. * * @param value the value to match against the column of the current input row and trigger the usage of the given processor implementation. * @param processor the processor implementation when the given value matches with the contents in the column provided in the constructor of this class. * @param indexesToUse the (optional) sequence of column indexes to assign to the {@link ParsingContext} of the given processor */ public void addSwitchForValue(String value, Processor processor, int... indexesToUse) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = new Switch(processor, null, indexesToUse, value, null); } /** * Associates a {@link Processor} implementation with a custom matching algorithm to be executed in the column provided in the constructor of this class. * * @param matcher a user defined matching implementation to execute against the values in the column of the current input row and trigger the usage of the given processor implementation. * @param processor the processor implementation when the given value matches with the contents in the column provided in the constructor of this class. * @param indexesToUse the (optional) sequence of column indexes to assign to the {@link ParsingContext} of the given processor */ public void addSwitchForValue(CustomMatcher matcher, Processor processor, int... indexesToUse) { switches = Arrays.copyOf(switches, switches.length + 1); switches[switches.length - 1] = new Switch(processor, null, indexesToUse, null, matcher); } @Override public String[] getHeaders() { return headers; } @Override public int[] getIndexes() { return indexes; } @Override protected final Processor switchRowProcessor(String[] row, T context) { if (columnIndex == -1) { NormalizedString[] headers = NormalizedString.toIdentifierGroupArray(context.headers()); if (headers == null) { throw new DataProcessingException("Unable to determine position of column named '" + columnName + "' as no headers have been defined nor extracted from the input"); } columnIndex = ArgumentUtils.indexOf(headers, columnName); if (columnIndex == -1) { throw new DataProcessingException("Unable to determine position of column named '" + columnName + "' as it does not exist in the headers. Available headers are " + Arrays.toString(headers)); } } if (columnIndex < row.length) { String valueToMatch = row[columnIndex]; for (int i = 0; i < switches.length; i++) { Switch s = switches[i]; if ((s.matcher != null && s.matcher.matches(valueToMatch)) || comparator.compare(valueToMatch, s.value) == 0) { headers = s.headers; indexes = s.indexes; return s.processor; } } } if (defaultSwitch != null) { headers = defaultSwitch.headers; indexes = defaultSwitch.indexes; return defaultSwitch.processor; } headers = null; indexes = null; throw new DataProcessingException("Unable to process input row. No switches activated and no default switch defined.", columnIndex, row, null); } private static class Switch { final Processor processor; final String[] headers; final int[] indexes; final String value; final CustomMatcher matcher; Switch(Processor processor, String[] headers, int[] indexes, String value, CustomMatcher matcher) { this.processor = processor; this.headers = headers == null || headers.length == 0 ? null : headers; this.indexes = indexes == null || indexes.length == 0 ? null : indexes; this.value = value == null ? null : value.intern(); this.matcher = matcher; } @Override public String toString() { return "Switch{" + "processor=" + processor + ", headers=" + Arrays.toString(headers) + ", indexes=" + Arrays.toString(indexes) + ", value='" + value + '\'' + ", matcher=" + matcher + '}'; } } } AbstractListProcessor.java000077500000000000000000000067561400120543400355070ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * * A convenience {@link Processor} implementation for storing all rows parsed into a list. * A typical use case of this class will be: * *
 *
 * parserSettings.setRowProcessor(new RowListProcessor());
 * parser.parse(reader); // will invoke the {@link AbstractListProcessor#rowProcessed(String[], Context)} method for each parsed record.
 *
 * String[] headers = rowProcessor.getHeaders();
 * List<String[]> rows = rowProcessor.getRows();
 *
 * 

* * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractListProcessor implements Processor { private List rows; private String[] headers; private final int expectedRowCount; /** * Creates a new processor of {@code String[]} rows. */ public AbstractListProcessor() { this(0); } /** * Creates a new processor of {@code String[]} rows. * * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} returned by {@link #getRows()} */ public AbstractListProcessor(int expectedRowCount) { this.expectedRowCount = expectedRowCount <= 0 ? 10000 : expectedRowCount; } @Override public void processStarted(T context) { rows = new ArrayList(expectedRowCount); } /** * Stores the row extracted by the parser into a list. * * @param row the data extracted by the parser for an individual record. Note that: *
    *
  • it will never by null.
  • *
  • it will never be empty unless explicitly configured using {@link CommonSettings#setSkipEmptyLines(boolean)}
  • *
  • it won't contain lines identified by the parser as comments. To disable comment processing set {@link Format#setComment(char)} to '\0'
  • *
* @param context A contextual object with information and controls over the current state of the parsing process */ @Override public void rowProcessed(String[] row, T context) { rows.add(row); } @Override public void processEnded(T context) { headers = context.headers(); } /** * The list of parsed records * @return the list of parsed records */ public List getRows() { return rows == null ? Collections.emptyList() : rows; } /** * Returns the record headers. This can be either the headers defined in {@link CommonSettings#getHeaders()} or the headers parsed in the file when {@link CommonSettings#getHeaders()} equals true * @return the headers of all records parsed. */ public String[] getHeaders() { return headers; } } AbstractMasterDetailListProcessor.java000066400000000000000000000103571400120543400377730ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import java.util.*; /** * * A convenience {@link Processor} implementation for storing all {@link MasterDetailRecord} generated form the parsed input into a list. * A typical use case of this class will be: * *
{@code
 *
 * ObjectRowListProcessor detailProcessor = new ObjectRowListProcessor();
 * MasterDetailListProcessor masterRowProcessor = new MasterDetailListProcessor(detailProcessor) {
 *      protected boolean isMasterRecord(String[] row, ParsingContext context) {
 *          return "Total".equals(row[0]);
 *      }
 * };
 *
 * parserSettings.setRowProcessor(masterRowProcessor);
 *
 * List<MasterDetailRecord> rows = masterRowProcessor.getRecords();
 * }

* * @see AbstractMasterDetailProcessor * @see Processor * @see AbstractParser * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractMasterDetailListProcessor extends AbstractMasterDetailProcessor { private final List records = new ArrayList(); private String[] headers; /** * Creates a MasterDetailListProcessor * * @param rowPlacement indication whether the master records are placed in relation its detail records in the input. * *
	 *
	 * Master record (Totals)       Master record (Totals)
	 *  above detail records         under detail records
	 *
	 *    Totals | 100                 Item   | 60
	 *    Item   | 60                  Item   | 40
	 *    Item   | 40                  Totals | 100
	 * 

* @param detailProcessor the {@link ObjectRowListProcessor} that processes detail rows. */ public AbstractMasterDetailListProcessor(RowPlacement rowPlacement, AbstractObjectListProcessor detailProcessor) { super(rowPlacement, detailProcessor); } /** * Creates a MasterDetailListProcessor assuming master records are positioned above its detail records in the input. * * @param detailProcessor the {@link ObjectRowListProcessor} that processes detail rows. */ public AbstractMasterDetailListProcessor(AbstractObjectListProcessor detailProcessor) { super(detailProcessor); } /** * Stores the generated {@link MasterDetailRecord} with the set of associated parsed records into a list. * * @param record {@link MasterDetailRecord} generated with a set of associated records extracted by the parser * @param context A contextual object with information and controls over the current state of the parsing process * * @see MasterDetailRecord */ @Override protected void masterDetailRecordProcessed(MasterDetailRecord record, T context) { records.add(record); } @Override public void processEnded(T context) { headers = context.headers(); super.processEnded(context); } /** * Returns the list of generated MasterDetailRecords at the end of the parsing process. * @return the list of generated MasterDetailRecords at the end of the parsing process. */ public List getRecords() { return this.records; } /** * Returns the record headers. This can be either the headers defined in {@link CommonSettings#getHeaders()} or the headers parsed in the file when {@link CommonSettings#getHeaders()} equals true * @return the headers of all records parsed. */ public String[] getHeaders() { return headers; } } AbstractMasterDetailProcessor.java000066400000000000000000000157311400120543400371400ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.util.*; /** * * A {@link Processor} implementation for associating rows extracted from any implementation of {@link AbstractParser} into {@link MasterDetailRecord} instances. * *

For each row processed, a call to {@link AbstractMasterDetailProcessor#isMasterRecord(String[], Context)} will be made to identify whether or not it is a master row. *

The detail rows are automatically associated with the master record in an instance of {@link MasterDetailRecord}. *

When the master record is fully processed (i.e. {@link MasterDetailRecord} contains a master row and all associated detail rows), * it is sent to the user for processing in {@link AbstractMasterDetailProcessor#masterDetailRecordProcessed(MasterDetailRecord, Context)}. * *

Note this class extends {@link AbstractObjectProcessor} and value conversions provided by {@link Conversion} instances are fully supported. * * @see MasterDetailRecord * @see RowPlacement * @see AbstractParser * @see ObjectRowListProcessor * @see Processor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractMasterDetailProcessor extends AbstractObjectProcessor { private final AbstractObjectListProcessor detailProcessor; private MasterDetailRecord record; private final boolean isMasterRowAboveDetail; /** * Creates a MasterDetailProcessor * * @param rowPlacement indication whether the master records are placed in relation its detail records in the input. * *


	 *
	 * Master record (Totals)       Master record (Totals)
	 *  above detail records         under detail records
	 *
	 *    Totals | 100                 Item   | 60
	 *    Item   | 60                  Item   | 40
	 *    Item   | 40                  Totals | 100
	 * 

* @param detailProcessor the {@link ObjectRowListProcessor} that processes detail rows. */ public AbstractMasterDetailProcessor(RowPlacement rowPlacement, AbstractObjectListProcessor detailProcessor) { ArgumentUtils.noNulls("Row processor for reading detail rows", detailProcessor); this.detailProcessor = detailProcessor; this.isMasterRowAboveDetail = rowPlacement == RowPlacement.TOP; } /** * Creates a MasterDetailProcessor assuming master records are positioned above its detail records in the input. * * @param detailProcessor the {@link AbstractObjectListProcessor} that processes detail rows. */ public AbstractMasterDetailProcessor(AbstractObjectListProcessor detailProcessor) { this(RowPlacement.TOP, detailProcessor); } @Override public void processStarted(T context) { detailProcessor.processStarted(context); } /** * Invoked by the parser after all values of a valid record have been processed. * *

This method will then try to identify whether the given record is a master record. *

If it is, any conversions applied to the fields of the master record will be executed; *

Otherwise, the parsed row will be delegated to the {@link AbstractMasterDetailProcessor#detailProcessor} given in the constructor, and a detail record will be associated with the current {@link MasterDetailRecord} * * * @param row the data extracted by the parser for an individual record. * @param context A contextual object with information and controls over the current state of the parsing process */ @Override public final void rowProcessed(String[] row, T context) { if (isMasterRecord(row, context)) { super.rowProcessed(row, context); } else { if (isMasterRowAboveDetail && record == null) { return; } detailProcessor.rowProcessed(row, context); } } /** * Invoked by the parser after all values of a valid record have been processed and any conversions have been executed. * * @param row the data extracted by the parser for an individual record. * @param context A contextual object with information and controls over the current state of the parsing process */ @Override public final void rowProcessed(Object[] row, T context) { if (record == null) { record = new MasterDetailRecord(); record.setMasterRow(row); if (isMasterRowAboveDetail) { return; } } processRecord(row, context); } /** * Associates individual rows to a {@link MasterDetailRecord} and invokes {@link AbstractMasterDetailProcessor#masterDetailRecordProcessed(MasterDetailRecord, T)} when it is fully populated. * @param row a record extracted from the parser that had all (if any) conversions executed and is ready to be sent to the user. * @param context A contextual object with information and controls over the current state of the parsing process */ private void processRecord(Object[] row, T context) { List detailRows = detailProcessor.getRows(); record.setDetailRows(new ArrayList(detailRows)); if (!isMasterRowAboveDetail) { record.setMasterRow(row); } if (record.getMasterRow() != null) { masterDetailRecordProcessed(record.clone(), context); record.clear(); } detailRows.clear(); if (isMasterRowAboveDetail) { record.setMasterRow(row); } } @Override public void processEnded(T context) { super.processEnded(context); detailProcessor.processEnded(context); if (isMasterRowAboveDetail) { processRecord(null, context); } } /** * Queries whether or not the given row is a master record. * @param row the data extracted by the parser for an individual record. * @param context A contextual object with information and controls over the current state of the parsing process * @return true if the row is a master record, false if it is a detail record. */ protected abstract boolean isMasterRecord(String[] row, T context); /** * Invoked by the processor after a master row and all associated detail rows have been processed. * * @param record The master detail records * @param context A contextual object with information and controls over the current state of the parsing process */ protected abstract void masterDetailRecordProcessed(MasterDetailRecord record, T context); } AbstractMultiBeanListProcessor.java000066400000000000000000000113301400120543400372650ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import java.util.*; /** * A {@link Processor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects, storing * them into lists. This processor stores beans in separate lists, one for each type of bean processed. * All lists of all types will have the same number of entries as the number of records in the input. * When an object of a particular type can't be generated from a row, {@code null} will be added to the list. This ensures all lists are the same size, * and each element of each list contains the exact information parsed from each row. * *

The class types passed to the constructor of this class must contain the annotations provided in {@link com.univocity.parsers.annotations}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see Processor * @see AbstractBeanProcessor * @see AbstractMultiBeanProcessor */ public class AbstractMultiBeanListProcessor extends AbstractMultiBeanRowProcessor { private final Class[] beanTypes; private final List[] beans; private String[] headers; private final int expectedBeanCount; /** * Creates a processor for java beans of multiple types * * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} returned by {@link #getBeans()} * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public AbstractMultiBeanListProcessor(int expectedBeanCount, Class... beanTypes) { super(beanTypes); this.beanTypes = beanTypes; this.beans = new List[beanTypes.length]; this.expectedBeanCount = expectedBeanCount <= 0 ? 10000 : expectedBeanCount; } /** * Creates a processor for java beans of multiple types * * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public AbstractMultiBeanListProcessor(Class... beanTypes) { this(0, beanTypes); } @Override public final void processStarted(C context) { super.processStarted(context); for (int i = 0; i < beanTypes.length; i++) { beans[i] = new ArrayList(expectedBeanCount); } } @Override protected final void rowProcessed(Map, Object> row, C context) { for (int i = 0; i < beanTypes.length; i++) { Object bean = row.get(beanTypes[i]); beans[i].add(bean); } } @Override public final void processEnded(C context) { headers = context.headers(); super.processEnded(context); } /** * Returns the record headers. This can be either the headers defined in {@link CommonSettings#getHeaders()} or the headers parsed in the file when {@link CommonSettings#getHeaders()} equals true * * @return the headers of all records parsed. */ public final String[] getHeaders() { return headers; } /** * Returns the beans of a given type processed from the input. * * @param beanType the type of bean processed * @param the type of bean processed * * @return a list with all beans of the given that were processed from the input. Might contain nulls. */ public List getBeans(Class beanType) { int index = ArgumentUtils.indexOf(beanTypes, beanType); if (index == -1) { throw new IllegalArgumentException("Unknown bean type '" + beanType.getSimpleName() + "'. Available types are: " + Arrays.toString(beanTypes)); } return beans[index]; } /** * Returns a map of all beans processed from the input. * * @return all beans processed from the input. */ public Map, List> getBeans() { LinkedHashMap, List> out = new LinkedHashMap, List>(); for (int i = 0; i < beanTypes.length; i++) { out.put(beanTypes[i], beans[i]); } return out; } } AbstractMultiBeanProcessor.java000066400000000000000000000132721400120543400364400ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.conversions.*; import java.util.*; /** * A {@link Processor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects. * *

The class types passed to the constructor of this class must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For each row processed, one or more java bean instances of any given class will be created with their fields populated. *

Each individual instance will then be sent to the {@link AbstractMultiBeanProcessor#beanProcessed(Class, Object, Context)} method, where the user can access the * beans parsed for each row. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see Processor * @see com.univocity.parsers.common.processor.BeanProcessor */ public abstract class AbstractMultiBeanProcessor implements Processor, ConversionProcessor { private final AbstractBeanProcessor[] beanProcessors; private final Map processorMap = new HashMap(); /** * Creates a processor for java beans of multiple types * * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public AbstractMultiBeanProcessor(Class... beanTypes) { ArgumentUtils.noNulls("Bean types", beanTypes); this.beanProcessors = new AbstractBeanProcessor[beanTypes.length]; for (int i = 0; i < beanTypes.length; i++) { final Class type = beanTypes[i]; beanProcessors[i] = new AbstractBeanProcessor(type, MethodFilter.ONLY_SETTERS) { @Override public void beanProcessed(Object bean, C context) { AbstractMultiBeanProcessor.this.beanProcessed(type, bean, context); } }; processorMap.put(type, beanProcessors[i]); } } public final Class[] getBeanClasses() { Class[] classes = new Class[beanProcessors.length]; for (int i = 0; i < beanProcessors.length; i++) { classes[i] = beanProcessors[i].beanClass; } return classes; } /** * Returns the {@link com.univocity.parsers.common.processor.BeanProcessor} responsible for processing a given class * * @param type the type of java bean being processed * @param the type of java bean being processed * * @return the {@link com.univocity.parsers.common.processor.BeanProcessor} that handles java beans of the given class. */ public AbstractBeanProcessor getProcessorOfType(Class type) { AbstractBeanProcessor processor = processorMap.get(type); if (processor == null) { throw new IllegalArgumentException("No processor of type '" + type.getName() + "' is available. Supported types are: " + processorMap.keySet()); } return processor; } /** * Invoked by the processor after all values of a valid record have been processed and converted into a java object. * * @param beanType the type of the object created by the parser using the information collected for an individual record. * @param beanInstance java object created with the information extracted by the parser for an individual record. * @param context A contextual object with information and controls over the current state of the parsing process */ public abstract void beanProcessed(Class beanType, Object beanInstance, C context); @Override public void processStarted(C context) { for (int i = 0; i < beanProcessors.length; i++) { beanProcessors[i].processStarted(context); } } @Override public final void rowProcessed(String[] row, C context) { for (int i = 0; i < beanProcessors.length; i++) { beanProcessors[i].rowProcessed(row, context); } } @Override public void processEnded(C context) { for (int i = 0; i < beanProcessors.length; i++) { beanProcessors[i].processEnded(context); } } @Override public FieldSet convertIndexes(Conversion... conversions) { List> sets = new ArrayList>(beanProcessors.length); for (int i = 0; i < beanProcessors.length; i++) { sets.add(beanProcessors[i].convertIndexes(conversions)); } return new FieldSet(sets); } @Override public void convertAll(Conversion... conversions) { for (int i = 0; i < beanProcessors.length; i++) { beanProcessors[i].convertAll(conversions); } } @Override public FieldSet convertFields(Conversion... conversions) { List> sets = new ArrayList>(beanProcessors.length); for (int i = 0; i < beanProcessors.length; i++) { sets.add(beanProcessors[i].convertFields(conversions)); } return new FieldSet(sets); } @Override public void convertType(Class type, Conversion... conversions) { for (int i = 0; i < beanProcessors.length; i++) { beanProcessors[i].convertType(type, conversions); } } } AbstractMultiBeanRowProcessor.java000066400000000000000000000061661400120543400371340ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * * A {@link Processor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into java objects. * *

The class types passed to the constructor of this class must contain the annotations provided in {@link com.univocity.parsers.annotations}. * *

For each row processed, one or more java bean instances of any given class will be created with their fields populated. *

Once all beans are populated from an individual input record, they will be sent to through the {@link AbstractMultiBeanRowProcessor#rowProcessed(Map, Context)} method, * where the user can access all beans parsed for that row. * * @see AbstractParser * @see Processor * @see AbstractBeanProcessor * @see AbstractMultiBeanProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractMultiBeanRowProcessor extends AbstractMultiBeanProcessor { private final HashMap, Object> row = new HashMap, Object>(); private long record = -1L; /** * Creates a processor for java beans of multiple types * @param beanTypes the classes with their attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}. */ public AbstractMultiBeanRowProcessor(Class... beanTypes) { super(beanTypes); } public void processStarted(C context) { record = -1L; row.clear(); super.processStarted(context); } @Override public final void beanProcessed(Class beanType, Object beanInstance, C context) { if(record != context.currentRecord() && record != -1L){ submitRow(context); } record = context.currentRecord(); row.put(beanType, beanInstance); } private void submitRow(C context){ if(!row.isEmpty()){ rowProcessed(row, context); row.clear(); } } @Override public void processEnded(C context) { submitRow(context); super.processEnded(context); } /** * Invoked by the processor after all beans of a valid record have been processed. * @param row a map containing all object instances generated from an input row. The map is reused internally. Make a copy if you want to keep the map. * @param context A contextual object with information and controls over the current state of the parsing process */ protected abstract void rowProcessed(Map, Object> row, C context); }AbstractObjectColumnProcessor.java000066400000000000000000000111661400120543400371440ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import java.util.*; /** * * A {@link Processor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into columns of objects. *

This uses the value conversions provided by {@link Conversion} instances.

* *

For each row processed, a sequence of conversions will be executed to generate the appropriate object. Each resulting object will then be stored in * a list that contains the values of the corresponding column.

* *

At the end of the process, the user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

* *

Note: Storing the values of all columns may be memory intensive. For large inputs, use a {@link AbstractBatchedObjectColumnProcessor} instead

* * @see AbstractParser * @see Processor * @see ColumnReader * @see Conversion * @see AbstractObjectProcessor * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractObjectColumnProcessor extends AbstractObjectProcessor implements ColumnReader { private final ColumnSplitter splitter; /** * Constructs a column processor, pre-allocating room for 1000 rows. */ public AbstractObjectColumnProcessor() { this(1000); } /** * Constructs a column processor pre-allocating room for the expected number of rows to be processed * @param expectedRowCount the expected number of rows to be processed */ public AbstractObjectColumnProcessor(int expectedRowCount) { splitter = new ColumnSplitter(expectedRowCount); } @Override public final String[] getHeaders() { return splitter.getHeaders(); } @Override public final List> getColumnValuesAsList() { return splitter.getColumnValues(); } @Override public final void putColumnValuesInMapOfNames(Map> map) { splitter.putColumnValuesInMapOfNames(map); } @Override public final void putColumnValuesInMapOfIndexes(Map> map) { splitter.putColumnValuesInMapOfIndexes(map); } @Override public final Map> getColumnValuesAsMapOfNames() { return splitter.getColumnValuesAsMapOfNames(); } @Override public final Map> getColumnValuesAsMapOfIndexes() { return splitter.getColumnValuesAsMapOfIndexes(); } @Override public void rowProcessed(Object[] row, T context) { splitter.addValuesToColumns(row, context); } @Override public void processStarted(T context) { super.processStarted(context); splitter.reset(); } /** * Returns the values of a given column. * @param columnName the name of the column in the input. * @param columnType the type of data in that column * @param the type of data in that column * @return a list with all data stored in the given column */ public List getColumn(String columnName, Class columnType){ return splitter.getColumnValues(columnName, columnType); } /** * Returns the values of a given column. * @param columnIndex the position of the column in the input (0-based). * @param columnType the type of data in that column * @param the type of data in that column * @return a list with all data stored in the given column */ public List getColumn(int columnIndex, Class columnType){ return splitter.getColumnValues(columnIndex, columnType); } @Override public List getColumn(String columnName) { return splitter.getColumnValues(columnName, Object.class); } @Override public List getColumn(int columnIndex) { return splitter.getColumnValues(columnIndex, Object.class); } } AbstractObjectListProcessor.java000077500000000000000000000074131400120543400366250ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * A convenience {@link Processor} implementation for storing all rows parsed and converted to Object arrays into a list. * A typical use case of this class will be: * *
{@code
 *
 * ObjectRowListProcessor processor = new ObjectRowListProcessor();
 * processor.convertIndexes(Conversions.toBigDecimal()).set(4, 6);
 * parserSettings.setRowProcessor(new ObjectRowListProcessor());
 * parser.parse(reader); // will invoke the {@link AbstractObjectListProcessor#rowProcessed(Object[], T)} method for each parsed record.
 *
 * String[] headers = rowProcessor.getHeaders();
 * List<Object[]> rows = rowProcessor.getRows();
 * BigDecimal value1 = (BigDecimal) row.get(4);
 * BigDecimal value2 = (BigDecimal) row.get(6);
 * }

* * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see Processor * @see AbstractProcessor * @see AbstractObjectProcessor */ public abstract class AbstractObjectListProcessor extends AbstractObjectProcessor { private List rows; private String[] headers; private final int expectedRowCount; /** * Creates a new processor of {@code Object[]} rows with varying types. */ public AbstractObjectListProcessor() { this(0); } /** * Creates a new processor of {@code Object[]} rows with varying types. * * @param expectedRowCount expected number of rows to be parsed from the input. * Used to pre-allocate the size of the output {@link List} returned by {@link #getRows()} */ public AbstractObjectListProcessor(int expectedRowCount) { this.expectedRowCount = expectedRowCount <= 0 ? 10000 : expectedRowCount; } @Override public void processStarted(T context) { super.processStarted(context); rows = new ArrayList(expectedRowCount); } /** * Stores the row extracted by the parser and them converted to an Object array into a list. * * @param row the data extracted by the parser for an individual record and converted to an Object array. * @param context A contextual object with information and controls over the current state of the parsing process */ @Override public void rowProcessed(Object[] row, T context) { rows.add(row); } @Override public void processEnded(T context) { super.processEnded(context); this.headers = context.headers(); } /** * Returns the list of parsed and converted records * * @return the list of parsed and converted records */ public List getRows() { return rows == null ? Collections.emptyList() : rows; } /** * Returns the record headers. This can be either the headers defined in {@link CommonSettings#getHeaders()} or the headers parsed in the file when {@link CommonSettings#getHeaders()} equals true * * @return the headers of all records parsed. */ public String[] getHeaders() { return headers; } } AbstractObjectProcessor.java000066400000000000000000000063041400120543400357640ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; /** * A {@link Processor} implementation for converting rows extracted from any implementation of {@link AbstractParser} into arrays of objects. *

This uses the value conversions provided by {@link Conversion} instances. * *

For each row processed, a sequence of conversions will be executed and stored in an object array, at its original position. *

The row with the result of these conversions will then be sent to the {@link AbstractObjectProcessor#rowProcessed(Object[], Context)} method, where the user can access it. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see AbstractParser * @see Processor */ public abstract class AbstractObjectProcessor extends DefaultConversionProcessor implements Processor { /** * Executes the sequences of conversions defined using {@link DefaultConversionProcessor#convertFields(Conversion...)}, * {@link DefaultConversionProcessor#convertIndexes(Conversion...)} and {@link DefaultConversionProcessor#convertAll(Conversion...)}, * for every field in the given row. * *

Each field will be transformed using the {@link Conversion#execute(Object)} method. *

In general the conversions will process a String and convert it to some object value (such as booleans, dates, etc). * * @param row the parsed record with its individual records as extracted from the original input. * @param context the current state of the parsing process. *

Fields that do not have any conversion defined will just be copied to the object array into their original positions. */ @Override public void rowProcessed(String[] row, T context) { Object[] objectRow = applyConversions(row, context); if (objectRow != null) { rowProcessed(objectRow, context); } } /** * Invoked by the processor after all values of a valid record have been processed and converted into an Object array. * * @param row object array created with the information extracted by the parser and then converted. * @param context A contextual object with information and controls over the current state of the parsing process */ public abstract void rowProcessed(Object[] row, T context); @Override public void processStarted(T context) { } @Override public void processEnded(T context) { } } AbstractProcessor.java000077500000000000000000000024701400120543400346400ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; /** * A {@link Processor} implementation that just implements all methods defined by the interface. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public abstract class AbstractProcessor implements Processor { @Override public void processStarted(T context) { } @Override public void rowProcessed(String[] row, T context) { } @Override public void processEnded(T context) { } } AbstractProcessorSwitch.java000066400000000000000000000134221400120543400360160ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import java.util.*; import java.util.Map.*; /** * A special {@link Processor} implementation that combines and allows switching among different * Processors. Each Processor will have its own {@link Context}. Concrete implementations of this class * are expected to implement the {@link #switchRowProcessor(String[], Context)} method and analyze the input row * to determine whether or not the current {@link Processor} implementation must be changed to handle a special * circumstance (determined by the concrete implementation) such as a different row format. * * When the processor is switched, the {@link #processorSwitched(Processor, Processor)} will be called, and * must be overridden, to notify the change to the user. */ public abstract class AbstractProcessorSwitch implements Processor, ColumnOrderDependent { private Map processors; private Processor selectedProcessor; private T contextForProcessor; /** * Analyzes the input to determine whether or not the row processor implementation must be changed * * @param row a row parsed from the input * @param context the current parsing context (not associated with the current row processor used by this class) * * @return the row processor implementation to use. If it is not the same as the one used by the previous row, * the returned row processor will be used, and the {@link #processorSwitched(Processor, Processor)} method * will be called. */ protected abstract Processor switchRowProcessor(String[] row, T context); /** * Returns the headers in use by the current row processor implementation, which can vary among row processors. * If {@code null}, the headers parsed by the input, or defined in {@link CommonParserSettings#getHeaders()} will be returned. * * @return the current sequence of headers to use. */ public String[] getHeaders() { return null; } /** * Returns the indexes in use by the current row processor implementation, which can vary among row processors. * If {@code null} all columns of a given record will be considered. * * @return the current sequence of indexes to use. */ public int[] getIndexes() { return null; } /** * Notifies a change of {@link Processor} implementation. Users are expected to override this method to receive the notification. * * @param from the processor previously in use * @param to the new processor to use to continue processing the input. */ public void processorSwitched(Processor from, Processor to) { if (from != null) { if (from instanceof RowProcessor) { if (to == null || to instanceof RowProcessor) { rowProcessorSwitched((RowProcessor) from, (RowProcessor) to); } } } else if (to != null && to instanceof RowProcessor) { rowProcessorSwitched((RowProcessor) from, (RowProcessor) to); } } /** * Notifies a change of {@link RowProcessor} implementation. Users are expected to override this method to receive the notification. * * @param from the row processor previously in use * @param to the new row processor to use to continue processing the input. */ public void rowProcessorSwitched(RowProcessor from, RowProcessor to) { } @Override public void processStarted(T context) { processors = new HashMap(); selectedProcessor = NoopProcessor.instance; } /** * Wraps a given parser context object that returns headers and extracted field indexes * associated with a given processor in this switch. * @param context the context to wrap * @return a wrapped context that returns the headers and extracted field * indexes from {@link #getHeaders()} and {@link #getIndexes()} */ protected abstract T wrapContext(T context); @Override public final void rowProcessed(String[] row, final T context) { Processor processor = switchRowProcessor(row, context); if (processor == null) { processor = NoopProcessor.instance; } if (processor != selectedProcessor) { contextForProcessor = processors.get(processor); if (processor != NoopProcessor.instance) { if (contextForProcessor == null) { contextForProcessor = wrapContext(context); processor.processStarted(contextForProcessor); processors.put(processor, contextForProcessor); } processorSwitched(selectedProcessor, processor); selectedProcessor = processor; if (getIndexes() != null) { int[] indexes = getIndexes(); String[] tmp = new String[indexes.length]; for (int i = 0; i < indexes.length; i++) { int index = indexes[i]; if (index < row.length) { tmp[i] = row[index]; } } row = tmp; } selectedProcessor.rowProcessed(row, contextForProcessor); } } else { selectedProcessor.rowProcessed(row, contextForProcessor); } } @Override public void processEnded(T context) { processorSwitched(selectedProcessor, null); selectedProcessor = NoopProcessor.instance; for (Entry e : processors.entrySet()) { e.getKey().processEnded(e.getValue()); } } public boolean preventColumnReordering() { return true; } }BatchedColumnReader.java000066400000000000000000000053021400120543400350220ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; /** * A common interface for {@link Processor}s that collect the values parsed from each column in a row and store values of columns in batches. *

Use implementations of this interface implementation in favor of {@link ColumnReader} when processing large inputs to avoid running out of memory.

* *

During the execution of the process, the {@link #batchProcessed(int)} method will be invoked after a given number of rows has been processed.

* *

The user can access the lists with values parsed for all columns using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}.

*

After {@link #batchProcessed(int)} is invoked, all values will be discarded and the next batch of column values will be accumulated. * This process will repeat until there's no more rows in the input. * * @see AbstractBatchedColumnProcessor * @see AbstractBatchedObjectColumnProcessor * @see Processor * * @author Univocity Software Pty Ltd - parsers@univocity.com * * @param the type of the data stored by the columns. */ interface BatchedColumnReader extends ColumnReader { /** * Returns the number of rows processed in each batch * @return the number of rows per batch */ int getRowsPerBatch(); /** * Returns the number of batches already processed * @return the number of batches already processed */ int getBatchesProcessed(); /** * Callback to the user, where the lists with values parsed for all columns can be accessed using the methods {@link #getColumnValuesAsList()}, * {@link #getColumnValuesAsMapOfIndexes()} and {@link #getColumnValuesAsMapOfNames()}. * @param rowsInThisBatch the number of rows processed in the current batch. This corresponds to the number of elements of each list of each column. */ void batchProcessed(int rowsInThisBatch); } BeanConversionProcessor.java000066400000000000000000001001541400120543400360030ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.beans.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; import static com.univocity.parsers.annotations.helpers.AnnotationHelper.*; /** * The base class for {@link Processor} and {@link RowWriterProcessor} implementations that support java beans annotated with the annotations provided in * {@link com.univocity.parsers.annotations}. * * @param the annotated class type. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see Processor * @see RowWriterProcessor */ public class BeanConversionProcessor extends DefaultConversionProcessor { final Class beanClass; final Constructor constructor; protected final Set parsedFields = new LinkedHashSet(); private int lastFieldIndexMapped = -1; private FieldMapping[] readOrder; private FieldMapping[] missing; private Object[] valuesForMissing; protected boolean initialized = false; boolean strictHeaderValidationEnabled = false; private NormalizedString[] syntheticHeaders = null; private Object[] row; private Map> nestedAttributes = null; protected final HeaderTransformer transformer; protected final MethodFilter methodFilter; private ColumnMapping columnMapper = new ColumnMapping(); private boolean mappingsForWritingValidated = false; /** * Initializes the BeanConversionProcessor with the annotated bean class. If any method of the given class has annotations, * only the setter methods will be used (getters will be ignored), making this processor useful mostly for parsing into * instances of the given class. * * @param beanType the class annotated with one or more of the annotations provided in {@link com.univocity.parsers.annotations}. * * @deprecated Use the {@link #BeanConversionProcessor(Class, MethodFilter)} constructor instead. */ @Deprecated public BeanConversionProcessor(Class beanType) { this(beanType, null, MethodFilter.ONLY_SETTERS); } /** * Initializes the BeanConversionProcessor with the annotated bean class * * @param beanType the class annotated with one or more of the annotations provided in {@link com.univocity.parsers.annotations}. * @param methodFilter filter to apply over annotated methods when the processor is reading data from beans (to write values to an output) * or writing values into beans (when parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. */ public BeanConversionProcessor(Class beanType, MethodFilter methodFilter) { this(beanType, null, methodFilter); } BeanConversionProcessor(Class beanType, HeaderTransformer transformer, MethodFilter methodFilter) { this.beanClass = beanType; this.transformer = transformer; this.methodFilter = methodFilter; Constructor c = null; for (Constructor constructor : this.beanClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { c = constructor; break; } } if (c != null) { if (!c.isAccessible()) { c.setAccessible(true); } } this.constructor = (Constructor) c; } /** * Returns a flag indicating whether all headers declared in the annotated class must be present in the input. * If enabled, an exception will be thrown in case the input data does not contain all headers required. * * @return flag indicating whether strict validation of headers is enabled. */ public boolean isStrictHeaderValidationEnabled() { return strictHeaderValidationEnabled; } /** * Identifies and extracts fields annotated with the {@link Parsed} annotation */ public final void initialize() { initialize((NormalizedString[]) null); } /** * Returns a mapper that allows users to manually define mappings from * attributes/methods of a given class to columns to be parsed or written. * * This allows users to use instances of classes that are not annotated with {@link Parsed} nor * {@link Nested}. Any mappings defined with the column mapper will take * precedence over these annotations. * * @return the column mapper */ public final ColumnMapper getColumnMapper() { return columnMapper; } /** * Identifies and extracts fields annotated with the {@link Parsed} annotation * * @param headers headers parsed from the input. */ protected final void initialize(String[] headers) { initialize(NormalizedString.toArray(headers)); } /** * Identifies and extracts fields annotated with the {@link Parsed} annotation * * @param headers headers parsed from the input. */ protected final void initialize(NormalizedString[] headers) { if (!initialized) { initialized = true; Map allFields = AnnotationHelper.getAllFields(beanClass); Set nestedFields = columnMapper.getNestedAttributeNames(); for (String nestedAttributeName : nestedFields) { for (Map.Entry e : allFields.entrySet()) { Field field = e.getKey(); if (field.getName().equals(nestedAttributeName)) { Nested nested = AnnotationHelper.findAnnotation(field, Nested.class); if (nested == null) { processNestedField(field.getType(), field, field.getName(), e.getValue(), headers, null); } } } } for (Map.Entry e : allFields.entrySet()) { Field field = e.getKey(); PropertyWrapper property = e.getValue(); processField(field, field.getName(), property, headers); } for (Method method : AnnotationHelper.getAllMethods(beanClass, methodFilter)) { processField(method, method.getName(), null, headers); } readOrder = null; lastFieldIndexMapped = -1; identifyLiterals(); validateMappings(); } } private void identifyLiterals() { NormalizedString[] fieldNames = new NormalizedString[parsedFields.size()]; FieldMapping[] fields = parsedFields.toArray(new FieldMapping[0]); for (int i = 0; i < fieldNames.length; i++) { fieldNames[i] = fields[i].getFieldName(); } if (NormalizedString.identifyLiterals(fieldNames)) { for (int i = 0; i < fieldNames.length; i++) { fields[i].setFieldName(fieldNames[i]); } } } /** * Defines whether all headers declared in the annotated class must be present in the input. * If enabled, an exception will be thrown in case the input data does not contain all headers required. * * @param strictHeaderValidationEnabled flag indicating whether strict validation of headers is enabled. */ public void setStrictHeaderValidationEnabled(boolean strictHeaderValidationEnabled) { this.strictHeaderValidationEnabled = strictHeaderValidationEnabled; } void processField(AnnotatedElement element, String targetName, PropertyWrapper propertyDescriptor, NormalizedString[] headers) { FieldMapping mapping = null; Parsed annotation = AnnotationHelper.findAnnotation(element, Parsed.class); if (annotation != null) { mapping = new FieldMapping(beanClass, element, propertyDescriptor, transformer, headers); if (processField(mapping)) { parsedFields.add(mapping); setupConversions(element, mapping); } } MethodDescriptor descriptor = null; if (element instanceof Method) { descriptor = methodFilter.toDescriptor(columnMapper.getPrefix(), (Method) element); } if (columnMapper.isMapped(descriptor, targetName)) { if (mapping == null) { mapping = new FieldMapping(beanClass, element, propertyDescriptor, transformer, headers); columnMapper.updateMapping(mapping, targetName, descriptor); parsedFields.add(mapping); setupConversions(element, mapping); } else { columnMapper.updateMapping(mapping, targetName, descriptor); } } Nested nested = AnnotationHelper.findAnnotation(element, Nested.class); if (nested != null) { Class nestedType = AnnotationRegistry.getValue(element, nested, "type", nested.type()); if (nestedType == Object.class) { nestedType = getType(element); } processNestedField(nestedType, element, targetName, propertyDescriptor, headers, nested); } } private void processNestedField(Class nestedType, AnnotatedElement element, String targetName, PropertyWrapper propertyDescriptor, NormalizedString[] headers, Nested nested) { HeaderTransformer transformer = null; if (nested != null) { Class transformerType = AnnotationRegistry.getValue(element, nested, "headerTransformer", nested.headerTransformer()); if (transformerType != HeaderTransformer.class) { String[] args = AnnotationRegistry.getValue(element, nested, "args", nested.args()); transformer = AnnotationHelper.newInstance(HeaderTransformer.class, transformerType, args); } } FieldMapping mapping = new FieldMapping(nestedType, element, propertyDescriptor, null, headers); BeanConversionProcessor processor = createNestedProcessor(nested, nestedType, mapping, transformer); processor.conversions = this.conversions == null ? null : cloneConversions(); processor.columnMapper = new ColumnMapping(targetName, this.columnMapper); processor.initialize(headers); getNestedAttributes().put(mapping, processor); } /** * Creates a copy of the manually defined conversions to be applied over any columns. * * @return a copy of the currently defined conversions */ protected FieldConversionMapping cloneConversions() { return this.conversions.clone(); } Map> getNestedAttributes() { if (nestedAttributes == null) { nestedAttributes = new LinkedHashMap>(); } return nestedAttributes; } BeanConversionProcessor createNestedProcessor(Annotation annotation, Class nestedType, FieldMapping fieldMapping, HeaderTransformer transformer) { return new BeanConversionProcessor(nestedType, transformer, methodFilter); } /** * Determines whether or not an annotated field should be processed. * Can be overridden by subclasses for fine grained control. * * @param field the field to be processed * * @return {@code true} if the given field should be processed, otherwise {@code false}. */ protected boolean processField(FieldMapping field) { return true; } void validateMappings() { Map mappedNames = new HashMap(); Map mappedIndexes = new HashMap(); Set duplicateNames = new HashSet(); Set duplicateIndexes = new HashSet(); for (FieldMapping mapping : parsedFields) { NormalizedString name = mapping.getFieldName(); int index = mapping.getIndex(); if (index != -1) { if (mappedIndexes.containsKey(index)) { duplicateIndexes.add(mapping); duplicateIndexes.add(mappedIndexes.get(index)); } else { mappedIndexes.put(index, mapping); } } else { if (mappedNames.containsKey(name)) { duplicateNames.add(mapping); duplicateNames.add(mappedNames.get(name)); } else { mappedNames.put(name, mapping); } } } if (duplicateIndexes.size() > 0 || duplicateNames.size() > 0) { StringBuilder msg = new StringBuilder("Conflicting field mappings defined in annotated class: " + this.getBeanClass().getName()); for (FieldMapping mapping : duplicateIndexes) { msg.append("\n\tIndex: '").append(mapping.getIndex()).append("' of ").append(describeField(mapping.getTarget())); } for (FieldMapping mapping : duplicateNames) { msg.append("\n\tName: '").append(mapping.getFieldName()).append("' of ").append(describeField(mapping.getTarget())); } throw new DataProcessingException(msg.toString()); } } static String describeField(AnnotatedElement target) { if (target instanceof Method) { return "method: " + target; } return "field '" + AnnotationHelper.getName(target) + "' (" + getType(target).getName() + ')'; } /** * Goes through each field and method annotated with {@link Parsed} and extracts the sequence of {@link Conversion} elements associated with each one. * * @param target the field and method annotated with {@link Parsed} that must be associated with one or more {@link Conversion} objects * @param mapping a helper class to store information how the field or method is mapped to a parsed record. */ @SuppressWarnings("rawtypes") private void setupConversions(AnnotatedElement target, FieldMapping mapping) { List annotations = AnnotationHelper.findAllAnnotationsInPackage(target, Parsed.class.getPackage()); Conversion lastConversion = null; if (!annotations.isEmpty()) { Class targetType = getType(target); Parsed parsed = target == null ? null : findAnnotation(target, Parsed.class); String nullRead = getNullReadValue(target, parsed); String nullWrite = getNullWriteValue(target, parsed); for (Annotation annotation : annotations) { try { Conversion conversion = AnnotationHelper.getConversion(targetType, target, annotation, nullRead, nullWrite); if (conversion != null) { addConversion(conversion, mapping); lastConversion = conversion; } } catch (Throwable ex) { String path = annotation.annotationType().getSimpleName() + "' of field " + mapping; throw new DataProcessingException("Error processing annotation '" + path + ". " + ex.getMessage(), ex); } } if (targetType.isEnum()) { boolean hasEnumOptions = false; for (Annotation annotation : annotations) { if (annotation.annotationType() == EnumOptions.class) { hasEnumOptions = true; } } if (!hasEnumOptions) { Conversion conversion = createDefaultEnumConversion(targetType, nullRead, nullWrite); addConversion(conversion, mapping); lastConversion = conversion; } } } Parsed parsed = AnnotationHelper.findAnnotation(target, Parsed.class); boolean applyDefaultConversion = parsed == null || AnnotationRegistry.getValue(target, parsed, "applyDefaultConversion", parsed.applyDefaultConversion()); if (applyDefaultConversion) { Conversion defaultConversion = AnnotationHelper.getDefaultConversion(target); if (applyDefaultConversion(lastConversion, defaultConversion)) { addConversion(defaultConversion, mapping); } } } @SuppressWarnings("rawtypes") private boolean applyDefaultConversion(Conversion lastConversionApplied, Conversion defaultConversion) { if (defaultConversion == null) { return false; } if (lastConversionApplied == null) { return true; } if (lastConversionApplied.getClass() == defaultConversion.getClass()) { // no need to add the default conversion as it was manually specified by the user with his settings return false; } Method execute = getConversionMethod(lastConversionApplied, "execute"); Method revert = getConversionMethod(lastConversionApplied, "revert"); Method defaultExecute = getConversionMethod(defaultConversion, "execute"); Method defaultRevert = getConversionMethod(defaultConversion, "revert"); return !(execute.getReturnType() == defaultExecute.getReturnType() && revert.getReturnType() == defaultRevert.getReturnType()); } @SuppressWarnings("rawtypes") private Method getConversionMethod(Conversion conversion, String methodName) { Method targetMethod = null; for (Method method : conversion.getClass().getMethods()) { if (method.getName().equals(methodName) && !method.isSynthetic() && !method.isBridge() && ((method.getModifiers() & Modifier.PUBLIC) == 1) && method.getParameterTypes().length == 1 && method.getReturnType() != void.class) { if (targetMethod != null) { throw new DataProcessingException("Unable to convert values for class '" + beanClass + "'. Multiple '" + methodName + "' methods defined in conversion " + conversion.getClass() + '.'); } targetMethod = method; } } if (targetMethod != null) { return targetMethod; } //should never happen throw new DataProcessingException("Unable to convert values for class '" + beanClass + "'. Cannot find method '" + methodName + "' in conversion " + conversion.getClass() + '.'); } /** * Associates a conversion to a field of the java bean class. * * @param conversion The conversion object that must be executed against the given field * @param mapping the helper object that contains information about how a field is mapped. */ @SuppressWarnings("rawtypes") protected void addConversion(Conversion conversion, FieldMapping mapping) { if (conversion == null) { return; } if (mapping.isMappedToIndex()) { this.convertIndexes(conversion).add(mapping.getIndex()); } else { this.convertFields(conversion).add(NormalizedString.valueOf(mapping.getFieldName())); } } /** * Goes through a list of objects and associates each value to a particular field of a java bean instance * * @param instance the java bean instance that is going to have its properties set * @param row the values to associate with each field of the javabean. * @param context information about the current parsing process. */ void mapValuesToFields(T instance, Object[] row, Context context) { if (row.length > lastFieldIndexMapped) { this.lastFieldIndexMapped = row.length; mapFieldIndexes(context, row, NormalizedString.toIdentifierGroupArray(context.headers()), context.extractedFieldIndexes(), context.columnsReordered()); } int last = row.length < readOrder.length ? row.length : readOrder.length; int i = 0; for (; i < last; i++) { FieldMapping field = readOrder[i]; if (field != null) { Object value = row[i]; field.write(instance, value); } } if (conversions != null && row.length < readOrder.length) { i = last; for (; i < readOrder.length; i++) { FieldMapping field = readOrder[i]; if (field != null) { Object value = conversions.applyConversions(i, null, null); field.write(instance, value); } } } if (missing != null) { for (i = 0; i < missing.length; i++) { Object value = valuesForMissing[i]; if (value != null) { FieldMapping field = missing[i]; field.write(instance, value); } } } } /** * Identifies which fields are associated with which columns in a row. * * @param row A row with values for the given java bean. * @param headers The names of all fields of the record (including any header that is not mapped to the java bean). May be null if no headers have * been defined in {@link CommonSettings#getHeaders()} * @param indexes The indexes of the headers or row that are actually being used. May be null if no fields have been selected using * {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @param columnsReordered Indicates the indexes provided were reordered and do not match the original sequence of headers. */ private void mapFieldIndexes(Context context, Object[] row, NormalizedString[] headers, int[] indexes, boolean columnsReordered) { if (headers == null) { headers = ArgumentUtils.EMPTY_NORMALIZED_STRING_ARRAY; } boolean boundToIndex = false; int last = headers.length > row.length ? headers.length : row.length; for (FieldMapping mapping : parsedFields) { int index = mapping.getIndex(); if (last <= index) { last = index; boundToIndex = true; } } if (boundToIndex) { last++; } FieldMapping[] fieldOrder = new FieldMapping[last]; TreeSet fieldsNotFound = new TreeSet(); for (FieldMapping mapping : parsedFields) { if (mapping.isMappedToField()) { int[] positions = ArgumentUtils.indexesOf(headers, mapping.getFieldName()); if (positions.length == 0) { fieldsNotFound.add(mapping.getFieldName()); continue; } for (int i = 0; i < positions.length; i++) { fieldOrder[positions[i]] = mapping; } } else if (mapping.getIndex() < fieldOrder.length) { fieldOrder[mapping.getIndex()] = mapping; } } if (context != null && !fieldsNotFound.isEmpty()) { //Trigger this validation only when reading, not writing. if (headers.length == 0) { throw new DataProcessingException("Could not find fields " + fieldsNotFound.toString() + " in input. Please enable header extraction in the parser settings in order to match field names."); } if (strictHeaderValidationEnabled) { DataProcessingException exception = new DataProcessingException("Could not find fields " + fieldsNotFound.toString() + "' in input. Names found: {headers}"); exception.setValue("headers", Arrays.toString(headers)); throw exception; } } if (indexes != null) { // sets fields not read from CSV to null. for (int i = 0; i < fieldOrder.length; i++) { boolean isIndexUsed = false; for (int j = 0; j < indexes.length; j++) { if (indexes[j] == i) { isIndexUsed = true; break; } } if (!isIndexUsed) { fieldOrder[i] = null; } } // reorders the fields so they are positioned in the same order as in the incoming row[] if (columnsReordered) { FieldMapping[] newFieldOrder = new FieldMapping[indexes.length]; for (int i = 0; i < indexes.length; i++) { for (int j = 0; j < fieldOrder.length; j++) { int index = indexes[i]; if (index != -1) { FieldMapping field = fieldOrder[index]; newFieldOrder[i] = field; } } } fieldOrder = newFieldOrder; } } readOrder = fieldOrder; initializeValuesForMissing(); } private int nonNullReadOrderLength(){ int count = 0; for(int i = 0; i < readOrder.length; i++){ if(readOrder[i] != null){ count++; } } return count; } private void initializeValuesForMissing() { if (nonNullReadOrderLength() < parsedFields.size()) { Set unmapped = new LinkedHashSet(parsedFields); unmapped.removeAll(Arrays.asList(readOrder)); missing = unmapped.toArray(new FieldMapping[0]); String[] headers = new String[missing.length]; BeanConversionProcessor tmp = new BeanConversionProcessor(getBeanClass(), methodFilter) { protected void addConversion(Conversion conversion, FieldMapping mapping) { if (conversion == null) { return; } convertFields(conversion).add(NormalizedString.valueOf(mapping.getFieldName())); } }; for (int i = 0; i < missing.length; i++) { FieldMapping mapping = missing[i]; if (processField(mapping)) { tmp.setupConversions(mapping.getTarget(), mapping); } headers[i] = NormalizedString.valueOf(mapping.getFieldName()); } tmp.initializeConversions(headers, null); valuesForMissing = tmp.applyConversions(new String[missing.length], null); } else { missing = null; valuesForMissing = null; } } /** * Converts a record with values extracted from the parser into a java bean instance. * * @param row The values extracted from the parser * @param context The current state of the parsing process * * @return an instance of the java bean type defined in this class constructor. */ public T createBean(String[] row, Context context) { Object[] convertedRow = super.applyConversions(row, context); if (convertedRow == null) { return null; } T instance; try { instance = constructor.newInstance(); } catch (Throwable e) { throw new DataProcessingException("Unable to instantiate class '" + beanClass.getName() + '\'', row, e); } mapValuesToFields(instance, convertedRow, context); if (nestedAttributes != null) { processNestedAttributes(row, instance, context); } return instance; } void processNestedAttributes(String[] row, Object instance, Context context) { for (Map.Entry> e : nestedAttributes.entrySet()) { Object nested = e.getValue().createBean(row, context); if (nested != null) { e.getKey().write(instance, nested); } } } /** * Iterates over all fields in the java bean instance and extracts its values. * * @param instance the java bean instance to be read * @param row object array that will receive the values extracted from java bean * @param headers The names of all fields of the record (including any header that is not mapped to the java bean). May be null if no headers have * been defined in {@link CommonSettings#getHeaders()} * @param indexes The indexes of the headers or row that are actually being used. May be null if no fields have been selected using * {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @param columnsReordered Indicates the indexes provided were reordered and do not match the original sequence of headers. */ private void mapFieldsToValues(T instance, Object[] row, NormalizedString[] headers, int[] indexes, boolean columnsReordered) { if (row.length > this.lastFieldIndexMapped) { mapFieldIndexes(null, row, headers, indexes, columnsReordered); } int last = row.length < readOrder.length ? row.length : readOrder.length; for (int i = 0; i < last; i++) { FieldMapping field = readOrder[i]; if (field != null) { try { row[i] = field.read(instance); } catch (Throwable e) { if (!beanClass.isAssignableFrom(instance.getClass())) { handleConversionError(e, new Object[]{instance}, -1); throw toDataProcessingException(e, row, i); } else if (!handleConversionError(e, row, i)) { throw toDataProcessingException(e, row, i); }//else proceed } } } } /** * Converts a java bean instance into a sequence of values for writing. * * @param bean an instance of the type defined in this class constructor. * @param headers All field names used to produce records in a given destination. May be null if no headers have been defined in * {@link CommonSettings#getHeaders()} * @param indexesToWrite The indexes of the headers that are actually being written. May be null if no fields have been selected using * {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * * @return a row of objects containing the values extracted from the java bean */ public final Object[] reverseConversions(T bean, NormalizedString[] headers, int[] indexesToWrite) { if (!mappingsForWritingValidated) { mappingsForWritingValidated = true; validateMappingsForWriting(); } if (bean == null) { return null; } if (row == null) { if (headers != null) { row = new Object[headers.length]; } else if (indexesToWrite != null) { int minimumRowLength = 0; for (int index : indexesToWrite) { if (index + 1 > minimumRowLength) { minimumRowLength = index + 1; } } if (minimumRowLength < indexesToWrite.length) { minimumRowLength = indexesToWrite.length; } row = new Object[minimumRowLength]; } else { Set assignedIndexes = new HashSet(); int lastIndex = -1; for (FieldMapping f : parsedFields) { if (lastIndex < f.getIndex() + 1) { lastIndex = f.getIndex() + 1; } assignedIndexes.add(f.getIndex()); } if (lastIndex < parsedFields.size()) { lastIndex = parsedFields.size(); } row = new Object[lastIndex]; if (syntheticHeaders == null) { syntheticHeaders = new NormalizedString[lastIndex]; Iterator it = parsedFields.iterator(); for (int i = 0; i < lastIndex; i++) { if (assignedIndexes.contains(i)) { continue; } NormalizedString fieldName = null; while (it.hasNext() && (fieldName = it.next().getFieldName()) == null) ; syntheticHeaders[i] = fieldName; } } } } if (nestedAttributes != null) { for (Map.Entry> e : nestedAttributes.entrySet()) { Object nested = e.getKey().read(bean); if (nested != null) { BeanConversionProcessor nestedProcessor = (BeanConversionProcessor) e.getValue(); nestedProcessor.row = row; nestedProcessor.reverseConversions(nested, headers, indexesToWrite); } } } NormalizedString[] normalizedHeaders = NormalizedString.toIdentifierGroupArray(headers); if (syntheticHeaders != null) { normalizedHeaders = syntheticHeaders; } try { mapFieldsToValues(bean, row, normalizedHeaders, indexesToWrite, false); } catch (Throwable ex) { if (ex instanceof DataProcessingException) { DataProcessingException error = (DataProcessingException) ex; if (error.isHandled()) { return null; } else { throw error; } } else if (!handleConversionError(ex, row, -1)) { throw toDataProcessingException(ex, row, -1); } return null; } if (super.reverseConversions(true, row, normalizedHeaders, indexesToWrite)) { return row; } return null; } /** * Returns the class of the annotated java bean instances that will be manipulated by this processor. * * @return the class of the annotated java bean instances that will be manipulated by this processor. */ public Class getBeanClass() { return beanClass; } /** * Copies the given column mappings over to this processor. Further changes * to the given object won't be reflected on the copy stored internally. * * @param columnMapper the column mappings to use */ public void setColumnMapper(ColumnMapper columnMapper) { this.columnMapper = columnMapper == null ? new ColumnMapping() : (ColumnMapping) columnMapper.clone(); } private void validateMappingsForWriting() { Map targetCounts = new TreeMap(); Map targetSources = new HashMap(); populateTargetMaps(targetCounts, targetSources); StringBuilder msg = new StringBuilder(); for (Map.Entry e : targetCounts.entrySet()) { if (e.getValue() > 1) { String sources = targetSources.get(e.getKey()); if (msg.length() > 0) { msg.append("\n"); } msg.append('\t'); msg.append(e.getKey()); msg.append(": "); msg.append(sources); } } if (msg.length() > 0) { throw new DataProcessingException("Cannot write object as multiple attributes/methods have been mapped to the same output column:\n" + msg.toString()); } } private void populateTargetMaps(Map targetCounts, Map targetSources) { for (FieldMapping field : parsedFields) { Object outputColumn = field.getIndex() == -1 ? field.getFieldName() : NormalizedString.valueOf("Column #" + field.getIndex()); Integer count = targetCounts.get(outputColumn); if (count == null) { count = 0; } count++; targetCounts.put(outputColumn, count); String str = targetSources.get(outputColumn); String sourceName; if (field.getTarget() instanceof Method) { sourceName = ((Method) field.getTarget()).getName(); } else { sourceName = ((Field) field.getTarget()).getName(); } if (!columnMapper.getPrefix().isEmpty()) { sourceName = columnMapper.getPrefix() + '.' + sourceName; } if (str == null) { str = sourceName; } else { str += ", " + sourceName; } targetSources.put(outputColumn, str); } if (nestedAttributes != null) { for (BeanConversionProcessor nestedProcessor : nestedAttributes.values()) { nestedProcessor.populateTargetMaps(targetCounts, targetSources); } } } } ColumnOrderDependent.java000066400000000000000000000030231400120543400352450ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.processor.*; /** * A marker interface used by special implementations of {@link RowProcessor} to indicate columns should not * be reordered by the parser. Conflicting settings provided in {@link com.univocity.parsers.common.CommonParserSettings#setColumnReorderingEnabled(boolean)} will be prevented. *

This marker is used to configure the parser automatically based on the specific {@link RowProcessor} implementation used. */ public interface ColumnOrderDependent { /** * Returns a flag indicating whether or not columns should be reordered by the parser * * @return a flag indicating whether or not columns should be reordered by the parser */ boolean preventColumnReordering(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/ColumnReader.java000066400000000000000000000070171400120543400336330ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * A common interface for {@link Processor}s that collect the values parsed from each column in a row. * Namely: {@link AbstractColumnProcessor}, {@link AbstractObjectColumnProcessor}, {@link AbstractBatchedColumnProcessor} and {@link AbstractBatchedObjectColumnProcessor}. * * @see AbstractColumnProcessor * @see AbstractObjectColumnProcessor * @see AbstractBatchedColumnProcessor * @see AbstractBatchedObjectColumnProcessor * @see Processor * * @author Univocity Software Pty Ltd - parsers@univocity.com * * @param the type of the data stored by the columns. */ interface ColumnReader { /** * Returns the column headers. This can be either the headers defined in {@link CommonSettings#getHeaders()} or the headers parsed in * the input when {@link CommonSettings#getHeaders()} equals to {@code true} * @return the headers of all column parsed. */ String[] getHeaders(); /** * Returns the values processed for each column * @return a list of lists. The stored lists correspond to the position of the column processed from the input; Each list * contains the corresponding values parsed for a column, across multiple rows. */ List> getColumnValuesAsList(); /** * Fills a given map associating each column name to its list o values * @param map the map to hold the values of each column * @throws IllegalArgumentException if a column does not have a name associated to it. In this case, use {@link #putColumnValuesInMapOfIndexes(Map)} instead. */ void putColumnValuesInMapOfNames(Map> map); /** * Fills a given map associating each column index to its list of values * @param map the map to hold the values of each column */ void putColumnValuesInMapOfIndexes(Map> map); /** * Returns a map of column names and their respective list of values parsed from the input. * @return a map of column names and their respective list of values. */ Map> getColumnValuesAsMapOfNames(); /** * Returns a map of column indexes and their respective list of values parsed from the input. * @return a map of column indexes and their respective list of values. */ Map> getColumnValuesAsMapOfIndexes(); /** * Returns the values of a given column. * @param columnName the name of the column in the input. * @return a list with all data stored in the given column */ List getColumn(String columnName); /** * Returns the values of a given column. * @param columnIndex the position of the column in the input (0-based). * @return a list with all data stored in the given column */ List getColumn(int columnIndex); } ColumnSplitter.java000066400000000000000000000216371400120543400341640ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import java.util.*; /** * A utility class used split and store values columns parsed from each row in a {@link Processor}. Used to centralize common code used by implementations * of {@link ColumnReader}, namely: * {@link AbstractColumnProcessor}, {@link AbstractObjectColumnProcessor}, {@link AbstractBatchedColumnProcessor} and {@link AbstractBatchedObjectColumnProcessor}. * * @see ColumnReader * @see AbstractColumnProcessor * @see AbstractObjectColumnProcessor * @see AbstractBatchedColumnProcessor * @see AbstractBatchedObjectColumnProcessor * @see Processor * * @author Univocity Software Pty Ltd - parsers@univocity.com * * @param the type of values stored in the columns. */ class ColumnSplitter { private List> columnValues; private String[] headers = null; private int expectedRowCount = 1000; private long rowCount; private long addNullsFrom; /** * Creates a splitter allocating a space for a give number of expected rows to be read * @param expectedRowCount the expected number of rows to be parsed */ ColumnSplitter(int expectedRowCount) { if (expectedRowCount <= 0) { throw new IllegalArgumentException("Expected row count must be positive"); } this.expectedRowCount = expectedRowCount; } /** * Removes any column values previously processed */ void clearValues() { addNullsFrom = rowCount; this.columnValues = null; } /** * Prepares to execute a column splitting process from the beginning. * Removes any column values previously processed, as well as information about headers in the input. Resets row count to 0. */ void reset() { this.columnValues = null; this.headers = null; addNullsFrom = 0L; rowCount = 0L; } /** * Returns the values processed for each column * @return a list of lists. The stored lists correspond to the position of the column processed from the input; Each list * contains the corresponding values parsed for a column, across multiple rows. */ List> getColumnValues() { return columnValues; } /** * Returns the headers of the input. This can be either the headers defined in {@link CommonSettings#getHeaders()} * or the headers parsed in the input when {@link CommonSettings#getHeaders()} equals to {@code true} * @return the headers of all records parsed. */ String[] getHeaders() { return headers; } /** * Initializes the list of column values, the headers of each column and which columns to read if fields * have been selected using {@link CommonSettings#selectFields(String...)} or {@link CommonSettings#selectIndexes(Integer...)} * @param context the current active parsing context, which will be used to obtain information about headers and selected fields. */ private void initialize(Context context) { headers: if (this.headers == null) { String[] allHeaders = context.headers(); if (allHeaders == null) { headers = ArgumentUtils.EMPTY_STRING_ARRAY; break headers; } if (!context.columnsReordered()) { this.headers = allHeaders; break headers; } int[] selectedIndexes = context.extractedFieldIndexes(); final int last = Math.min(allHeaders.length, selectedIndexes.length); this.headers = new String[selectedIndexes.length]; for (int i = 0; i < last; i++) { int idx = selectedIndexes[i]; if(idx < allHeaders.length) { headers[i] = allHeaders[selectedIndexes[i]]; } } } columnValues = new ArrayList>(headers.length > 0 ? headers.length : 10); } /** * Returns the header of a particular column * @param columnIndex the index of the column whose header is to be obtained * @return the name of the column at the given index, or null if there's no header defined for the given column index. */ String getHeader(int columnIndex) { if (columnIndex < headers.length) { return headers[columnIndex]; } return null; } /** * Splits the row and add stores the value of each column in its corresponding list in {@link #columnValues} * @param row the row whose column values will be split * @param context the current active parsing context. */ void addValuesToColumns(T[] row, Context context) { if (columnValues == null) { initialize(context); } if (columnValues.size() < row.length) { int columnsToAdd = row.length - columnValues.size(); while (columnsToAdd-- > 0) { long records = context.currentRecord() - addNullsFrom; ArrayList values = new ArrayList(expectedRowCount < records ? (int) records : expectedRowCount); //adding nulls to the values of a new row with more columns than parsed before. //this ensures all columns will have the same number of values. while (--records > 0) { values.add(null); } columnValues.add(values); } } for (int i = 0; i < row.length; i++) { columnValues.get(i).add(row[i]); } //if we have more columns than what was parsed in the current row, we need to add nulls to the remaining columns. if (row.length < columnValues.size()) { for (int i = row.length; i < columnValues.size(); i++) { columnValues.get(i).add(null); } } rowCount++; } /** * Fills a given map associating each column name to its list o values * @param map the map to hold the values of each column * @throws IllegalArgumentException if a column does not have a name associated to it. In this case, use {@link #putColumnValuesInMapOfIndexes(Map)} instead. */ void putColumnValuesInMapOfNames(Map> map) { if (columnValues == null) { return; } for (int i = 0; i < columnValues.size(); i++) { String header = getHeader(i); if (header == null) { throw new DataProcessingException("Parsed input does not have header for column at index '" + i + "'. Parsed header names: " + Arrays.toString(getHeaders()), i); } map.put(header, columnValues.get(i)); } } /** * Returns the values of a given column. * @param columnIndex the position of the column in the input (0-based). * @param columnType the type of data in that column * @param the type of data in that column * @return a list with all data stored in the given column */ List getColumnValues(int columnIndex, Class columnType){ if(columnIndex < 0){ throw new IllegalArgumentException("Column index must be positive"); } if(columnIndex >= columnValues.size()){ throw new IllegalArgumentException("Column index must be less than " + columnValues.size() +". Got " + columnIndex); } return (List) columnValues.get(columnIndex); } /** * Returns the values of a given column. * @param columnName the name of the column in the input. * @param columnType the type of data in that column * @param the type of data in that column * @return a list with all data stored in the given column */ List getColumnValues(String columnName, Class columnType){ int index = ArgumentUtils.indexOf(headers, columnName); if(index == -1){ throw new IllegalArgumentException("No column named '" + columnName +"' has been found. Available column headers: " + Arrays.toString(headers)); } return getColumnValues(index, columnType); } /** * Fills a given map associating each column index to its list of values * @param map the map to hold the values of each column */ void putColumnValuesInMapOfIndexes(Map> map) { if (columnValues == null) { return; } for (int i = 0; i < columnValues.size(); i++) { map.put(i, columnValues.get(i)); } } /** * Returns a map of column names and their respective list of values parsed from the input. * @return a map of column names and their respective list of values. */ Map> getColumnValuesAsMapOfNames() { Map> map = new HashMap>(); putColumnValuesInMapOfNames(map); return map; } /** * Returns a map of column indexes and their respective list of values parsed from the input. * @return a map of column indexes and their respective list of values. */ Map> getColumnValuesAsMapOfIndexes() { Map> map = new HashMap>(); putColumnValuesInMapOfIndexes(map); return map; } } CompositeProcessor.java000077500000000000000000000070151400120543400350370ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/******************************************************************************* * Copyright 2017 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; /** * A utility {@link Processor} implementation that facilitates using multiple implementations of {@link Processor} at the * same time. * * @param the tye of the contextual object with information and controls over the current state of the parsing process */ public class CompositeProcessor implements Processor { private final Processor processors[]; /** * Creates a new {@code CompositeProcessor} with the list of {@link Processor} implementations to be used. * * @param processors the sequence of {@link Processor} implementations to be used. */ public CompositeProcessor(Processor... processors) { this.processors = processors; } /** * Initializes each {@link Processor} used by this class. This is invoked by the parser once, when it is ready to start processing the input. * * @param context A contextual object with information and controls over the current state of the parsing process */ @Override public void processStarted(C context) { for (int i = 0; i < processors.length; i++) { processors[i].processStarted(context); } } /** * Invoked by the parser after all values of a valid record have been processed. All {@link Processor} implementations * will have their corresponding {@link Processor#rowProcessed(String[], Context)} method called with the given row. * * @param row the data extracted by the parser for an individual record. Note that: *

    *
  • it will never by null.
  • *
  • it will never be empty unless explicitly configured using {@link CommonSettings#setSkipEmptyLines(boolean)}
  • *
  • it won't contain lines identified by the parser as comments. To disable comment processing set {@link Format#setComment(char)} to '\0'
  • *
* @param context A contextual object with information and controls over the current state of the parsing process */ @Override public void rowProcessed(String[] row, C context) { for (int i = 0; i < processors.length; i++) { processors[i].rowProcessed(row, context); } } /** * This method will by invoked by the parser once for each {@link Processor} used by this class, after the parsing process stopped and all resources were closed. *

It will always be called by the parser: in case of errors, if the end of the input us reached, or if the user stopped the process manually using {@link ParsingContext#stop()}. * * @param context A contextual object with information and controls over the state of the parsing process */ @Override public void processEnded(C context) { for (int i = 0; i < processors.length; i++) { processors[i].processEnded(context); } } }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/NoopProcessor.java000066400000000000000000000021341400120543400340610ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; /** * A singleton instance of a {@link Processor} that does nothing. */ public final class NoopProcessor extends AbstractProcessor { /** * The singleton instance of the no-op {@link Processor} */ public static final Processor instance = new NoopProcessor(); private NoopProcessor() { } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/processor/core/Processor.java000077500000000000000000000112261400120543400332320ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor.core; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; /** * The essential callback interface to handle records parsed by any parser that extends {@link AbstractParser}. * *

When parsing an input, univocity-parsers will obtain the RowProcessor from {@link CommonParserSettings#getRowProcessor()}, and * delegate each parsed row to {@link Processor#rowProcessed(String[], Context)}. * *

Before parsing the first row, the parser will invoke the {@link Processor#processStarted(Context)} method. * By this time the input buffer will be already loaded and ready to be consumed. * *

After parsing the last row, all resources are closed and the processing stops. Only after the {@link Processor#processEnded(Context)} is called so you * can perform any additional housekeeping you might need. * *

More control and information over the parsing process are provided by the {@link Context} object. * *

univocity-parsers provides many useful default implementations of this interface in the package {@link com.univocity.parsers.common.processor}, namely: * *

    *
  • {@link RowListProcessor}: convenience class for storing the processed rows into a list.
  • *
  • {@link ObjectRowProcessor}: used for processing rows and executing conversions of parsed values to objects using instances of {@link Conversion}
  • *
  • {@link ObjectRowListProcessor}: convenience class for rows of converted objects using {@link ObjectRowProcessor} into a list.
  • *
  • {@link AbstractMasterDetailProcessor}: used for reading inputs where records are organized in a master-detail fashion (with a master element that contains a list of associated elements)
  • *
  • {@link AbstractMasterDetailListProcessor}: convenience class for storing {@link MasterDetailRecord} created by instances created by {@link AbstractMasterDetailProcessor} into a list
  • *
  • {@link AbstractBeanProcessor}: used for automatically create and populate javabeans annotated with the annotations provided in package {@link com.univocity.parsers.annotations}
  • *
  • {@link AbstractBeanListProcessor}: convenience class for storing all javabeans created by {@link AbstractBeanProcessor} into a list
  • *
* * @see AbstractParser * @see CommonParserSettings * @see ParsingContext * @see Context * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public interface Processor { /** * This method will by invoked by the parser once, when it is ready to start processing the input. * * @param context A contextual object with information and controls over the current state of the parsing process */ void processStarted(T context); /** * Invoked by the parser after all values of a valid record have been processed. * * @param row the data extracted by the parser for an individual record. Note that: *
    *
  • it will never by null.
  • *
  • it will never be empty unless explicitly configured using {@link CommonSettings#setSkipEmptyLines(boolean)}
  • *
  • it won't contain lines identified by the parser as comments. To disable comment processing set {@link Format#setComment(char)} to '\0'
  • *
* @param context A contextual object with information and controls over the current state of the parsing process */ void rowProcessed(String[] row, T context); /** * This method will by invoked by the parser once, after the parsing process stopped and all resources were closed. *

It will always be called by the parser: in case of errors, if the end of the input us reached, or if the user stopped the process manually using {@link ParsingContext#stop()}. * * @param context A contextual object with information and controls over the state of the parsing process */ void processEnded(T context); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/000077500000000000000000000000001400120543400267125ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/AbstractRecordFactory.java000066400000000000000000000022351400120543400340110ustar00rootroot00000000000000package com.univocity.parsers.common.record; import com.univocity.parsers.common.*; /** * An abstract factory class which allows subclasses to provide implementations of {@link Record} * * @param the specific type of {@link Record} that is provided */ public abstract class AbstractRecordFactory { protected final M metaData; /** * Creates a new factory of {@link Record} based the state of a parser * * @param context the parser context */ public AbstractRecordFactory(Context context) { this.metaData = createMetaData(context); } /** * Creates a new {@link Record} with a row parsed from the input * * @param data the row parsed from the input * * @return a {@link Record} that provides many utility methods for consuming the data collected for a record parsed from the input. */ public abstract R newRecord(String[] data); public abstract M createMetaData(Context context); /** * Returns the metadata information associated with the records generated by this factory class * * @return the record metadata. */ public final M getRecordMetaData() { return metaData; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/MetaData.java000066400000000000000000000030101400120543400312270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.record; import com.univocity.parsers.conversions.*; class MetaData { MetaData(int index) { this.index = index; } public final int index; public Class type = String.class; public Object defaultValue = null; @SuppressWarnings("rawtypes") private Conversion[] conversions = null; @SuppressWarnings("rawtypes") public Conversion[] getConversions(){ return conversions; } @SuppressWarnings("rawtypes") public void setDefaultConversions(Conversion[] conversions){ this.conversions = conversions; } @SuppressWarnings("unchecked") public Object convert(Object out){ if(conversions == null){ return out; } for (int i = 0; i < conversions.length; i++) { out = conversions[i].execute(out); } return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/Record.java000066400000000000000000001730231400120543400310010ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.record; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import java.math.*; import java.util.*; /** * A record parsed from the input, with convenience methods for easier data manipulation. * Records are obtained from {@link com.univocity.parsers.common.AbstractParser} methods such as *

    *
  • {@link AbstractParser#parseAllRecords(java.io.Reader)}
  • *
  • {@link AbstractParser#parseNextRecord()}
  • *
  • {@link AbstractParser#parseRecord(String)}
  • *
*/ public interface Record { /** * Returns the {@link RecordMetaData} associated with all records parsed from the input. * The metadata allows associating types, conversions and default values to any column, which * will be used when performing operations that can convert plain input strings into object instances. * Methods such as {@link #toFieldMap(String...)}, {@link #fillFieldMap(Map, String...)} and any other * method that returns Objects will use the metadata information to perform data conversions. * * @return the metadata object that provides information and basic data conversion controls * over all records parsed from the input. */ RecordMetaData getMetaData(); /** * Returns the plain values obtained from a record parsed from the input. * * @return a {@code String} array with all values parsed from the input for this record. */ String[] getValues(); /** * Returns a selection of plain values obtained from a record parsed from the input. * * @param fieldNames sequence of field names whose values will be returned. * * @return a {@code String} array with the selected values parsed from the input for this record. */ String[] getValues(String... fieldNames); /** * Returns a selection of plain values obtained from a record parsed from the input. * * @param fieldIndexes sequence of field indexes whose values will be returned. * * @return a {@code String} array with the selected values parsed from the input for this record. */ String[] getValues(int... fieldIndexes); /** * Returns a selection of plain values obtained from a record parsed from the input. * * @param fields sequence of fields whose values will be returned. * * @return a {@code String} array with the selected values parsed from the input for this record. */ String[] getValues(Enum... fields); /** * Returns the value contained in the given column. * * @param headerName the name of the column whose value will be returned * @param expectedType the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * * @return the value this record holds at the given column */ T getValue(String headerName, Class expectedType); /** * Returns the value contained in the given column. * * @param column the column whose value will be returned * @param expectedType the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * * @return the value this record holds at the given column */ T getValue(Enum column, Class expectedType); /** * Returns the value contained in the given column. * * @param columnIndex the position of the column whose value will be returned * @param expectedType the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * * @return the value this record holds at the given column */ T getValue(int columnIndex, Class expectedType); /** * Returns the value contained in the given column, after applying a sequence of conversion over it. * * @param headerName the name of the column whose value will be returned * @param expectedType the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * @param conversions the sequence of {@link Conversion}s to apply over the column value. * * @return the value this record holds at the given column */ @SuppressWarnings("rawtypes") T getValue(String headerName, Class expectedType, Conversion... conversions); /** * Returns the value contained in the given column, after applying a sequence of conversion over it. * * @param column the column whose value will be returned * @param expectedType the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * @param conversions the sequence of {@link Conversion}s to apply over the column value. * * @return the value this record holds at the given column */ @SuppressWarnings("rawtypes") T getValue(Enum column, Class expectedType, Conversion... conversions); /** * Returns the value contained in the given column, after applying a sequence of conversion over it. * * @param columnIndex the index of the column whose value will be returned * @param expectedType the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * @param conversions the sequence of {@link Conversion}s to apply over the column value. * * @return the value this record holds at the given column */ @SuppressWarnings("rawtypes") T getValue(int columnIndex, Class expectedType, Conversion... conversions); /** * Returns the value contained in the given column, or a default value if the column contains {@code null} * * @param headerName the name of the column whose value will be returned * @param defaultValue The default value to use if the column contains {@code null}. Its type will be used to derive * the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * * @return the value this record holds at the given column, or the default value in case the columns is {@code null} */ T getValue(String headerName, T defaultValue); /** * Returns the value contained in the given column, or a default value if the column contains {@code null} * * @param column the column whose value will be returned * @param defaultValue The default value to use if the column contains {@code null}. Its type will be used to derive * the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * * @return the value this record holds at the given column, or the default value in case the columns is {@code null} */ T getValue(Enum column, T defaultValue); /** * Returns the value contained in the given column, or a default value if the column contains {@code null} * * @param columnIndex index of the column whose value will be returned * @param defaultValue The default value to use if the column contains {@code null}. Its type will be used to derive * the expected type of the value. A conversion will be executed against the value * to produce a result with the expected type. * @param the expected value type * * @return the value this record holds at the given column, or the default value in case the column is {@code null} */ T getValue(int columnIndex, T defaultValue); /** * Returns the value contained in a given column, after applying a sequence of conversions over it. * * @param headerName the name of the column whose value will be returned * @param defaultValue The default value to use if the column contains {@code null}. * @param conversions the sequence of {@link Conversion}s to apply over the column value. * @param the expected value type * * @return the value this record holds at the given column */ @SuppressWarnings("rawtypes") T getValue(String headerName, T defaultValue, Conversion... conversions); /** * Returns the value contained in a given column, after applying a sequence of conversions over it. * * @param column the column whose value will be returned * @param defaultValue The default value to use if the column contains {@code null}. * @param conversions the sequence of {@link Conversion}s to apply over the column value. * @param the expected value type * * @return the value this record holds at the given column */ @SuppressWarnings("rawtypes") T getValue(Enum column, T defaultValue, Conversion... conversions); /** * Returns the value contained in a given column, after applying a sequence of conversions over it. * * @param columnIndex the index of the column whose value will be returned * @param defaultValue The default value to use if the column contains {@code null}. * @param conversions the sequence of {@link Conversion}s to apply over the column value. * @param the expected value type * * @return the value this record holds at the given column */ @SuppressWarnings("rawtypes") T getValue(int columnIndex, T defaultValue, Conversion... conversions); /** * Returns the {@code String} value in the given column * * @param headerName the name of the column * * @return the value stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ String getString(String headerName); /** * Returns the {@code String} value in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} * * @param column the column * * @return the value stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ String getString(Enum column); /** * Returns the {@code String} value in the given column, truncating it to a given maximum length * * @param columnIndex the index of the column * @param maxLength the maximum number of characters to be returned. * * @return the value stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ String getString(int columnIndex, int maxLength); /** * Returns the {@code String} value in the given column, truncating it to a given maximum length * * @param headerName the name of the column * @param maxLength the maximum number of characters to be returned. * * @return the value stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ String getString(String headerName, int maxLength); /** * Returns the {@code String} value in the given column, truncating it to a given maximum length * * @param column the column * @param maxLength the maximum number of characters to be returned. * * @return the value stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ String getString(Enum column, int maxLength); /** * Returns the {@code String} value in the given column * * @param columnIndex the index of the column * * @return the value stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ String getString(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Byte} and returns the result. * The {@link ByteConversion} conversion will be used perform the transformation. * * @param headerName the name of the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Byte} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Byte getByte(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Byte} and returns the result. * The {@link ByteConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Byte} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Byte getByte(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Byte} and returns the result. * The {@link ByteConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Byte} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Byte getByte(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Short} and returns the result. * The {@link ShortConversion} conversion will be used perform the transformation. * * @param headerName the name of the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Short} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Short getShort(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Short} and returns the result. * The {@link ShortConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Short} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Short getShort(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Short} and returns the result. * The {@link ShortConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Short} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Short getShort(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Integer} and returns the result. * The {@link IntegerConversion} conversion will be used perform the transformation. * * @param headerName the name of the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Integer} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Integer getInt(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Integer} and returns the result. * The {@link IntegerConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Integer} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Integer getInt(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Integer} and returns the result. * The {@link IntegerConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Integer} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Integer getInt(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link LongConversion} conversion will be used perform the transformation. * * @param headerName the name of the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Long getLong(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link LongConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Long getLong(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link LongConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Long getLong(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link FloatConversion} conversion will be used perform the transformation. * * @param headerName the name of the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Float getFloat(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Float} and returns the result. * The {@link FloatConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Float} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Float getFloat(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Float} and returns the result. * The {@link FloatConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Float} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Float getFloat(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Double} and returns the result. * The {@link DoubleConversion} conversion will be used perform the transformation. * * @param headerName the name of the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Double} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Double getDouble(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Double} and returns the result. * The {@link DoubleConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Double} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Double getDouble(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Double} and returns the result. * The {@link DoubleConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code Double} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Double getDouble(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Byte} and returns the result. * The {@link ByteConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Byte} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Byte getByte(String headerName); /** * Converts the {@code String} value in the given column to a {@code Byte} and returns the result. * The {@link ByteConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Byte} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Byte getByte(Enum column); /** * Converts the {@code String} value in the given column to a {@code Byte} and returns the result. * The {@link ByteConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code Byte} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Byte getByte(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Short} and returns the result. * The {@link ShortConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Short} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Short getShort(String headerName); /** * Converts the {@code String} value in the given column to a {@code Short} and returns the result. * The {@link ShortConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Short} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Short getShort(Enum column); /** * Converts the {@code String} value in the given column to a {@code Short} and returns the result. * The {@link ShortConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code Short} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Short getShort(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Integer} and returns the result. * The {@link IntegerConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Integer} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Integer getInt(String headerName); /** * Converts the {@code String} value in the given column to a {@code Integer} and returns the result. * The {@link IntegerConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Integer} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Integer getInt(Enum column); /** * Converts the {@code String} value in the given column to a {@code Integer} and returns the result. * The {@link IntegerConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code Integer} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Integer getInt(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link LongConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Long getLong(String headerName); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link LongConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Long getLong(Enum column); /** * Converts the {@code String} value in the given column to a {@code Long} and returns the result. * The {@link LongConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code Long} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Long getLong(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Float} and returns the result. * The {@link FloatConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Float} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Float getFloat(String headerName); /** * Converts the {@code String} value in the given column to a {@code Float} and returns the result. * The {@link FloatConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Float} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Float getFloat(Enum column); /** * Converts the {@code String} value in the given column to a {@code Float} and returns the result. * The {@link FloatConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code Float} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Float getFloat(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Double} and returns the result. * The {@link DoubleConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Double} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Double getDouble(String headerName); /** * Converts the {@code String} value in the given column to a {@code Double} and returns the result. * The {@link DoubleConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Double} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Double getDouble(Enum column); /** * Converts the {@code String} value in the given column to a {@code Double} and returns the result. * The {@link DoubleConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code Double} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Double getDouble(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Character} and returns the result. * The {@link CharacterConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Character} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Character getChar(String headerName); /** * Converts the {@code String} value in the given column to a {@code Character} and returns the result. * The {@link CharacterConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Character} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Character getChar(Enum column); /** * Converts the {@code String} value in the given column to a {@code Character} and returns the result. * The {@link CharacterConversion} conversion will be used perform the transformation. * * @param columnIndex the column index * * @return the {@code Character} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Character getChar(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Boolean} and returns the result. * The {@link BooleanConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code Boolean} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Boolean getBoolean(String headerName); /** * Converts the {@code String} value in the given column to a {@code Boolean} and returns the result. * The {@link BooleanConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code Boolean} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Boolean getBoolean(Enum column); /** * Converts the {@code String} value in the given column to a {@code Boolean} and returns the result. * The {@link BooleanConversion} conversion will be used perform the transformation. * * @param columnIndex the column index * * @return the {@code Boolean} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Boolean getBoolean(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Boolean} and returns the result. * The {@link BooleanConversion} conversion will be used perform the transformation. * * @param headerName the column name * @param trueString a {@code String} that represents the {@code Boolean} value {@code true} * @param falseString a {@code String} that represents the {@code Boolean} value {@code false} * * @return the {@code Boolean} stored in the given column if its original {@code String} value matches * either the trueString or falseString, otherwise {@code null} or the default specified in {@link RecordMetaData#defaultValueOf(String)} */ Boolean getBoolean(String headerName, String trueString, String falseString); /** * Converts the {@code String} value in the given column to a {@code Boolean} and returns the result. * The {@link BooleanConversion} conversion will be used perform the transformation. * * @param column the column * @param trueString a {@code String} that represents the {@code Boolean} value {@code true} * @param falseString a {@code String} that represents the {@code Boolean} value {@code false} * * @return the {@code Boolean} stored in the given column if its original {@code String} value matches * either the trueString or falseString, otherwise {@code null} or the default specified in {@link RecordMetaData#defaultValueOf(String)} */ Boolean getBoolean(Enum column, String trueString, String falseString); /** * Converts the {@code String} value in the given column to a {@code Boolean} and returns the result. * The {@link BooleanConversion} conversion will be used perform the transformation. * * @param columnIndex the column index * @param trueString a {@code String} that represents the {@code Boolean} value {@code true} * @param falseString a {@code String} that represents the {@code Boolean} value {@code false} * * @return the {@code Boolean} stored in the given column if its original {@code String} value matches * either the trueString or falseString, otherwise {@code null} or the default specified in {@link RecordMetaData#defaultValueOf(String)} */ Boolean getBoolean(int columnIndex, String trueString, String falseString); /** * Converts the {@code String} value in the given column to a {@code BigInteger} and returns the result. * The {@link BigIntegerConversion} conversion will be used perform the transformation. * * @param headerName the column name * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code BigInteger} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigInteger getBigInteger(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code BigInteger} and returns the result. * The {@link BigIntegerConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code BigInteger} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigInteger getBigInteger(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code BigInteger} and returns the result. * The {@link BigIntegerConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code BigInteger} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigInteger getBigInteger(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code BigDecimal} and returns the result. * The {@link BigDecimalConversion} conversion will be used perform the transformation. * * @param headerName the column name * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code BigDecimal} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigDecimal getBigDecimal(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code BigDecimal} and returns the result. * The {@link BigDecimalConversion} conversion will be used perform the transformation. * * @param column the column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code BigDecimal} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigDecimal getBigDecimal(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code BigDecimal} and returns the result. * The {@link BigDecimalConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} * * @return the {@code BigDecimal} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigDecimal getBigDecimal(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code BigInteger} and returns the result. * The {@link BigIntegerConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code BigInteger} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigInteger getBigInteger(String headerName); /** * Converts the {@code String} value in the given column to a {@code BigInteger} and returns the result. * The {@link BigIntegerConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code BigInteger} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigInteger getBigInteger(Enum column); /** * Converts the {@code String} value in the given column to a {@code BigInteger} and returns the result. * The {@link BigIntegerConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code BigInteger} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigInteger getBigInteger(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code BigDecimal} and returns the result. * The {@link BigDecimalConversion} conversion will be used perform the transformation. * * @param headerName the column name * * @return the {@code BigDecimal} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigDecimal getBigDecimal(String headerName); /** * Converts the {@code String} value in the given column to a {@code BigDecimal} and returns the result. * The {@link BigDecimalConversion} conversion will be used perform the transformation. * * @param column the column * * @return the {@code BigDecimal} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigDecimal getBigDecimal(Enum column); /** * Converts the {@code String} value in the given column to a {@code BigDecimal} and returns the result. * The {@link BigDecimalConversion} conversion will be used perform the transformation. * * @param columnIndex the columnIndex * * @return the {@code BigDecimal} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ BigDecimal getBigDecimal(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Date} and returns the result. * The {@link DateConversion} conversion will be used perform the transformation. * * @param headerName the column name * @param format the numeric mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"lenient=true"} * * @return the {@code Date} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Date getDate(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Date} and returns the result. * The {@link DateConversion} conversion will be used perform the transformation. * * @param column the column * @param format the date mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"lenient=true"} * * @return the {@code Date} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Date getDate(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Date} and returns the result. * The {@link DateConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the date mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"lenient=true"} * * @return the {@code Date} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Date getDate(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Calendar} and returns the result. * The {@link CalendarConversion} conversion will be used perform the transformation. * * @param headerName the column name * @param format the date mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"lenient=true"} * * @return the {@code Calendar} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Calendar getCalendar(String headerName, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Calendar} and returns the result. * The {@link CalendarConversion} conversion will be used perform the transformation. * * @param column the column * @param format the date mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"lenient=true"} * * @return the {@code Calendar} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Calendar getCalendar(Enum column, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Calendar} and returns the result. * The {@link CalendarConversion} conversion will be used perform the transformation. * * @param columnIndex the index of column * @param format the date mask to apply over the parsed content * @param formatOptions a sequence of key-value pairs with options to configure the underlying formatter. * Each element must be specified as {@code property_name=property_value}, * e.g. options={"lenient=true"} * * @return the {@code Calendar} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Calendar getCalendar(int columnIndex, String format, String... formatOptions); /** * Converts the {@code String} value in the given column to a {@code Date} and returns the result. * The {@link DateConversion} conversion sequence registered using * {@link RecordMetaData#convertFields(Conversion[])} will be used perform the transformation. * * @param headerName the column name * * @return the {@code Date} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Date getDate(String headerName); /** * Converts the {@code String} value in the given column to a {@code Date} and returns the result. * The {@link DateConversion} conversion sequence registered using * {@link RecordMetaData#convertFields(Conversion[])} will be used perform the transformation. * * @param column the column * * @return the {@code Date} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Date getDate(Enum column); /** * Converts the {@code String} value in the given column to a {@code Date} and returns the result. * The {@link DateConversion} conversion sequence registered using * {@link RecordMetaData#convertFields(Conversion[])} will be used perform the transformation. * * @param columnIndex the column index * * @return the {@code Date} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Date getDate(int columnIndex); /** * Converts the {@code String} value in the given column to a {@code Calendar} and returns the result. * The {@link CalendarConversion} conversion sequence registered using * {@link RecordMetaData#convertFields(Conversion[])} will be used perform the transformation. * * @param headerName the column name * * @return the {@code Calendar} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Calendar getCalendar(String headerName); /** * Converts the {@code String} value in the given column to a {@code Calendar} and returns the result. * The {@link CalendarConversion} conversion sequence registered using * {@link RecordMetaData#convertFields(Conversion[])} will be used perform the transformation. * * @param column the column * * @return the {@code Calendar} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Calendar getCalendar(Enum column); /** * Converts the {@code String} value in the given column to a {@code Calendar} and returns the result. * The {@link CalendarConversion} conversion sequence registered using * {@link RecordMetaData#convertFields(Conversion[])} will be used perform the transformation. * * @param columnIndex the column index * * @return the {@code Calendar} stored in the given column, {@code null} or the * default value specified in {@link RecordMetaData#defaultValueOf(String)} */ Calendar getCalendar(int columnIndex); /** * Converts the record into a map of {@code String} values. * * @param selectedFields the header names to use as keys of the map. If no selection then all headers will be used. * * @return a map containing the selected (or all) header names as the keys, and their respective values. */ Map toFieldMap(String... selectedFields); /** * Converts the record into a map of {@code String} values. * * @param selectedIndexes the column indexes to use as keys of the map. If no selection then all indexes will be used. * * @return a map containing the selected (or all) column indexes as the keys, and their respective values. */ Map toIndexMap(int... selectedIndexes); /** * Converts the record into a map of {@code String} values. * * @param enumType the enumeration type. * @param selectedColumns the columns to use as keys of the map. If no selection then all values of the enumeration type will be used. * @param the enumeration type * * @return a map containing the selected (or all) columns as the keys, and their respective values. */ > Map toEnumMap(Class enumType, T... selectedColumns); /** * Fills a map with the {@code String} values of this record. * * @param map the map that will receive the values * @param selectedFields the header names to use as keys of the map * * @return the input map, containing the selected header names as the keys, and their respective values. */ Map fillFieldMap(Map map, String... selectedFields); /** * Fills a map with the {@code String} values of this record. * * @param map the map that will receive the values * @param selectedIndexes the column indexes to use as keys of the map * * @return the input map, containing the selected column indexes as the keys, and their respective values. */ Map fillIndexMap(Map map, int... selectedIndexes); /** * Fills a map with the {@code String} values of this record. * * @param map the map that will receive the values * @param selectedColumns the column to use as keys of the map * @param the enumeration type * * @return the input map, containing the selected header names as the keys, and their respective values. */ > Map fillEnumMap(Map map, T... selectedColumns); /** * Converts the record into a map of {@code Object} values. Conversions must be registered using * {@link RecordMetaData#convertFields(Conversion[])} or {@link RecordMetaData#convertIndexes(Conversion[])} (Conversion[])}. * Columns without a known conversion will have their values put into the map as plain {@code String}s. * * @param selectedFields the column names to use as keys of the map * * @return a map containing the selected column names as the keys, and their respective values. */ Map toFieldObjectMap(String... selectedFields); /** * Converts the record into a map of {@code Object} values. Conversions must be registered using * {@link RecordMetaData#convertFields(Conversion[])} or {@link RecordMetaData#convertIndexes(Conversion[])} (Conversion[])}. * Columns without a known conversion will have their values put into the map as plain {@code String}s. * * @param selectedIndexes the column indexes to use as keys of the map * * @return a map containing the selected column indexes as the keys, and their respective values. */ Map toIndexObjectMap(int... selectedIndexes); /** * Converts the record into a map of {@code Object} values. Conversions must be registered using * {@link RecordMetaData#convertFields(Conversion[])} or {@link RecordMetaData#convertIndexes(Conversion[])} (Conversion[])}. * Columns without a known conversion will have their values put into the map as plain {@code String}s. * * @param enumType the enumeration type. * @param selectedColumns the column to use as keys of the map * @param the enumeration type * * @return a map containing the selected columns as the keys, and their respective values. */ > Map toEnumObjectMap(Class enumType, T... selectedColumns); /** * Fills a map with the converted {@code Object} values of this record. Conversions must be registered using * {@link RecordMetaData#convertFields(Conversion[])} or {@link RecordMetaData#convertIndexes(Conversion[])} (Conversion[])}. * Columns without a known conversion will have their values put into the map as plain {@code String}s. * * @param map the map that will receive the values * @param selectedFields the column names to use as keys of the map * * @return the input map, containing the selected columns as the keys, and their respective values. */ Map fillFieldObjectMap(Map map, String... selectedFields); /** * Fills a map with the converted {@code Object} values of this record. Conversions must be registered using * {@link RecordMetaData#convertFields(Conversion[])} or {@link RecordMetaData#convertIndexes(Conversion[])} (Conversion[])}. * Columns without a known conversion will have their values put into the map as plain {@code String}s. * * @param map the map that will receive the values * @param selectedIndexes the column indexes to use as keys of the map * * @return the input map, containing the selected columns as the keys, and their respective values. */ Map fillIndexObjectMap(Map map, int... selectedIndexes); /** * Fills a map with the converted {@code Object} values of this record. Conversions must be registered using * {@link RecordMetaData#convertFields(Conversion[])} or {@link RecordMetaData#convertIndexes(Conversion[])} (Conversion[])}. * Columns without a known conversion will have their values put into the map as plain {@code String}s. * * @param map the map that will receive the values * @param selectedColumns the column to use as keys of the map * @param the enumeration type * * @return the input map, containing the selected columns as the keys, and their respective values. */ > Map fillEnumObjectMap(Map map, T... selectedColumns); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/RecordFactory.java000066400000000000000000000031611400120543400323240ustar00rootroot00000000000000/* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.record; import com.univocity.parsers.common.*; /** * A factory class that provides implementations of {@link Record} based on the current state * of an {@link AbstractParser}(via its {@link ParsingContext}), and raw input records. */ public class RecordFactory extends AbstractRecordFactory { /** * Creates a new factory of {@link Record} based the state of a parser * * @param context the parser context */ public RecordFactory(Context context) { super(context); } /** * Creates a new {@link Record} with a row parsed from the input * * @param data the row parsed from the input * * @return a {@link Record} that provides many utility methods for consuming the data collected for a record parsed from the input. */ @Override public Record newRecord(String[] data) { return new RecordImpl(data, metaData); } @Override public RecordMetaDataImpl createMetaData(Context context) { return new RecordMetaDataImpl(context); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/RecordImpl.java000066400000000000000000000477061400120543400316330ustar00rootroot00000000000000/* * Copyright 2015 Univocity Software Pty Ltd * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.record; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import java.math.*; import java.util.*; class RecordImpl implements Record { private final String[] data; private final RecordMetaDataImpl metaData; RecordImpl(String[] data, RecordMetaDataImpl metaData) { this.data = data; this.metaData = metaData; } @Override public RecordMetaData getMetaData() { return metaData; } @Override public String[] getValues() { return data; } @Override public T getValue(String headerName, Class expectedType) { return metaData.getObjectValue(data, headerName, expectedType, null); } @Override public T getValue(Enum column, Class expectedType) { return metaData.getObjectValue(data, column, expectedType, null); } @Override public T getValue(int columnIndex, Class expectedType) { return metaData.getObjectValue(data, columnIndex, expectedType, null); } @SuppressWarnings("rawtypes") @Override public T getValue(String headerName, Class expectedType, Conversion... conversions) { return metaData.getValue(data, headerName, expectedType, conversions); } @SuppressWarnings("rawtypes") @Override public T getValue(Enum column, Class expectedType, Conversion... conversions) { return metaData.getValue(data, column, expectedType, conversions); } @SuppressWarnings("rawtypes") @Override public T getValue(int columnIndex, Class expectedType, Conversion... conversions) { return metaData.getValue(data, columnIndex, expectedType, conversions); } @SuppressWarnings("unchecked") @Override public T getValue(String headerName, T defaultValue) { return metaData.getObjectValue(data, headerName, (Class) defaultValue.getClass(), defaultValue); } @SuppressWarnings("unchecked") @Override public T getValue(Enum column, T defaultValue) { return metaData.getObjectValue(data, column, (Class) defaultValue.getClass(), defaultValue); } @SuppressWarnings("unchecked") @Override public T getValue(int columnIndex, T defaultValue) { return metaData.getObjectValue(data, columnIndex, (Class) defaultValue.getClass(), defaultValue); } @SuppressWarnings("rawtypes") @Override public T getValue(String headerName, T defaultValue, Conversion... conversions) { return metaData.getValue(data, headerName, defaultValue, conversions); } @SuppressWarnings("rawtypes") @Override public T getValue(Enum column, T defaultValue, Conversion... conversions) { return metaData.getValue(data, column, defaultValue, conversions); } @SuppressWarnings("rawtypes") @Override public T getValue(int columnIndex, T defaultValue, Conversion... conversions) { return metaData.getValue(data, columnIndex, defaultValue, conversions); } @Override public String getString(String headerName) { return metaData.getObjectValue(data, headerName, String.class, null); } @Override public String getString(Enum column) { return metaData.getObjectValue(data, column, String.class, null); } @Override public String getString(int columnIndex) { return metaData.getObjectValue(data, columnIndex, String.class, null); } @Override public String getString(String headerName, int maxLength) { return truncate(metaData.getValue(data, headerName), maxLength); } @Override public String getString(Enum column, int maxLength) { return truncate(metaData.getValue(data, column), maxLength); } @Override public String getString(int columnIndex, int maxLength) { return truncate(metaData.getValue(data, columnIndex), maxLength); } private String truncate(String string, int maxLength) { if (string == null) { return null; } if (maxLength < 0) { throw new IllegalArgumentException("Maximum length can't be negative"); } if (string.length() > maxLength) { return string.substring(0, maxLength); } return string; } @Override public Byte getByte(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Byte.class, null, format, formatOptions); } @Override public Byte getByte(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Byte.class, null, format, formatOptions); } @Override public Byte getByte(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Byte.class, null, format, formatOptions); } @Override public Short getShort(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Short.class, null, format, formatOptions); } @Override public Short getShort(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Short.class, null, format, formatOptions); } @Override public Short getShort(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Short.class, null, format, formatOptions); } @Override public Integer getInt(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Integer.class, null, format, formatOptions); } @Override public Integer getInt(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Integer.class, null, format, formatOptions); } @Override public Integer getInt(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Integer.class, null, format, formatOptions); } @Override public Long getLong(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Long.class, null, format, formatOptions); } @Override public Long getLong(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Long.class, null, format, formatOptions); } @Override public Long getLong(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Long.class, null, format, formatOptions); } @Override public Float getFloat(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Float.class, null, format, formatOptions); } @Override public Float getFloat(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Float.class, null, format, formatOptions); } @Override public Float getFloat(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Float.class, null, format, formatOptions); } @Override public Double getDouble(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Double.class, null, format, formatOptions); } @Override public Double getDouble(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Double.class, null, format, formatOptions); } @Override public Double getDouble(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Double.class, null, format, formatOptions); } @Override public Character getChar(String headerName) { return metaData.getObjectValue(data, headerName, Character.class, null); } @Override public Character getChar(Enum column) { return metaData.getObjectValue(data, column, Character.class, null); } @Override public Character getChar(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Character.class, null); } @Override public Boolean getBoolean(String headerName) { return metaData.getObjectValue(data, headerName, Boolean.class, null); } @Override public Boolean getBoolean(Enum column) { return metaData.getObjectValue(data, column, Boolean.class, null); } @Override public Boolean getBoolean(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Boolean.class, null); } @Override public Boolean getBoolean(String headerName, String trueString, String falseString) { return metaData.getObjectValue(data, headerName, Boolean.class, false, trueString, falseString); } @Override public Boolean getBoolean(Enum column, String trueString, String falseString) { return metaData.getObjectValue(data, column, Boolean.class, false, trueString, falseString); } @Override public Boolean getBoolean(int columnIndex, String trueString, String falseString) { return metaData.getObjectValue(data, columnIndex, Boolean.class, false, trueString, falseString); } @Override public BigInteger getBigInteger(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, BigInteger.class, null, format, formatOptions); } @Override public BigInteger getBigInteger(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, BigInteger.class, null, format, formatOptions); } @Override public BigInteger getBigInteger(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, BigInteger.class, null, format, formatOptions); } @Override public BigDecimal getBigDecimal(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, BigDecimal.class, null, format, formatOptions); } @Override public BigDecimal getBigDecimal(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, BigDecimal.class, null, format, formatOptions); } @Override public BigDecimal getBigDecimal(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, BigDecimal.class, null, format, formatOptions); } @Override public Date getDate(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Date.class, null, format, formatOptions); } @Override public Date getDate(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Date.class, null, format, formatOptions); } @Override public Date getDate(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Date.class, null, format, formatOptions); } @Override public Calendar getCalendar(String headerName, String format, String... formatOptions) { return metaData.getObjectValue(data, headerName, Calendar.class, null, format, formatOptions); } @Override public Calendar getCalendar(Enum column, String format, String... formatOptions) { return metaData.getObjectValue(data, column, Calendar.class, null, format, formatOptions); } @Override public Calendar getCalendar(int columnIndex, String format, String... formatOptions) { return metaData.getObjectValue(data, columnIndex, Calendar.class, null, format, formatOptions); } private String[] buildSelection(String[] selectedFields) { if (selectedFields.length == 0) { selectedFields = metaData.headers(); } return selectedFields; } private int[] buildSelection(int[] selectedIndexes) { if (selectedIndexes.length == 0) { selectedIndexes = new int[data.length]; for (int i = 0; i < data.length; i++) { selectedIndexes[i] = i; } } return selectedIndexes; } public > T[] buildSelection(Class enumType, T... selectedColumns) { if (selectedColumns.length == 0) { selectedColumns = enumType.getEnumConstants(); } return selectedColumns; } @Override public Map toIndexMap(int... selectedIndexes) { return fillIndexMap(new HashMap(selectedIndexes.length), selectedIndexes); } @Override public Map toFieldMap(String... selectedFields) { return fillFieldMap(new HashMap(selectedFields.length), selectedFields); } @Override public > Map toEnumMap(Class enumType, T... selectedColumns) { return fillEnumMap(new EnumMap(enumType), selectedColumns); } @Override public Map fillFieldMap(Map map, String... selectedFields) { selectedFields = buildSelection(selectedFields); for (int i = 0; i < selectedFields.length; i++) { map.put(selectedFields[i], getString(selectedFields[i])); } return map; } @Override public Map fillIndexMap(Map map, int... selectedIndexes) { selectedIndexes = buildSelection(selectedIndexes); for (int i = 0; i < selectedIndexes.length; i++) { map.put(selectedIndexes[i], getString(selectedIndexes[i])); } return map; } @Override public > Map fillEnumMap(Map map, T... selectedColumns) { for (int i = 0; i < selectedColumns.length; i++) { map.put(selectedColumns[i], getString(selectedColumns[i])); } return map; } @Override public Map toFieldObjectMap(String... selectedFields) { return fillFieldObjectMap(new HashMap(selectedFields.length), selectedFields); } @Override public Map toIndexObjectMap(int... selectedIndex) { return fillIndexObjectMap(new HashMap(selectedIndex.length), selectedIndex); } @Override public > Map toEnumObjectMap(Class enumType, T... selectedColumns) { return fillEnumObjectMap(new EnumMap(enumType), selectedColumns); } @Override public Map fillFieldObjectMap(Map map, String... selectedFields) { selectedFields = buildSelection(selectedFields); for (int i = 0; i < selectedFields.length; i++) { map.put(selectedFields[i], metaData.getObjectValue(data, selectedFields[i], null, null)); } return map; } @Override public Map fillIndexObjectMap(Map map, int... selectedIndexes) { selectedIndexes = buildSelection(selectedIndexes); for (int i = 0; i < selectedIndexes.length; i++) { map.put(selectedIndexes[i], metaData.getObjectValue(data, selectedIndexes[i], null, null)); } return map; } @Override public > Map fillEnumObjectMap(Map map, T... selectedColumns) { selectedColumns = buildSelection((Class) selectedColumns.getClass().getComponentType(), selectedColumns); for (int i = 0; i < selectedColumns.length; i++) { map.put(selectedColumns[i], metaData.getObjectValue(data, selectedColumns[i], null, null)); } return map; } @Override public BigInteger getBigInteger(String headerName) { return metaData.getObjectValue(data, headerName, BigInteger.class, null); } @Override public BigInteger getBigInteger(Enum column) { return metaData.getObjectValue(data, column, BigInteger.class, null); } @Override public BigInteger getBigInteger(int columnIndex) { return metaData.getObjectValue(data, columnIndex, BigInteger.class, null); } @Override public BigDecimal getBigDecimal(String headerName) { return metaData.getObjectValue(data, headerName, BigDecimal.class, null); } @Override public BigDecimal getBigDecimal(Enum column) { return metaData.getObjectValue(data, column, BigDecimal.class, null); } @Override public BigDecimal getBigDecimal(int columnIndex) { return metaData.getObjectValue(data, columnIndex, BigDecimal.class, null); } @Override public Byte getByte(String headerName) { return metaData.getObjectValue(data, headerName, Byte.class, null); } @Override public Byte getByte(Enum column) { return metaData.getObjectValue(data, column, Byte.class, null); } @Override public Byte getByte(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Byte.class, null); } @Override public Short getShort(String headerName) { return metaData.getObjectValue(data, headerName, Short.class, null); } @Override public Short getShort(Enum column) { return metaData.getObjectValue(data, column, Short.class, null); } @Override public Short getShort(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Short.class, null); } @Override public Integer getInt(String headerName) { return metaData.getObjectValue(data, headerName, Integer.class, null); } @Override public Integer getInt(Enum column) { return metaData.getObjectValue(data, column, Integer.class, null); } @Override public Integer getInt(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Integer.class, null); } @Override public Long getLong(String headerName) { return metaData.getObjectValue(data, headerName, Long.class, null); } @Override public Long getLong(Enum column) { return metaData.getObjectValue(data, column, Long.class, null); } @Override public Long getLong(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Long.class, null); } @Override public Float getFloat(String headerName) { return metaData.getObjectValue(data, headerName, Float.class, null); } @Override public Float getFloat(Enum column) { return metaData.getObjectValue(data, column, Float.class, null); } @Override public Float getFloat(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Float.class, null); } @Override public Double getDouble(String headerName) { return metaData.getObjectValue(data, headerName, Double.class, null); } @Override public Double getDouble(Enum column) { return metaData.getObjectValue(data, column, Double.class, null); } @Override public Double getDouble(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Double.class, null); } @Override public Date getDate(String headerName) { return metaData.getObjectValue(data, headerName, Date.class, null); } @Override public Date getDate(Enum column) { return metaData.getObjectValue(data, column, Date.class, null); } @Override public Date getDate(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Date.class, null); } @Override public Calendar getCalendar(String headerName) { return metaData.getObjectValue(data, headerName, Calendar.class, null); } @Override public Calendar getCalendar(Enum column) { return metaData.getObjectValue(data, column, Calendar.class, null); } @Override public Calendar getCalendar(int columnIndex) { return metaData.getObjectValue(data, columnIndex, Calendar.class, null); } public String toString() { if (data == null) { return "null"; } if (data.length == 0) { return "[]"; } StringBuilder out = new StringBuilder(); for (int i = 0; i < data.length; i++) { if (out.length() != 0) { out.append(',').append(' '); } out.append(data[i]); } return out.toString(); } @Override public boolean equals(Object o) { return o == this; } @Override public int hashCode() { return Arrays.hashCode(data); } @Override public String[] getValues(String... fieldNames) { String[] out = new String[fieldNames.length]; for(int i = 0; i < out.length;i++){ out[i] = getString(fieldNames[i]); } return out; } @Override public String[] getValues(int... fieldIndexes) { String[] out = new String[fieldIndexes.length]; for(int i = 0; i < out.length;i++){ out[i] = getString(fieldIndexes[i]); } return out; } @Override public String[] getValues(Enum ... fields) { String[] out = new String[fields.length]; for(int i = 0; i < out.length;i++){ out[i] = getString(fields[i]); } return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/RecordMetaData.java000066400000000000000000000210221400120543400323710ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.record; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.conversions.*; /** * Metadata with information about {@link Record}s parsed from the input. */ public interface RecordMetaData { /** * Returns the index of a given column * * @param column the column whose index will be returned * * @return index of the given column */ int indexOf(Enum column); /** * Returns the index of a given column * * @param headerName name of the column whose index will be returned * * @return index of the given column */ int indexOf(String headerName); /** * Returns the type associated with a given column, defined with the method {@code setTypeOfColumns(type, columns)} * * @param column the column whose type will be returned * * @return the type of the given column */ Class typeOf(Enum column); /** * Returns the type associated with a given column name, defined with the method {@code setTypeOfColumns(type, columns)} * * @param headerName name of the column whose type will be returned * * @return the type of the given column */ Class typeOf(String headerName); /** * Returns the type associated with a given column, defined with the method {@code setTypeOfColumns(type, columns)} * * @param columnIndex the position of the column whose type will be returned * * @return the type of the given column */ Class typeOf(int columnIndex); /** * Associates a type with one or more column. This allows the parsed data to be converted automatically * to the given type when reading data from a {@link Record}, e.g. {@link Record#toFieldObjectMap(String...)} will * convert the selected field values to their respective types, and then set the result as the values in the map. * * @param type the type to associate with a list of column * @param columns the columns that will be associated with the given type. */ @SuppressWarnings("rawtypes") void setTypeOfColumns(Class type, Enum... columns); /** * Associates a type with one or more column. This allows the parsed data to be converted automatically * to the given type when reading data from a {@link Record}, e.g. {@link Record#toFieldObjectMap(String...)} will * convert the selected field values to their respective types, and then set the result as the values in the map. * * @param type the type to associate with a list of column * @param headerNames the columns that will be associated with the given type. */ void setTypeOfColumns(Class type, String... headerNames); /** * Associates a type with one or more column. This allows the parsed data to be converted automatically * to the given type when reading data from a {@link Record}, e.g. {@link Record#toFieldObjectMap(String...)} will * convert the selected field values to their respective types, and then set the result as the values in the map. * * @param type the type to associate with a list of column * @param columnIndexes the columns that will be associated with the given type. */ void setTypeOfColumns(Class type, int... columnIndexes); /** * Associates a default value with one or more columns, in case the values contained are {@code null} * * @param defaultValue the value to be used for the given column when the parsed result is {@code null} * @param columns the columns to be associated with a default value. * @param type of the default value. */ void setDefaultValueOfColumns(T defaultValue, Enum... columns); /** * Associates a default value with one or more columns, in case the values contained are {@code null} * * @param defaultValue the value to be used for the given column when the parsed result is {@code null} * @param headerNames the column names to be associated with a default value. * @param type of the default value. */ void setDefaultValueOfColumns(T defaultValue, String... headerNames); /** * Associates a default value with one or more columns, in case the values contained are {@code null} * * @param defaultValue the value to be used for the given column when the parsed result is {@code null} * @param columnIndexes the column indexes to be associated with a default value. * @param type of the default value. */ void setDefaultValueOfColumns(T defaultValue, int... columnIndexes); /** * Returns the default value associated with a column (defined using {@code setDefaultValueOf(Column, Object)}) * * @param column the column whose default value will be returned * * @return the default value associated with the given column or {@code null}. */ Object defaultValueOf(Enum column); /** * Returns the default value associated with a column (defined using {@code setDefaultValueOf(Column, Object)}) * * @param headerName the column name whose default value will be returned * * @return the default value associated with the given column or {@code null}. */ Object defaultValueOf(String headerName); /** * Returns the default value associated with a column (defined using {@code setDefaultValueOf(Column, Object)}) * * @param columnIndex the column index whose default value will be returned * * @return the default value associated with the given column or {@code null}. */ Object defaultValueOf(int columnIndex); /** * Associates a sequence of {@link Conversion}s to fields of a given set of fields * * @param enumType the type of the enumeration whose values represent headers in the input {@code Record}s * @param conversions the sequence of conversions to apply * @param the enumeration type * * @return (modifiable) set of fields to be selected and against which the given conversion sequence will be applied. */ @SuppressWarnings("rawtypes") > FieldSet convertFields(Class enumType, Conversion... conversions); /** * Associates a sequence of {@link Conversion}s to fields of a given set of field names * * @param conversions the sequence of conversions to apply * * @return (modifiable) set of fields names to be selected and against which the given conversion sequence will be applied. */ @SuppressWarnings("rawtypes") FieldSet convertFields(Conversion... conversions); /** * Associates a sequence of {@link Conversion}s to fields of a given set of column indexes * * @param conversions the sequence of conversions to apply * * @return (modifiable) set of column indexes to be selected and against which the given conversion sequence will be applied. */ @SuppressWarnings("rawtypes") FieldSet convertIndexes(Conversion... conversions); /** * Returns the column names of the {@link Record}s parsed from the input. * *

If the headers are extracted from the input (i.e. {@link CommonParserSettings#isHeaderExtractionEnabled()} == true), then these values will be returned. *

If no headers are extracted from the input, then the configured headers in {@link CommonSettings#getHeaders()} will be returned. * * @return the headers associated with the {@link Record}s parsed from the input */ String[] headers(); /** * Returns the sequence of headers that have been selected. If no selection has been made, all available headers * will be returned, producing the same output as a call to method {@link #headers()}. * * @return the sequence of selected headers, or all headers if no selection has been made. */ String[] selectedHeaders(); /** * Queries whether a given header name exists in the {@link Record}s parsed from the input * * @param headerName name of the header * * @return {@code true} if the given header name exists in the input records, otherwise {@code false} */ boolean containsColumn(String headerName); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/record/RecordMetaDataImpl.java000066400000000000000000000352121400120543400332210ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.record; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.conversions.*; import java.lang.annotation.*; import java.util.*; class RecordMetaDataImpl implements RecordMetaData { final C context; @SuppressWarnings("rawtypes") private Map conversionByType = new HashMap(); @SuppressWarnings("rawtypes") private Map> conversionsByAnnotation = new HashMap>(); private Map annotationHashes = new HashMap(); private MetaData[] indexMap; private FieldConversionMapping conversions = null; RecordMetaDataImpl(C context) { this.context = context; } private MetaData getMetaData(String name) { int index = context.indexOf(name); if (index == -1) { getValidatedHeaders(); throw new IllegalArgumentException("Header name '" + name + "' not found. Available columns are: " + Arrays.asList(selectedHeaders())); } return getMetaData(index); } private NormalizedString[] getValidatedHeaders() { NormalizedString[] headers = NormalizedString.toIdentifierGroupArray(context.headers()); if (headers == null || headers.length == 0) { throw new IllegalStateException("No headers parsed from input nor provided in the user settings. Only index-based operations are available."); } return headers; } private MetaData getMetaData(Enum column) { NormalizedString[] headers = NormalizedString.toIdentifierGroupArray(context.headers()); if (headers == null || headers.length == 0) { throw new IllegalStateException("No headers parsed from input nor provided in the user settings. Only index-based operations are available."); } return getMetaData(context.indexOf(column)); } public MetaData getMetaData(int index) { if (indexMap == null || indexMap.length < index + 1 || indexMap[index] == null) { synchronized (this) { if (indexMap == null || indexMap.length < index + 1 || indexMap[index] == null) { int startFrom = 0; int lastIndex = index; if (indexMap != null) { startFrom = indexMap.length; indexMap = Arrays.copyOf(indexMap, index + 1); } else { String[] headers = context.headers(); if (headers != null && lastIndex < headers.length) { lastIndex = headers.length; } int[] indexes = context.extractedFieldIndexes(); if (indexes != null) { for (int i = 0; i < indexes.length; i++) { if (lastIndex < indexes[i]) { lastIndex = indexes[i]; } } } indexMap = new MetaData[lastIndex + 1]; } for (int i = startFrom; i < lastIndex + 1; i++) { indexMap[i] = new MetaData(i); } } } } return indexMap[index]; } @Override public int indexOf(Enum column) { return getMetaData(column).index; } MetaData metadataOf(String headerName) { return getMetaData(headerName); } MetaData metadataOf(Enum column) { return getMetaData(column); } MetaData metadataOf(int columnIndex) { return getMetaData(columnIndex); } @Override public int indexOf(String headerName) { return getMetaData(headerName).index; } @Override public Class typeOf(Enum column) { return getMetaData(column).type; } @Override public Class typeOf(String headerName) { return getMetaData(headerName).type; } @Override public Class typeOf(int columnIndex) { return getMetaData(columnIndex).type; } @Override public void setDefaultValueOfColumns(T defaultValue, Enum... columns) { for (Enum column : columns) { getMetaData(column).defaultValue = defaultValue; } } @Override public void setDefaultValueOfColumns(T defaultValue, String... headerNames) { for (String headerName : headerNames) { getMetaData(headerName).defaultValue = defaultValue; } } @Override public void setDefaultValueOfColumns(T defaultValue, int... columnIndexes) { for (int columnIndex : columnIndexes) { getMetaData(columnIndex).defaultValue = defaultValue; } } @Override public Object defaultValueOf(Enum column) { return getMetaData(column).defaultValue; } @Override public Object defaultValueOf(String headerName) { return getMetaData(headerName).defaultValue; } @Override public Object defaultValueOf(int columnIndex) { return getMetaData(columnIndex).defaultValue; } private FieldConversionMapping getConversions() { if (conversions == null) { conversions = new FieldConversionMapping(); } return conversions; } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public > FieldSet convertFields(Class enumType, Conversion... conversions) { return (FieldSet) getConversions().applyConversionsOnFieldEnums(conversions); } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public FieldSet convertFields(Conversion... conversions) { return getConversions().applyConversionsOnFieldNames(conversions); } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public FieldSet convertIndexes(Conversion... conversions) { return getConversions().applyConversionsOnFieldIndexes(conversions); } @Override public String[] headers() { return context.headers(); } @Override public String[] selectedHeaders() { return context.selectedHeaders(); } String getValue(String[] data, String headerName) { MetaData md = metadataOf(headerName); if (md.index >= data.length) { return null; } return data[md.index]; } String getValue(String[] data, int columnIndex) { MetaData md = metadataOf(columnIndex); return data[md.index]; } String getValue(String[] data, Enum column) { MetaData md = metadataOf(column); return data[md.index]; } @SuppressWarnings("rawtypes") private T convert(MetaData md, String[] data, Class expectedType, Conversion[] conversions) { return expectedType.cast(convert(md, data, conversions)); } @SuppressWarnings("rawtypes") private Object convert(MetaData md, String[] data, Object defaultValue, Conversion[] conversions) { Object out = convert(md, data, conversions); return out == null ? defaultValue : out; } @SuppressWarnings({"rawtypes", "unchecked"}) private static Object convert(MetaData md, String[] data, Conversion[] conversions) { Object out = data[md.index]; for (int i = 0; i < conversions.length; i++) { out = conversions[i].execute(out); } return out; } @SuppressWarnings({"rawtypes", "unchecked"}) T getValue(String[] data, String headerName, T defaultValue, Conversion[] conversions) { return (T) convert(metadataOf(headerName), data, defaultValue, conversions); } @SuppressWarnings({"rawtypes", "unchecked"}) T getValue(String[] data, int columnIndex, T defaultValue, Conversion[] conversions) { return (T) convert(metadataOf(columnIndex), data, defaultValue, conversions); } @SuppressWarnings({"rawtypes", "unchecked"}) T getValue(String[] data, Enum column, T defaultValue, Conversion[] conversions) { return (T) convert(metadataOf(column), data, defaultValue, conversions); } @SuppressWarnings("rawtypes") T getValue(String[] data, String headerName, Class expectedType, Conversion[] conversions) { return convert(metadataOf(headerName), data, expectedType, conversions); } @SuppressWarnings("rawtypes") T getValue(String[] data, int columnIndex, Class expectedType, Conversion[] conversions) { return convert(metadataOf(columnIndex), data, expectedType, conversions); } @SuppressWarnings("rawtypes") T getValue(String[] data, Enum column, Class expectedType, Conversion[] conversions) { return convert(metadataOf(column), data, expectedType, conversions); } @SuppressWarnings({"rawtypes", "unchecked"}) private T convert(MetaData md, String[] data, Class type, T defaultValue, Annotation annotation) { Object out = md.index < data.length ? data[md.index] : null; if (out == null) { out = defaultValue == null ? md.defaultValue : defaultValue; } if (annotation == null) { initializeMetadataConversions(data, md); out = md.convert(out); if (out == null) { out = defaultValue == null ? md.defaultValue : defaultValue; } } if (type != null) { if (out != null && type.isAssignableFrom(out.getClass())) { return (T) out; } Conversion conversion; if (annotation == null) { conversion = conversionByType.get(type); if (conversion == null) { conversion = AnnotationHelper.getDefaultConversion(type, null, null); conversionByType.put(type, conversion); } } else { Map m = conversionsByAnnotation.get(type); if (m == null) { m = new HashMap(); conversionsByAnnotation.put(type, m); } conversion = m.get(annotation); if (conversion == null) { conversion = AnnotationHelper.getConversion(type, annotation); m.put(annotation, conversion); } } if (conversion == null) { if(type == String.class){ if(out == null){ return null; } return (T) (md.index < data.length ? data[md.index] : null); } String message = ""; if (type == Date.class || type == Calendar.class) { message = ". Need to specify format for date"; } DataProcessingException exception = new DataProcessingException("Cannot convert '{value}' to " + type.getName() + message); exception.setValue(out); exception.setErrorContentLength(context.errorContentLength()); throw exception; } out = conversion.execute(out); } if (type == null) { return (T) out; } try { return type.cast(out); } catch (ClassCastException e) { DataProcessingException exception = new DataProcessingException("Cannot cast value '{value}' of type " + out.getClass().toString() + " to " + type.getName()); exception.setValue(out); exception.setErrorContentLength(context.errorContentLength()); throw exception; } } private void initializeMetadataConversions(String[] data, MetaData md) { if (conversions != null) { synchronized (this) { String[] headers = headers(); if (headers == null) { headers = data; } conversions.prepareExecution(false, headers); md.setDefaultConversions(conversions.getConversions(md.index, md.type)); } } } T getObjectValue(String[] data, String headerName, Class type, T defaultValue) { return convert(metadataOf(headerName), data, type, defaultValue, null); } T getObjectValue(String[] data, int columnIndex, Class type, T defaultValue) { return convert(metadataOf(columnIndex), data, type, defaultValue, null); } T getObjectValue(String[] data, Enum column, Class type, T defaultValue) { return convert(metadataOf(column), data, type, defaultValue, null); } T getObjectValue(String[] data, String headerName, Class type, T defaultValue, String format, String... formatOptions) { if (format == null) { return getObjectValue(data, headerName, type, defaultValue); } return convert(metadataOf(headerName), data, type, defaultValue, buildAnnotation(type, format, formatOptions)); } T getObjectValue(String[] data, int columnIndex, Class type, T defaultValue, String format, String... formatOptions) { if (format == null) { return getObjectValue(data, columnIndex, type, defaultValue); } return convert(metadataOf(columnIndex), data, type, defaultValue, buildAnnotation(type, format, formatOptions)); } T getObjectValue(String[] data, Enum column, Class type, T defaultValue, String format, String... formatOptions) { if (format == null) { return getObjectValue(data, column, type, defaultValue); } return convert(metadataOf(column), data, type, defaultValue, buildAnnotation(type, format, formatOptions)); } static Annotation buildBooleanStringAnnotation(final String[] trueStrings, final String[] falseStrings) { return new com.univocity.parsers.annotations.BooleanString() { @Override public String[] trueStrings() { return trueStrings == null ? ArgumentUtils.EMPTY_STRING_ARRAY : trueStrings; } @Override public String[] falseStrings() { return falseStrings == null ? ArgumentUtils.EMPTY_STRING_ARRAY : falseStrings; } @Override public Class annotationType() { return com.univocity.parsers.annotations.BooleanString.class; } }; } private static Annotation newFormatAnnotation(final String format, final String... formatOptions) { return new com.univocity.parsers.annotations.Format() { @Override public String[] formats() { return new String[]{format}; } @Override public String[] options() { return formatOptions; } @Override public Class annotationType() { return com.univocity.parsers.annotations.Format.class; } }; } Annotation buildAnnotation(Class type, final String args1, final String... args2) { Integer hash = (type.hashCode() * 31) + String.valueOf(args1).hashCode() + (31 * Arrays.toString(args2).hashCode()); Annotation out = annotationHashes.get(hash); if (out == null) { if (type == Boolean.class || type == boolean.class) { out = buildBooleanStringAnnotation(args1 == null ? null : new String[]{args1}, args2); } else { out = newFormatAnnotation(args1, args2); } annotationHashes.put(hash, out); } return out; } @SuppressWarnings("rawtypes") @Override public void setTypeOfColumns(Class type, Enum... columns) { for (int i = 0; i < columns.length; i++) { getMetaData(columns[i]).type = type; } } @Override public void setTypeOfColumns(Class type, String... headerNames) { for (int i = 0; i < headerNames.length; i++) { getMetaData(headerNames[i]).type = type; } } @Override public void setTypeOfColumns(Class type, int... columnIndexes) { for (int i = 0; i < columnIndexes.length; i++) { getMetaData(columnIndexes[i]).type = type; } } @Override public boolean containsColumn(String headerName) { if (headerName == null) { return false; } return context.indexOf(headerName) != -1; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/routine/000077500000000000000000000000001400120543400271215ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/routine/AbstractRoutines.java000066400000000000000000001114041400120543400332610ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.routine; import com.univocity.parsers.annotations.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import com.univocity.parsers.common.processor.*; import java.io.*; import java.nio.charset.*; import java.sql.*; import java.util.*; /** * Basic implementation of commonly used routines around parsing/writing of data that can be reused and extended * by parsers/writers of any supported format. * * @param

parser configuration class * @param writer configuration class */ public abstract class AbstractRoutines

, W extends CommonWriterSettings> { private boolean keepResourcesOpen = false; private Writer previousOutput; private ColumnMapping columnMapper = new ColumnMapping(); /** * Creates a new parser implementation using the given parser configuration * * @param parserSettings the configuration for new parser * * @return a parser implementation configured according to the given settings object. */ protected abstract AbstractParser

createParser(P parserSettings); /** * Creates a new writer implementation using the given writer configuration * * @param output target output of the routine. * @param writerSettings the configuration for new writer * * @return a writer implementation configured according to the given settings object. */ protected abstract AbstractWriter createWriter(Writer output, W writerSettings); /** * Creates a default parser settings configuration * * @return a new instance of a usable parser configuration. */ protected abstract P createDefaultParserSettings(); /** * Creates a default writer settings configuration * * @return a new instance of a usable writer configuration. */ protected abstract W createDefaultWriterSettings(); private final String routineDescription; private P parserSettings; private W writerSettings; /** * Creates a new instance of this routine class. * * @param routineDescription description of the routines for a given format */ public AbstractRoutines(String routineDescription) { this(routineDescription, null, null); } /** * Creates a new instance of this routine class. * * @param routineDescription description of the routines for a given format * @param parserSettings configuration to use for parsing */ public AbstractRoutines(String routineDescription, P parserSettings) { this(routineDescription, parserSettings, null); } /** * Creates a new instance of this routine class. * * @param routineDescription description of the routines for a given format * @param writerSettings configuration to use for writing */ public AbstractRoutines(String routineDescription, W writerSettings) { this(routineDescription, null, writerSettings); } /** * Creates a new instance of this routine class. * * @param routineDescription description of the routines for a given format * @param parserSettings configuration to use for parsing * @param writerSettings configuration to use for writing */ public AbstractRoutines(String routineDescription, P parserSettings, W writerSettings) { this.routineDescription = routineDescription; this.parserSettings = parserSettings; this.writerSettings = writerSettings; } private void validateWriterSettings() { if (writerSettings == null) { writerSettings = createDefaultWriterSettings(); } } private void validateParserSettings() { if (parserSettings == null) { parserSettings = createDefaultParserSettings(); parserSettings.setLineSeparatorDetectionEnabled(true); } } /** * Returns the parser configuration (if any) used by the routines of this utility class. * * @return the parser configuration. */ public final P getParserSettings() { validateParserSettings(); return parserSettings; } /** * Defines the parser configuration to be used by the routines of this utility class. * * @param parserSettings the parser configuration. */ public final void setParserSettings(P parserSettings) { this.parserSettings = parserSettings; } /** * Returns the writer configuration (if any) used by the routines of this utility class. * * @return the writer configuration. */ public final W getWriterSettings() { validateWriterSettings(); return writerSettings; } /** * Defines the writer configuration to be used by the routines of this utility class. * * @param writerSettings the parser configuration. */ public final void setWriterSettings(W writerSettings) { this.writerSettings = writerSettings; } /** * Allows writers of any given format to adjust its settings to take into account column headers and lengths * prior to writing data in any routine. * * @param headers headers to be written * @param lengths the corresponding lengths of each header */ protected void adjustColumnLengths(String[] headers, int[] lengths) { } /** * Dumps the content of a {@link java.sql.ResultSet} into a file. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output file that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. */ public final void write(ResultSet rs, File output) { write(rs, output, (Charset) null); } /** * Dumps the content of a {@link java.sql.ResultSet} into a file. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output file that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. * @param encoding the output encoding of the file */ public final void write(ResultSet rs, File output, String encoding) { write(rs, output, Charset.forName(encoding)); } /** * Dumps the content of a {@link java.sql.ResultSet} into a file. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output file that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. * @param encoding the output encoding of the file */ public final void write(ResultSet rs, File output, Charset encoding) { Writer writer = ArgumentUtils.newWriter(output, encoding); try { write(rs, writer); } finally { try { writer.close(); } catch (Exception e) { throw new IllegalStateException("Error closing file: '" + output.getAbsolutePath() + "'", e); } } } /** * Dumps the content of a {@link java.sql.ResultSet} into an output stream. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output stream that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. */ public final void write(ResultSet rs, OutputStream output) { write(rs, ArgumentUtils.newWriter(output)); } /** * Dumps the content of a {@link java.sql.ResultSet} into an output stream. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output file that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. * @param encoding the output encoding of the output stream */ public final void write(ResultSet rs, OutputStream output, String encoding) { write(rs, ArgumentUtils.newWriter(output, encoding)); } /** * Dumps the content of a {@link java.sql.ResultSet} into an output stream. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output file that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. * @param encoding the output encoding of the output stream */ public final void write(ResultSet rs, OutputStream output, Charset encoding) { write(rs, ArgumentUtils.newWriter(output, encoding)); } /** * Dumps the content of a {@link java.sql.ResultSet}. * * @param rs the {@link java.sql.ResultSet} whose contents should be read and written to a given output * @param output the output that will store the data in the given {@link java.sql.ResultSet} * in the format specified by concrete implementations of this class. */ public final void write(ResultSet rs, Writer output) { validateWriterSettings(); boolean hasWriterProcessor = writerSettings.getRowWriterProcessor() != null; AbstractWriter writer = null; long rowCount = 0L; Object[] row = null; try { try { ResultSetMetaData md = rs.getMetaData(); int columns = md.getColumnCount(); String[] headers = new String[columns]; int[] lengths = new int[columns]; for (int i = 1; i <= columns; i++) { headers[i - 1] = md.getColumnLabel(i); int precision = md.getPrecision(i); int scale = md.getScale(i); int length; if (precision != 0 && scale != 0) { length = precision + scale + 2; //+2 to account for decimal point (non-integer numbers) and minus characters (negative numbers). } else { length = precision + scale; } lengths[i - 1] = length; } String[] userProvidedHeaders = writerSettings.getHeaders(); if (userProvidedHeaders == null) { writerSettings.setHeaders(headers); } else { headers = userProvidedHeaders; } adjustColumnLengths(headers, lengths); writer = createWriter(output, writerSettings); if (writerSettings.isHeaderWritingEnabled()) { writer.writeHeaders(); } row = new Object[columns]; while (rs.next()) { for (int i = 1; i <= columns; i++) { row[i - 1] = rs.getObject(i); } if (hasWriterProcessor) { writer.processRecord(row); } else { writer.writeRow(row); } rowCount++; } } finally { if (!keepResourcesOpen) { rs.close(); } } } catch (Exception e) { throw new TextWritingException("Error writing data from result set", rowCount, row, e); } finally { close(writer); } } /** * Reads all data from a given input and writes it to an output. * * @param input the input data to be parsed using the settings provided in {@link #getParserSettings()} * @param output the output into where the input data should be written, using the format provided in {@link #getWriterSettings()} */ public final void parseAndWrite(Reader input, Writer output) { setRowWriterProcessor(null); setRowProcessor(createWritingRowProcessor(output)); try { AbstractParser

parser = createParser(parserSettings); parser.parse(input); } finally { parserSettings.setRowProcessor(null); } } private void setRowWriterProcessor(RowWriterProcessor rowWriterProcessor) { validateWriterSettings(); writerSettings.setRowWriterProcessor(rowWriterProcessor); } private void setRowProcessor(RowProcessor rowProcessor) { validateParserSettings(); parserSettings.setRowProcessor(rowProcessor); } private RowProcessor createWritingRowProcessor(final Writer output) { return new RowProcessor() { private AbstractWriter writer; @Override public void processStarted(ParsingContext context) { writer = createWriter(output, writerSettings); } @Override public void rowProcessed(String[] row, ParsingContext context) { writer.writeRow(row); } @Override public void processEnded(ParsingContext context) { close(writer); } }; } private void close(AbstractWriter writer) { if (writer != null) { if (!keepResourcesOpen) { writer.close(); } else { writer.flush(); } } } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, File output, String... headers) { writeAll(elements, beanType, ArgumentUtils.newWriter(output), headers); } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param encoding the output encoding to use for writing * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, File output, String encoding, String[] headers) { writeAll(elements, beanType, ArgumentUtils.newWriter(output, encoding), headers); } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param encoding the output encoding to use for writing * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, File output, Charset encoding, String... headers) { writeAll(elements, beanType, ArgumentUtils.newWriter(output, encoding), headers); } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, OutputStream output, String... headers) { writeAll(elements, beanType, ArgumentUtils.newWriter(output), headers); } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param encoding the output encoding to use for writing * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, OutputStream output, String encoding, String[] headers) { writeAll(elements, beanType, ArgumentUtils.newWriter(output, encoding), headers); } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param encoding the output encoding to use for writing * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, OutputStream output, Charset encoding, String... headers) { writeAll(elements, beanType, ArgumentUtils.newWriter(output, encoding), headers); } /** * Writes a collection of annotated java beans to a given output. * * @param elements the elements to write to the output * @param beanType the type of element in the given collection * @param output the output into which the given elements will be written * @param headers headers to use in the first row of the written result. * @param the type of element in the given collection */ public void writeAll(Iterable elements, Class beanType, Writer output, String... headers) { BeanWriterProcessor processor = new BeanWriterProcessor(beanType); processor.setColumnMapper(columnMapper); setRowWriterProcessor(processor); try { if (headers.length > 0) { writerSettings.setHeaders(headers); writerSettings.setHeaderWritingEnabled(true); } if (keepResourcesOpen && previousOutput == output) { writerSettings.setHeaderWritingEnabled(false); } AbstractWriter writer = createWriter(output, writerSettings); if (keepResourcesOpen) { writer.processRecords(elements); previousOutput = output; } else { writer.processRecordsAndClose(elements); } } finally { writerSettings.setRowWriterProcessor(null); } } /** * Parses a file into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public List parseAll(final Class beanType, final File input, int expectedBeanCount) { return parseAll(beanType, ArgumentUtils.newReader(input), expectedBeanCount); } /** * Parses a file into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param encoding encoding of the given file * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public List parseAll(final Class beanType, final File input, String encoding, int expectedBeanCount) { return parseAll(beanType, ArgumentUtils.newReader(input, encoding), expectedBeanCount); } /** * Parses a file into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param encoding encoding of the given file * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final File input, Charset encoding, int expectedBeanCount) { return parseAll(beanType, ArgumentUtils.newReader(input, encoding), expectedBeanCount); } /** * Parses an input stream into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final InputStream input, int expectedBeanCount) { return parseAll(beanType, ArgumentUtils.newReader(input), expectedBeanCount); } /** * Parses an input stream into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param encoding encoding of the given input stream * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final InputStream input, String encoding, int expectedBeanCount) { return parseAll(beanType, ArgumentUtils.newReader(input, encoding), expectedBeanCount); } /** * Parses an input stream into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param encoding encoding of the given input stream * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final InputStream input, Charset encoding, int expectedBeanCount) { return parseAll(beanType, ArgumentUtils.newReader(input, encoding), expectedBeanCount); } /** * Parses the input into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input to be parsed * @param expectedBeanCount expected number of rows to be parsed from the input which will be converted into java beans. * Used to pre-allocate the size of the output {@link List} * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(Class beanType, Reader input, int expectedBeanCount) { BeanListProcessor processor = new BeanListProcessor(beanType, expectedBeanCount); processor.setColumnMapper(columnMapper); setRowProcessor(processor); try { createParser(parserSettings).parse(input); return processor.getBeans(); } finally { parserSettings.setRowProcessor(null); } } /** * Parses a file into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public List parseAll(final Class beanType, final File input) { return parseAll(beanType, input, 0); } /** * Parses a file into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param encoding encoding of the given file * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public List parseAll(final Class beanType, final File input, String encoding) { return parseAll(beanType, input, encoding, 0); } /** * Parses a file into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param encoding encoding of the given file * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final File input, Charset encoding) { return parseAll(beanType, input, encoding, 0); } /** * Parses an input stream into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final InputStream input) { return parseAll(beanType, input, 0); } /** * Parses an input stream into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param encoding encoding of the given input stream * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final InputStream input, String encoding) { return parseAll(beanType, input, encoding, 0); } /** * Parses an input stream into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param encoding encoding of the given input stream * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(final Class beanType, final InputStream input, Charset encoding) { return parseAll(beanType, input, encoding, 0); } /** * Parses the input into a list of annotated java beans * * @param beanType the type of java beans to be instantiated. * @param input the input to be parsed * @param the type of java beans to be instantiated. * * @return a list containing all java beans read from the input. */ public List parseAll(Class beanType, Reader input) { return parseAll(beanType, input, 0); } /** * Iterates over a file to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final File input) { return iterate(beanType, ArgumentUtils.newReader(input)); } /** * Iterates over a file to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param encoding encoding of the given file * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final File input, String encoding) { return iterate(beanType, ArgumentUtils.newReader(input, encoding)); } /** * Iterates over a file to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the file to be parsed * @param encoding encoding of the given file * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final File input, Charset encoding) { return iterate(beanType, ArgumentUtils.newReader(input, encoding)); } /** * Iterates over an input stream to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final InputStream input) { return iterate(beanType, ArgumentUtils.newReader(input)); } /** * Iterates over an input stream to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param encoding encoding of the given input stream * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final InputStream input, String encoding) { return iterate(beanType, ArgumentUtils.newReader(input, encoding)); } /** * Iterates over an input stream to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the input stream to be parsed * @param encoding encoding of the given input stream * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final InputStream input, Charset encoding) { return iterate(beanType, ArgumentUtils.newReader(input, encoding)); } /** * Iterates over an input to produce instances of annotated java beans on demand. * * @param beanType the type of java beans to be instantiated. * @param input the input to be parsed * @param the type of java beans to be instantiated. * * @return an {@link Iterable} that allows iterating over the input and producing instances of java beans on demand. */ public IterableResult iterate(final Class beanType, final Reader input) { final Object[] beanHolder = new Object[1]; BeanProcessor processor = new BeanProcessor(beanType) { @Override public void beanProcessed(T bean, ParsingContext context) { beanHolder[0] = bean; } @Override public void processEnded(ParsingContext context) { super.processEnded(context); parserSettings.setRowProcessor(null); } }; processor.setColumnMapper(columnMapper); setRowProcessor(processor); return new IterableResult() { private ParsingContext context; @Override public ParsingContext getContext() { return context; } @Override public ResultIterator iterator() { final AbstractParser

parser = createParser(parserSettings); parser.beginParsing(input); context = parser.getContext(); return new ResultIterator() { String[] row; @Override public boolean hasNext() { return beanHolder[0] != null || row != null || (row = parser.parseNext()) != null; } @Override public T next() { T out = (T) beanHolder[0]; if (out == null && hasNext()) { out = (T) beanHolder[0]; } beanHolder[0] = null; row = null; return out; } @Override public void remove() { throw new UnsupportedOperationException("Can't remove beans"); } @Override public ParsingContext getContext() { return context; } }; } }; } @Override public String toString() { return routineDescription; } /** * Calculates the dimensions of a file (row and column count). * * @param input the file to be parsed * * @return a {@link InputDimension} with information about the dimensions of the given input. */ public InputDimension getInputDimension(final File input) { return getInputDimension(ArgumentUtils.newReader(input)); } /** * Calculates the dimensions of a file (row and column count). * * @param input the file to be parsed * @param encoding encoding of the given file * * @return a {@link InputDimension} with information about the dimensions of the given input. */ public InputDimension getInputDimension(final File input, String encoding) { return getInputDimension(ArgumentUtils.newReader(input, encoding)); } /** * Calculates the dimensions of a given input (row and column count). * * @param input the input to be parsed * * @return a {@link InputDimension} with information about the dimensions of the given input. */ public InputDimension getInputDimension(final InputStream input) { return getInputDimension(ArgumentUtils.newReader(input)); } /** * Calculates the dimensions of a given input (row and column count). * * @param input the input to be parsed * @param encoding encoding of the given input * * @return a {@link InputDimension} with information about the dimensions of the given input. */ public InputDimension getInputDimension(final InputStream input, String encoding) { return getInputDimension(ArgumentUtils.newReader(input, encoding)); } /** * Calculates the dimensions of a given input (row and column count). * * @param input the input to be parsed * * @return a {@link InputDimension} with information about the dimensions of the given input. */ public InputDimension getInputDimension(Reader input) { final InputDimension out = new InputDimension(); setRowProcessor(new AbstractRowProcessor() { int lastColumn; @Override public void rowProcessed(String[] row, ParsingContext context) { if (lastColumn < row.length) { lastColumn = row.length; } } @Override public void processEnded(ParsingContext context) { out.rows = context.currentRecord() + 1; out.columns = lastColumn; } }); P settings = getParserSettings(); settings.setMaxCharsPerColumn(-1); if (settings.getMaxColumns() < 1000000) { //one million columns should be more than enough. settings.setMaxColumns(1000000); } //The parser will return values for the columns selected. //By selecting no indexes here, no String objects will be created settings.selectIndexes(/*nothing here*/); //By disabling column reordering, we get the original row, with nulls in the columns as nothing was selected. settings.setColumnReorderingEnabled(false); createParser(settings).parse(input); return out; } /** * Returns a flag indicating whether resources used for writing should be kept open after being * used by the routines available from this object, when applicable. * * @return flag indicating whether to call the {@code close()} (or any other cleanup method) * after a routine executes. */ public boolean getKeepResourcesOpen() { return keepResourcesOpen; } /** * Allows preventing resources used for writing from being closed after being * used by the routines available from this object, when applicable. * * @param keepResourcesOpen flag indicating whether to call the {@code close()} (or any other cleanup method) * after a routine executes. */ public void setKeepResourcesOpen(boolean keepResourcesOpen) { this.keepResourcesOpen = keepResourcesOpen; } /** * Returns a mapper that allows users to manually define mappings from * attributes/methods of a given class to columns to be parsed or written * in routines that manipulate java beans. * * This allows users to use instances of classes that are not annotated with {@link Parsed} nor * {@link Nested}. Any mappings defined with the column mapper will take * precedence over these annotations. * * @return the column mapper */ public ColumnMapper getColumnMapper() { return columnMapper; } /** * Copies the given column mappings over to this processor. Further changes * to the given object won't be reflected on the copy stored internally. * * @param columnMapper the column mappings to use */ public void setColumnMapper(ColumnMapper columnMapper) { this.columnMapper = columnMapper == null ? new ColumnMapping() : (ColumnMapping) columnMapper.clone(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/common/routine/InputDimension.java000066400000000000000000000030041400120543400327260ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.routine; import java.io.*; /** * A simple class to hold information about the dimensions of a given input, which are calculated using * {@link AbstractRoutines#getInputDimension(File)} * * @author Univocity Software Pty Ltd - dev@univocity.com */ public final class InputDimension { long rows; int columns; InputDimension() { } /** * Returns the total number of rows the input contains. * * @return the number of rows found in the input. */ public final long rowCount() { return rows; } /** * Returns the maximum number of column the input contains. * * @return the number of columns found in the input. */ public final int columnCount() { return columns; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/000077500000000000000000000000001400120543400265145ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/BigDecimalConversion.java000066400000000000000000000037771400120543400334230ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import java.math.*; /** * Converts Strings to BigDecimals and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class BigDecimalConversion extends ObjectConversion { /** * Creates a Conversion from String to BigDecimal with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public BigDecimalConversion() { super(); } /** * Creates a Conversion from String to BigDecimal with default values to return when the input is null. * @param valueIfStringIsNull default BigDecimal value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a BigDecimal input is null. Used when {@code revert(BigDecimal)} is invoked. */ public BigDecimalConversion(BigDecimal valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to BigDecimal. */ @Override protected BigDecimal fromString(String input) { return new BigDecimal(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/BigIntegerConversion.java000066400000000000000000000037771400120543400334620ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import java.math.*; /** * Converts Strings to BigIntegers and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class BigIntegerConversion extends ObjectConversion { /** * Creates a Conversion from String to BigInteger with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public BigIntegerConversion() { super(); } /** * Creates a Conversion from String to BigInteger with default values to return when the input is null. * @param valueIfStringIsNull default BigInteger value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a BigInteger input is null. Used when {@code revert(BigInteger)} is invoked. */ public BigIntegerConversion(BigInteger valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to BigInteger. */ @Override protected BigInteger fromString(String input) { return new BigInteger(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/BooleanConversion.java000066400000000000000000000175251400120543400330160ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; import java.util.*; /** * Converts Strings to Booleans and vice versa * *

This class supports multiple representations of boolean values. For example, you can define conversions from different Strings such as "Yes, Y, 1" to true, and * "No, N, 0" to false. * *

The reverse conversion from a Boolean to String (in {@link BooleanConversion#revert(Boolean)} will return the first String provided in this class constructor *

Using the previous example, a call to {@code revert(true)} will produce "Yes" and a call {@code revert(false)} will produce "No". * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class BooleanConversion extends ObjectConversion { private String defaultForTrue; private String defaultForFalse; private final Set falseValues = new LinkedHashSet(); private final Set trueValues = new LinkedHashSet(); /** * Creates conversions from String to Boolean. * This default constructor assumes the output of a conversion should be null when input is null *

The list of Strings that identify "true" the list of Strings that identify "false" are mandatory. * * @param valuesForTrue Strings that identify the boolean value true. The first element will be returned when executing {@code revert(true)} * @param valuesForFalse Strings that identify the boolean value false. The first element will be returned when executing {@code #revert(false)} */ public BooleanConversion(String[] valuesForTrue, String[] valuesForFalse) { this(null, null, valuesForTrue, valuesForFalse); } /** * Creates a Conversion from String to Boolean with default values to return when the input is null. *

The list of Strings that identify "true" the list of Strings that identify "false" are mandatory. * * @param valueIfStringIsNull default Boolean value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Boolean input is null. Used when {@link BooleanConversion#revert(Boolean)} is invoked. * @param valuesForTrue Strings that identify the boolean value true. The first element will be returned when executing {@code revert(true)} * @param valuesForFalse Strings that identify the boolean value false. The first element will be returned when executing {@code #revert(false)} */ public BooleanConversion(Boolean valueIfStringIsNull, String valueIfObjectIsNull, String[] valuesForTrue, String[] valuesForFalse) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.notEmpty("Values for true", valuesForTrue); ArgumentUtils.notEmpty("Values for false", valuesForFalse); Collections.addAll(falseValues, valuesForFalse); Collections.addAll(trueValues, valuesForTrue); normalize(falseValues); normalize(trueValues); for (String falseValue : falseValues) { if (trueValues.contains(falseValue)) { throw new DataProcessingException("Ambiguous string representation for both false and true values: '" + falseValue + '\''); } } defaultForTrue = valuesForTrue[0]; defaultForFalse = valuesForFalse[0]; } /** * Converts a Boolean back to a String *

The return value depends on the list of values for true/false provided in the constructor of this class. * * @param input the Boolean to be converted to a String * * @return a String representation for this boolean value, or the value of {@link BooleanConversion#getValueIfObjectIsNull()} if the Boolean input is null. */ @Override public String revert(Boolean input) { if (input != null) { if (Boolean.FALSE.equals(input)) { return defaultForFalse; } if (Boolean.TRUE.equals(input)) { return defaultForTrue; } } return getValueIfObjectIsNull(); } /** * Converts a String to a Boolean * * @param input a String to be converted into a Boolean value. * * @return true if the input String is part of {@link BooleanConversion#trueValues}, false if the input String is part of {@link BooleanConversion#falseValues}, or {@link BooleanConversion#getValueIfStringIsNull()} if the input String is null. */ @Override protected Boolean fromString(String input) { if (input != null) { return getBoolean(input, trueValues, falseValues); } return super.getValueIfStringIsNull(); } /** * Returns the {@code Boolean} value represented by a {@code String}, as defined by sets of Strings that represent {@code true} and {@code false} values. * @param booleanString the value that represents either {@code true} or {@code false} * @param trueValues a set of possible string values that represent {@code true}. If empty, then "true" will be assumed as the only acceptable representation. * @param falseValues a set of possible string values that represent {@code false}. If empty, then "false" will be assumed as the only acceptable representation. * @return the boolean value that the input string represents * @throws DataProcessingException if the input string does not match any value provided in neither set of possible values. */ public static Boolean getBoolean(String booleanString, String[] trueValues, String[] falseValues) { trueValues = trueValues == null || trueValues.length == 0 ? new String[]{"true"} : trueValues; falseValues = falseValues == null || falseValues.length == 0 ? new String[]{"false"} : falseValues; BooleanConversion tmp = new BooleanConversion(trueValues, falseValues); return getBoolean(booleanString, tmp.trueValues, tmp.falseValues); } private static Boolean getBoolean(String defaultString, Set trueValues, Set falseValues) { String normalized = normalize(defaultString); if (falseValues.contains(normalized)) { return Boolean.FALSE; } if (trueValues.contains(normalized)) { return Boolean.TRUE; } DataProcessingException exception = new DataProcessingException("Unable to convert '{value}' to Boolean. Allowed Strings are: " + trueValues + " for true; and " + falseValues + " for false."); exception.setValue(defaultString); throw exception; } /** * Normalizes a given String by trimming whitespaces and converting it to lower case. * * @param string a String to be normalized. * * @return the normalized version of the original String. */ private static String normalize(String string) { if (string == null) { return null; } return string.trim().toLowerCase(); } /** * Normalizes the Strings in a given collection by trimming all elements and converting them to lower case. * * @param strings a String collection with elements to be normalized. The original contents of the collection will be modified. */ private static void normalize(Collection strings) { LinkedHashSet normalized = new LinkedHashSet(strings.size()); for (String string : strings) { if (string == null) { normalized.add(null); } else { normalized.add(string.trim().toLowerCase()); } } strings.clear(); strings.addAll(normalized); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/ByteConversion.java000066400000000000000000000036321400120543400323340ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts Strings to Bytes and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ByteConversion extends ObjectConversion { /** * Creates a Conversion from String to Byte with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public ByteConversion() { super(); } /** * Creates a Conversion from String to Byte with default values to return when the input is null. * @param valueIfStringIsNull default Byte value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Byte input is null. Used when {@code revert(Byte)} is invoked. */ public ByteConversion(Byte valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to Byte. */ @Override protected Byte fromString(String input) { return Byte.valueOf(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/CalendarConversion.java000066400000000000000000000170541400120543400331450ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; import java.text.*; import java.util.*; /** * Converts Strings to instances of {@link java.util.Calendar} and vice versa. * *

This class supports multiple date formats. For example, you can define conversions from dates represented by different Strings such as "2001/05/02 and Dec/2013". * *

The reverse conversion from a Calendar to String (in {@link CalendarConversion#revert(Calendar)} will return a formatted String using the date pattern provided in this class constructor *

The date patterns must follows the pattern rules of {@link java.text.SimpleDateFormat} * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see java.text.SimpleDateFormat */ public class CalendarConversion extends ObjectConversion implements FormattedConversion { private final DateConversion dateConversion; /** * Defines a conversion from String to {@link java.util.Calendar} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param timeZone the {@link TimeZone} of the given calendar date * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param valueIfStringIsNull default Calendar value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Calendar input is null. Used when {@link CalendarConversion#revert(Calendar)} is invoked. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. */ public CalendarConversion(TimeZone timeZone, Locale locale, Calendar valueIfStringIsNull, String valueIfObjectIsNull, String... dateFormats) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.noNulls("Date formats", dateFormats); dateConversion = new DateConversion(locale, dateFormats); } /** * Defines a conversion from String to {@link java.util.Calendar} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param valueIfStringIsNull default Calendar value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Calendar input is null. Used when {@link CalendarConversion#revert(Calendar)} is invoked. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. */ public CalendarConversion(Locale locale, Calendar valueIfStringIsNull, String valueIfObjectIsNull, String... dateFormats) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.noNulls("Date formats", dateFormats); dateConversion = new DateConversion(locale, dateFormats); } /** * Defines a conversion from String to {@link java.util.Calendar} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param valueIfStringIsNull default Calendar value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Calendar input is null. Used when {@link CalendarConversion#revert(Calendar)} is invoked. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. */ public CalendarConversion(Calendar valueIfStringIsNull, String valueIfObjectIsNull, String... dateFormats) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.noNulls("Date formats", dateFormats); dateConversion = new DateConversion(Locale.getDefault(), dateFormats); } /** * Defines a conversion from String to {@link java.util.Calendar} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. */ public CalendarConversion(Locale locale, String... dateFormats) { this(locale, null, null, dateFormats); } /** * Defines a conversion from String to {@link java.util.Calendar} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. */ public CalendarConversion(String... dateFormats) { this(Locale.getDefault(), null, null, dateFormats); } /** * Converts Calendar to a formatted date String. *

The pattern used to generate the formatted date is the first date pattern provided in the constructor of this class * * @param input the Calendar to be converted to a String * * @return a formatted date String representing the date provided by the given Calendar, or the value of {@link CalendarConversion#getValueIfObjectIsNull()} if the Calendar parameter is null. */ @Override public String revert(Calendar input) { if (input == null) { return super.revert(null); } return dateConversion.revert(input.getTime()); } /** * Converts a formatted date String to an instance of Calendar. *

The pattern in the formatted date must match one of the date patterns provided in the constructor of this class. * * @param input the String containing a formatted date which must be converted to a Calendar * * @return the Calendar instance containing the date information represented by the given String, or the value of {@link CalendarConversion#getValueIfStringIsNull()} if the String input is null. */ @Override protected Calendar fromString(String input) { Date date = dateConversion.execute(input); Calendar out = Calendar.getInstance(); out.setTime(date); out.setTimeZone(dateConversion.getTimeZone()); return out; } @Override public SimpleDateFormat[] getFormatterObjects() { return dateConversion.getFormatterObjects(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/CharacterConversion.java000066400000000000000000000044661400120543400333330ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; /** * Converts Strings to Characters and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class CharacterConversion extends ObjectConversion { /** * Creates a Conversion from String to Character with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public CharacterConversion() { super(); } /** * Creates a Conversion from String to Character with default values to return when the input is null. * @param valueIfStringIsNull default Character value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Character input is null. Used when {@code revert(Character)} is invoked. */ public CharacterConversion(Character valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to a Character. * @throws IllegalArgumentException if the input String length is not equal to 1, then an IllegalArgumentException is thrown. */ @Override protected Character fromString(String input) { if (input.length() != 1) { DataProcessingException exception = new DataProcessingException("'{value}' is not a character"); exception.setValue(input); throw exception; } return input.charAt(0); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/Conversion.java000066400000000000000000000062131400120543400315060ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.processor.*; /** * The interface that defines the conversion from one type of value to another, and vice versa. * * univocity-parsers provides a set of default conversions for usage with * {@link ObjectRowProcessor} and {@link ObjectRowWriterProcessor}. * * Annotations in package {@link com.univocity.parsers.annotations} are associated with different Conversion * implementations in {@link com.univocity.parsers.conversions}. * * @param The input type to be converted to the output type O * @param The type of outputs produced by a conversion applied to the an input I. * * When used in conjunction with the {@link com.univocity.parsers.annotations.Convert} annotation, * O should be compatible with the type of the annotated field. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.processor.ObjectRowProcessor * @see com.univocity.parsers.common.processor.ObjectRowWriterProcessor */ public interface Conversion { /** * Converts a value of type I to a value of type O. * * When used in conjunction with the {@link com.univocity.parsers.annotations.Convert} annotation, this method will perform * the conversion from a parsed input {@code String} (if no other conversion has been applied before) * to a value of the desired type, and the result will be assigned to the annotated field. Note that conversions can be * chained so you need to make sure the input type of any previous conversion is compatible with I * * @param input the input of type I to be converted to an object of type O * * @return the conversion result. */ O execute(I input); /** * Converts a value of type O to a value of type I. * * When used in conjunction with the {@link com.univocity.parsers.annotations.Convert} annotation, this method will * convert the value of the annotated field so it can be written to the output (usually a {@code String}). Note that conversions can be * chained so you need to make sure the type of any previous conversion is compatible with O * * @param input the input of type O to be converted to an object of type I * * @return the conversion result. */ I revert(O input); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/Conversions.java000066400000000000000000001023141400120543400316700ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import java.math.*; import java.text.*; import java.util.*; /** * This class provides default instances of common implementations if {@code com.univocity.parsers.conversions.Conversion}, as well as useful methods for obtaining new instances of these. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class Conversions { private Conversions() { } private static final UpperCaseConversion upperCase = new UpperCaseConversion(); private static final LowerCaseConversion lowerCase = new LowerCaseConversion(); private static final TrimConversion trim = new TrimConversion(); private static final ToStringConversion toString = new ToStringConversion(); /** * Returns a singleton instance of {@link ToStringConversion} * * @return a singleton instance of {@link ToStringConversion} */ public static ToStringConversion string() { return toString; } /** * Returns a singleton instance of {@link UpperCaseConversion} * * @return a singleton instance of {@link UpperCaseConversion} */ public static UpperCaseConversion toUpperCase() { return upperCase; } /** * Returns a singleton instance of {@link LowerCaseConversion} * * @return a singleton instance of {@link LowerCaseConversion} */ public static LowerCaseConversion toLowerCase() { return lowerCase; } /** * Returns a singleton instance of {@link TrimConversion} * * @return a singleton instance of {@link TrimConversion} */ public static TrimConversion trim() { return trim; } /** * Returns a {@link TrimConversion} that limits the output to a fixed length * * @param length the maximum length a value can contain. Characters after this limit will * be discarded. * * @return a trim-to-length conversion */ public static TrimConversion trim(int length) { return new TrimConversion(length); } /** * Returns a new instance of {@link RegexConversion} * * @param replaceRegex the regular expression used to match contents of a given input String * @param replacement the replacement content to replace any contents matched by the given regular expression * * @return the new instance of {@link RegexConversion} created with the given parameters. */ public static RegexConversion replace(String replaceRegex, String replacement) { return new RegexConversion(replaceRegex, replacement); } /** * Returns a new instance of {@link NullStringConversion} * * @param nullRepresentations the sequence of Strings that represent a null value. * * @return the new instance of {@link NullStringConversion} created with the given parameters. */ public static NullStringConversion toNull(String... nullRepresentations) { return new NullStringConversion(nullRepresentations); } /** * Returns a new instance of {@link DateConversion} * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(Locale locale, String... dateFormats) { return new DateConversion(locale, dateFormats); } /** * Returns a new instance of {@link DateConversion} * * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(String... dateFormats) { return new DateConversion(Locale.getDefault(), dateFormats); } /** * Returns a new instance of {@link DateConversion} * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateIfNull default Date value to be returned when the input String is null. Used when {@link DateConversion#execute(String)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(Locale locale, Date dateIfNull, String... dateFormats) { return new DateConversion(locale, dateIfNull, null, dateFormats); } /** * Returns a new instance of {@link DateConversion} * * @param dateIfNull default Date value to be returned when the input String is null. Used when {@link DateConversion#execute(String)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(Date dateIfNull, String... dateFormats) { return new DateConversion(Locale.getDefault(), dateIfNull, null, dateFormats); } /** * Returns a new instance of {@link DateConversion} * * @param timeZone the {@link TimeZone} of the date to be formatted * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateIfNull default Date value to be returned when the input String is null. Used when {@link DateConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a Date input is null. Used when {@link DateConversion#revert(Date)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(TimeZone timeZone, Locale locale, Date dateIfNull, String stringIfNull, String... dateFormats) { return new DateConversion(timeZone, locale, dateIfNull, stringIfNull, dateFormats); } /** * Returns a new instance of {@link DateConversion} * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateIfNull default Date value to be returned when the input String is null. Used when {@link DateConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a Date input is null. Used when {@link DateConversion#revert(Date)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(Locale locale, Date dateIfNull, String stringIfNull, String... dateFormats) { return new DateConversion(locale, dateIfNull, stringIfNull, dateFormats); } /** * Returns a new instance of {@link DateConversion} * * @param dateIfNull default Date value to be returned when the input String is null. Used when {@link DateConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a Date input is null. Used when {@link DateConversion#revert(Date)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. * * @return the new instance of {@link DateConversion} created with the given parameters. */ public static DateConversion toDate(Date dateIfNull, String stringIfNull, String... dateFormats) { return new DateConversion(Locale.getDefault(), dateIfNull, stringIfNull, dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(Locale locale, String... dateFormats) { return new CalendarConversion(locale, dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(String... dateFormats) { return new CalendarConversion(Locale.getDefault(), dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateIfNull default Calendar value to be returned when the input String is null. Used when {@link CalendarConversion#execute(String)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(Locale locale, Calendar dateIfNull, String... dateFormats) { return new CalendarConversion(locale, dateIfNull, null, dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param dateIfNull default Calendar value to be returned when the input String is null. Used when {@link CalendarConversion#execute(String)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(Calendar dateIfNull, String... dateFormats) { return new CalendarConversion(Locale.getDefault(), dateIfNull, null, dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateIfNull default Calendar value to be returned when the input String is null. Used when {@link CalendarConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a Date input is null. Used when {@link CalendarConversion#revert(Calendar)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(Locale locale, Calendar dateIfNull, String stringIfNull, String... dateFormats) { return new CalendarConversion(TimeZone.getDefault(), locale, dateIfNull, stringIfNull, dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param timeZone the {@link TimeZone} to be considered * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateIfNull default Calendar value to be returned when the input String is null. Used when {@link CalendarConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a Date input is null. Used when {@link CalendarConversion#revert(Calendar)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(TimeZone timeZone, Locale locale, Calendar dateIfNull, String stringIfNull, String... dateFormats) { return new CalendarConversion(timeZone, locale, dateIfNull, stringIfNull, dateFormats); } /** * Returns a new instance of {@link CalendarConversion} * * @param dateIfNull default Calendar value to be returned when the input String is null. Used when {@link CalendarConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a Date input is null. Used when {@link CalendarConversion#revert(Calendar)} is invoked. * @param dateFormats list of acceptable date patterns. The first pattern in this sequence will be used to convert a Calendar into a String in {@link CalendarConversion#revert(Calendar)}. * * @return the new instance of {@link CalendarConversion} created with the given parameters. */ public static CalendarConversion toCalendar(Calendar dateIfNull, String stringIfNull, String... dateFormats) { return new CalendarConversion(Locale.getDefault(), dateIfNull, stringIfNull, dateFormats); } /** * Returns a new instance of {@link ByteConversion} * * @return a new instance of {@link ByteConversion} */ public static ByteConversion toByte() { return new ByteConversion(); } /** * Returns a new instance of {@link ShortConversion} * * @return a new instance of {@link ShortConversion} */ public static ShortConversion toShort() { return new ShortConversion(); } /** * Returns a new instance of {@link IntegerConversion} * * @return a new instance of {@link IntegerConversion} */ public static IntegerConversion toInteger() { return new IntegerConversion(); } /** * Returns a new instance of {@link LongConversion} * * @return a new instance of {@link LongConversion} */ public static LongConversion toLong() { return new LongConversion(); } /** * Returns a new instance of {@link BigIntegerConversion} * * @return a new instance of {@link BigIntegerConversion} */ public static BigIntegerConversion toBigInteger() { return new BigIntegerConversion(); } /** * Returns a new instance of {@link FloatConversion} * * @return a new instance of {@link FloatConversion} */ public static FloatConversion toFloat() { return new FloatConversion(); } /** * Returns a new instance of {@link DoubleConversion} * * @return a new instance of {@link DoubleConversion} */ public static DoubleConversion toDouble() { return new DoubleConversion(); } /** * Returns a new instance of {@link BigDecimalConversion} * * @return a new instance of {@link BigDecimalConversion} */ public static BigDecimalConversion toBigDecimal() { return new BigDecimalConversion(); } /** * Returns a new instance of {@link NumericConversion} * * @param numberFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. * * @return a new instance of {@link NumericConversion} that supports the given number formats */ public static NumericConversion formatToNumber(String... numberFormats) { return new NumericConversion(numberFormats) { @Override protected void configureFormatter(DecimalFormat formatter) { } }; } /** * Returns a new instance of {@link NumericConversion} * * @param numberType type of number to be returned. The resulting instance of {@code Number} will be cast to the expected type. * @param numberFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. * @param type of number * * @return a new instance of {@link NumericConversion} that supports the given number formats */ public static NumericConversion formatToNumber(Class numberType, String... numberFormats) { return new NumericConversion(numberFormats) { @Override protected void configureFormatter(DecimalFormat formatter) { } }; } /** * Returns a new instance of {@link FormattedBigDecimalConversion} * * @param numberFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a BigDecimal into a String in {@link NumericConversion#revert(Number)}. * * @return a new instance of {@link FormattedBigDecimalConversion} that supports the given number formats */ public static FormattedBigDecimalConversion formatToBigDecimal(String... numberFormats) { return new FormattedBigDecimalConversion(numberFormats); } /** * Returns a new instance of {@link FormattedBigDecimalConversion} * * @param defaultValueForNullString default BigDecimal to be returned when the input String is null. Used when {@link FormattedBigDecimalConversion#execute(String)} is invoked. * @param numberFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a BigDecimal into a String in {@link NumericConversion#revert(Number)}. * * @return a new instance of {@link FormattedBigDecimalConversion} that supports the given number formats */ public static FormattedBigDecimalConversion formatToBigDecimal(BigDecimal defaultValueForNullString, String... numberFormats) { return new FormattedBigDecimalConversion(defaultValueForNullString, null, numberFormats); } /** * Returns a new instance of {@link FormattedBigDecimalConversion} * * @param defaultValueForNullString default BigDecimal to be returned when the input String is null. Used when {@link FormattedBigDecimalConversion#execute(String)} is invoked. * @param stringIfNull default String value to be returned when a BigDecimal input is null. Used when {@code FormattedBigDecimalConversion#revert(BigDecimal)} is invoked. * @param numberFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a BigDecimal into a String in {@link NumericConversion#revert(Number)}. * * @return a new instance of {@link FormattedBigDecimalConversion} that supports the given number formats */ public static FormattedBigDecimalConversion formatToBigDecimal(BigDecimal defaultValueForNullString, String stringIfNull, String... numberFormats) { return new FormattedBigDecimalConversion(defaultValueForNullString, stringIfNull, numberFormats); } /** * Returns a new instance of {@link BooleanConversion} * * @param defaultValueForNullString default Boolean value to be returned when the input String is null. Used when {@link BooleanConversion#execute(String)} is invoked. * @param defaultValueForNullBoolean default String value to be returned when a Boolean input is null. Used when {@link BooleanConversion#revert(Boolean)} is invoked. * @param valuesForTrue Strings that identify the boolean value true. The first element will be returned when executing {@code BooleanConversion.revert(true)} * @param valuesForFalse Strings that identify the boolean value false. The first element will be returned when executing {@code BooleanConversion.revert(false)} * * @return a new instance of {@link BooleanConversion} with support for multiple representations of true and false */ public static BooleanConversion toBoolean(Boolean defaultValueForNullString, String defaultValueForNullBoolean, String[] valuesForTrue, String[] valuesForFalse) { return new BooleanConversion(defaultValueForNullString, defaultValueForNullBoolean, valuesForTrue, valuesForFalse); } /** * Returns a new instance of {@link BooleanConversion} * * @param defaultValueForNullString default Boolean value to be returned when the input String is null. Used when {@link BooleanConversion#execute(String)} is invoked. * @param defaultValueForNullBoolean default String value to be returned when a Boolean input is null. Used when {@link BooleanConversion#revert(Boolean)} is invoked. * @param valueForTrue String that identify the boolean value true. * @param valueForFalse String that identify the boolean value false. * * @return a new instance of {@link BooleanConversion} with support for multiple representations of true and false */ public static BooleanConversion toBoolean(Boolean defaultValueForNullString, String defaultValueForNullBoolean, String valueForTrue, String valueForFalse) { return new BooleanConversion(defaultValueForNullString, defaultValueForNullBoolean, new String[]{valueForTrue}, new String[]{valueForFalse}); } /** * Returns a new instance of {@link BooleanConversion} * * @param valuesForTrue Strings that identify the boolean value true. The first element will be returned when executing {@code BooleanConversion.revert(true)} * @param valuesForFalse Strings that identify the boolean value false. The first element will be returned when executing {@code BooleanConversion.revert(false)} * * @return a new instance of {@link BooleanConversion} with support for multiple representations of true and false */ public static BooleanConversion toBoolean(String[] valuesForTrue, String[] valuesForFalse) { return new BooleanConversion(valuesForTrue, valuesForFalse); } /** * Returns a new instance of {@link BooleanConversion} that converts the string "true" to true, and the String "false" to false. * * @return a new instance of {@link BooleanConversion} with support for multiple representations of true and false */ public static BooleanConversion toBoolean() { return toBoolean("true", "false"); } /** * Returns a new instance of {@link BooleanConversion} * * @param valueForTrue String that identifies the boolean value true. * @param valueForFalse String that identifies the boolean value false. * * @return a new instance of {@link BooleanConversion} with support for multiple representations of true and false */ public static BooleanConversion toBoolean(String valueForTrue, String valueForFalse) { return new BooleanConversion(new String[]{valueForTrue}, new String[]{valueForFalse}); } /** * Returns a new instance of {@link CharacterConversion} * * @return a new instance of {@link CharacterConversion} */ public static CharacterConversion toChar() { return new CharacterConversion(); } /** * Returns a new instance of {@link CharacterConversion} * * @param defaultValueForNullString default Character value to be returned when the input String is null. Used when {@link CharacterConversion#execute(String)} is invoked. * @param defaultValueForNullChar default String value to be returned when a Character input is null. Used when {@code CharacterConversion#revert(Character)} is invoked. * * @return a new instance of {@link CharacterConversion} */ public static CharacterConversion toChar(Character defaultValueForNullString, String defaultValueForNullChar) { return new CharacterConversion(defaultValueForNullString, defaultValueForNullChar); } /** * Returns a new instance of {@link CharacterConversion} * * @param defaultValueForNullString default Character value to be returned when the input String is null. Used when {@link CharacterConversion#execute(String)} is invoked. * * @return a new instance of {@link CharacterConversion} */ public static CharacterConversion toChar(Character defaultValueForNullString) { return new CharacterConversion(defaultValueForNullString, null); } /** * Returns a new instance of {@link EnumConversion} * * @param the {@code enum} type * @param enumType the enumeration type to be converted from/to {@code String} * * @return new instance of {@link EnumConversion} */ public static > EnumConversion toEnum(Class enumType) { return new EnumConversion(enumType); } /** * Returns a new instance of {@link EnumConversion} * * @param the {@code enum} type * @param enumType the enumeration type to be converted from/to {@code String} * @param selectors the selection elements of the enumeration to use for matching {@code String}s. * * @return new instance of {@link EnumConversion} */ public static > EnumConversion toEnum(Class enumType, EnumSelector... selectors) { return toEnum(enumType, null, null, null, selectors); } /** * Returns a new instance of {@link EnumConversion} * * @param the {@code enum} type * @param enumType the enumeration type to be converted from/to {@code String} * @param customEnumElement name of custom element of the enumeration (attribute or method) whose values should be used to match equivalent {@code String}s. * @param selectors the selection elements of the enumeration to use for matching {@code String}s. * * @return new instance of {@link EnumConversion} */ public static > EnumConversion toEnum(Class enumType, String customEnumElement, EnumSelector... selectors) { return toEnum(enumType, null, null, customEnumElement); } /** * Returns a new instance of {@link EnumConversion} * * @param the {@code enum} type * @param enumType the enumeration type to be converted from/to {@code String} * @param valueIfStringIsNull the default enumeration constant to use if the input {@code String} is {@code null} * @param valueIfEnumIsNull the default {@code String} value to use if the input {@code enum} constant is {@code null} * @param customEnumElement name of custom element of the enumeration (attribute or method) whose values should be used to match equivalent {@code String}s. * @param selectors the selection elements of the enumeration to use for matching {@code String}s. * * @return new instance of {@link EnumConversion} */ public static > EnumConversion toEnum(Class enumType, T valueIfStringIsNull, String valueIfEnumIsNull, String customEnumElement, EnumSelector... selectors) { return new EnumConversion(enumType, valueIfStringIsNull, valueIfEnumIsNull, customEnumElement, selectors); } /** * Returns a new instance of {@link FormattedDateConversion} * * @param pattern Date mask to be be used to convert a date object (i.e. {@link Date} or {@link Calendar}) into a String. * * @return new instance of {@link FormattedDateConversion} configured with the given parameters */ public static FormattedDateConversion toFormattedDate(String pattern) { return toFormattedDate(pattern, null, null); } /** * Returns a new instance of {@link FormattedDateConversion} * * @param pattern Date mask to be be used to convert a date object (i.e. {@link Date} or {@link Calendar}) into a String. * @param valueIfObjectIsNull Default string value to be returned when the input object is null. * * @return new instance of {@link FormattedDateConversion} configured with the given parameters */ public static FormattedDateConversion toFormattedDate(String pattern, String valueIfObjectIsNull) { return toFormattedDate(pattern, null, valueIfObjectIsNull); } /** * Returns a new instance of {@link FormattedDateConversion} * * @param pattern Date mask to be be used to convert a date object (i.e. {@link Date} or {@link Calendar}) into a String. * @param locale The {@link Locale} that determines how the date pattern should be formatted. * * @return new instance of {@link FormattedDateConversion} configured with the given parameters */ public static FormattedDateConversion toFormattedDate(String pattern, Locale locale) { return toFormattedDate(pattern, locale, null); } /** * Returns a new instance of {@link FormattedDateConversion} * * @param pattern Date mask to be be used to convert a date object (i.e. {@link Date} or {@link Calendar}) into a String. * @param locale The {@link Locale} that determines how the date pattern should be formatted. * @param valueIfObjectIsNull Default string value to be returned when the input object is null. * * @return new instance of {@link FormattedDateConversion} configured with the given parameters */ public static FormattedDateConversion toFormattedDate(String pattern, Locale locale, String valueIfObjectIsNull) { return new FormattedDateConversion(pattern, locale, valueIfObjectIsNull); } /** * Returns a {@link ValidatedConversion} that doesn't allow null values * @return a not-null validator */ public static ValidatedConversion notNull() { return validate(false, true, null, null); } /** * Returns a {@link ValidatedConversion} that doesn't allow null or blank values * @return a not-blank validator */ public static ValidatedConversion notBlank() { return validate(false, false, null, null); } /** * Returns a {@link ValidatedConversion} that verifies the format of a given value * * @param regexToMatch regular expression to match and ensure the value has a given format * * @return a value format validator */ public static ValidatedConversion notBlank(String regexToMatch) { return validate(false, false, null, null, regexToMatch); } /** * Returns a {@link ValidatedConversion} that checks for nulls or blank values. * * @param nullable flag to indicate whether values can be null * @param allowBlanks flag to indicate whether values can be blank * * @return new instance of {@link ValidatedConversion} configured with the given parameters */ public static ValidatedConversion validate(boolean nullable, boolean allowBlanks) { return new ValidatedConversion(nullable, allowBlanks, null, null, null); } /** * Returns a new instance of {@link ValidatedConversion} to validate values of a record * * @param nullable flag to indicate whether values can be null * @param allowBlanks flag to indicate whether values can be blank * @param oneOf list of accepted values. * @param noneOf list of unacceptable values * * @return new instance of {@link ValidatedConversion} configured with the given parameters */ public static ValidatedConversion validate(boolean nullable, boolean allowBlanks, String[] oneOf, String[] noneOf) { return new ValidatedConversion(nullable, allowBlanks, oneOf, noneOf, null); } /** * Returns a {@link ValidatedConversion} that checks for nulls or blank values. * * @param nullable flag to indicate whether values can be null * @param allowBlanks flag to indicate whether values can be blank * @param regexToMatch regular expression to match and ensure the value has a given format * * @return new instance of {@link ValidatedConversion} configured with the given parameters */ public static ValidatedConversion validate(boolean nullable, boolean allowBlanks, String regexToMatch) { return new ValidatedConversion(nullable, allowBlanks, null, null, regexToMatch); } /** * Returns a new instance of {@link ValidatedConversion} to validate values of a record * * @param nullable flag to indicate whether values can be null * @param allowBlanks flag to indicate whether values can be blank * @param oneOf list of accepted values. * @param noneOf list of unacceptable values * @param regexToMatch regular expression to match and ensure the value has a given format * * @return new instance of {@link ValidatedConversion} configured with the given parameters */ public static ValidatedConversion validate(boolean nullable, boolean allowBlanks, String[] oneOf, String[] noneOf, String regexToMatch) { return new ValidatedConversion(nullable, allowBlanks, oneOf, noneOf, regexToMatch); } /** * Returns a new instance of {@link ValidatedConversion} to validate values of a record * * Nulls and blanks are not be allowed by default. * * @param oneOf list of accepted values. * * @return new instance of {@link ValidatedConversion} configured with the given parameters */ public static ValidatedConversion oneOf(String... oneOf) { return new ValidatedConversion(false, false, oneOf, null, null); } /** * Returns a new instance of {@link ValidatedConversion} to validate values of a record * * Nulls and blanks are not be allowed by default. * * @param noneOf list of values that are not allowed. * * @return new instance of {@link ValidatedConversion} configured with the given parameters */ public static ValidatedConversion noneOf(String... noneOf) { return new ValidatedConversion(false, false, null, noneOf, null); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/DateConversion.java000066400000000000000000000175351400120543400323150ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; import java.text.*; import java.util.*; /** * Converts Strings to instances of {@link java.util.Date} and vice versa. * *

This class supports multiple date formats. For example, you can define conversions from dates represented by different Strings such as "2001/05/02 and Dec/2013". * *

The reverse conversion from a Date to String (in {@link DateConversion#revert(Date)} will return a formatted String using the date pattern provided in this class constructor *

The date patterns must follows the pattern rules of {@link java.text.SimpleDateFormat} * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see java.text.SimpleDateFormat */ public class DateConversion extends ObjectConversion implements FormattedConversion { private final Locale locale; private final TimeZone timeZone; private final SimpleDateFormat[] parsers; private final String[] formats; /** * Defines a conversion from String to {@link java.util.Date} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param timeZone the {@link TimeZone} of the date to be formatted * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param valueIfStringIsNull default Date value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Date input is null. Used when {@link DateConversion#revert(Date)} is invoked. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. */ public DateConversion(TimeZone timeZone, Locale locale, Date valueIfStringIsNull, String valueIfObjectIsNull, String... dateFormats) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.noNulls("Date formats", dateFormats); this.timeZone = timeZone == null ? TimeZone.getDefault() : timeZone; this.locale = locale == null ? Locale.getDefault() : locale; this.formats = dateFormats.clone(); this.parsers = new SimpleDateFormat[dateFormats.length]; for (int i = 0; i < dateFormats.length; i++) { String dateFormat = dateFormats[i]; parsers[i] = new SimpleDateFormat(dateFormat, this.locale); parsers[i].setTimeZone(this.timeZone); } } /** * Defines a conversion from String to {@link java.util.Date} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param valueIfStringIsNull default Date value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Date input is null. Used when {@link DateConversion#revert(Date)} is invoked. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. */ public DateConversion(Locale locale, Date valueIfStringIsNull, String valueIfObjectIsNull, String... dateFormats) { this(TimeZone.getDefault(), locale, valueIfStringIsNull, valueIfObjectIsNull, dateFormats); } /** * Defines a conversion from String to {@link java.util.Date} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param valueIfStringIsNull default Date value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Date input is null. Used when {@link DateConversion#revert(Date)} is invoked. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. */ public DateConversion(Date valueIfStringIsNull, String valueIfObjectIsNull, String... dateFormats) { this(Locale.getDefault(), valueIfStringIsNull, valueIfObjectIsNull, dateFormats); } /** * Defines a conversion from String to {@link java.util.Date} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. */ public DateConversion(Locale locale, String... dateFormats) { this(locale, null, null, dateFormats); } /** * Defines a conversion from String to {@link java.util.Date} using a sequence of acceptable date patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param dateFormats list of acceptable date patterns The first pattern in this sequence will be used to convert a Date into a String in {@link DateConversion#revert(Date)}. */ public DateConversion(String... dateFormats) { this(Locale.getDefault(), null, null, dateFormats); } /** * Converts Date to a formatted date String. *

The pattern used to generate the formatted date is the first date pattern provided in the constructor of this class * * @param input the Date to be converted to a String * * @return a formatted date String representing the date provided by the given Date, or the value of {@code valueIfObjectIsNull} if the Date parameter is null. */ @Override public String revert(Date input) { if (input == null) { return super.revert(null); } return parsers[0].format(input); } /** * Converts a formatted date String to an instance of Date. *

The pattern in the formatted date must match one of the date patterns provided in the constructor of this class. * * @param input the String containing a formatted date which must be converted to a Date * * @return the Date instance containing the date information represented by the given String, or the value of {@code valueIfObjectIsNull} if the String input is null. */ @Override protected Date fromString(String input) { for (SimpleDateFormat formatter : parsers) { try { synchronized (formatter) { return formatter.parse(input); } } catch (ParseException ex) { //ignore and continue } } DataProcessingException exception = new DataProcessingException("Cannot parse '{value}' as a valid date of locale '" + locale + "'. Supported formats are: " + Arrays.toString(formats)); exception.setValue(input); throw exception; } @Override public SimpleDateFormat[] getFormatterObjects() { return parsers; } public TimeZone getTimeZone(){ return timeZone; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/DoubleConversion.java000066400000000000000000000036661400120543400326520ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts Strings to Doubles and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class DoubleConversion extends ObjectConversion { /** * Creates a Conversion from String to Double with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public DoubleConversion() { super(); } /** * Creates a Conversion from String to Double with default values to return when the input is null. * @param valueIfStringIsNull default Double value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Double input is null. Used when {@code revert(Double)} is invoked. */ public DoubleConversion(Double valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to Double. */ @Override protected Double fromString(String input) { return Double.valueOf(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/EnumConversion.java000066400000000000000000000261601400120543400323360ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; import java.lang.reflect.*; import java.util.*; /** * Converts Strings to enumeration constants and vice versa. * *

This class supports multiple types of identification of enumeration constants. For example, you can match the literal ({@link Enum#name()} the ordinal ({@link Enum#ordinal()} or * the result of a method defined in your enumeration. * *

The reverse conversion from an enumeration to String (in {@link EnumConversion#revert(Enum)} will return a String using the first {@link EnumSelector} provided in the constructor of this class. * * @param the enumeration type whose constants will be converted from/to {@code String} * @see ObjectConversion * @see EnumSelector * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class EnumConversion> extends ObjectConversion { private final Class enumType; private final Field customEnumField; private final Method customEnumMethod; private final EnumSelector[] selectors; private final Map[] conversions; /** * Defines a conversion for an enumeration type that will attempt to match Strings against * the results of the output produced by ({@link Enum#name()}, ({@link Enum#ordinal()} and ({@link Enum#toString()} * of each constant of the given enumeration (@link {@link Class#getEnumConstants()}). * * @param enumType the enumeration type to be converted from/to {@code String} */ public EnumConversion(Class enumType) { this(enumType, EnumSelector.NAME, EnumSelector.ORDINAL, EnumSelector.STRING); } /** * Defines a conversion for an enumeration type that will attempt to match Strings the list of {@link EnumSelector}s, in the specified order. * Each {@link EnumSelector} identifies which element of each constant of the enumeration class (@link {@link Class#getEnumConstants()} * should be used to match equivalent {@code String}s. * * @param enumType the enumeration type to be converted from/to {@code String} * @param selectors the selection elements of the enumeration to use for matching {@code String}s. */ public EnumConversion(Class enumType, EnumSelector... selectors) { this(enumType, null, null, null, selectors); } /** * Defines a conversion for an enumeration type that will attempt to match Strings the list of {@link EnumSelector}s, in the specified order. * Each {@link EnumSelector} identifies which element of each constant of the enumeration class (@link {@link Class#getEnumConstants()} * should be used to match equivalent {@code String}s. * * @param enumType the enumeration type to be converted from/to {@code String} * @param customEnumElement name of custom element of the enumeration (attribute or method) whose values should be used to match equivalent {@code String}s. * @param selectors the selection elements of the enumeration to use for matching {@code String}s. */ public EnumConversion(Class enumType, String customEnumElement, EnumSelector... selectors) { this(enumType, null, null, customEnumElement); } /** * Defines a conversion for an enumeration type that will attempt to match Strings the list of {@link EnumSelector}s, in the specified order. * Each {@link EnumSelector} identifies which element of each constant of the enumeration class (@link {@link Class#getEnumConstants()} * should be used to match equivalent {@code String}s. * * @param enumType the enumeration type to be converted from/to {@code String} * @param valueIfStringIsNull the default enumeration constant to use if the input {@code String} is {@code null} * @param valueIfEnumIsNull the default {@code String} value to use if the input {@code enum} constant is {@code null} * @param customEnumElement name of custom element of the enumeration (attribute or method) whose values should be used to match equivalent {@code String}s. * @param selectors the selection elements of the enumeration to use for matching {@code String}s. */ @SuppressWarnings("unchecked") public EnumConversion(Class enumType, T valueIfStringIsNull, String valueIfEnumIsNull, String customEnumElement, EnumSelector... selectors) { super(valueIfStringIsNull, valueIfEnumIsNull); this.enumType = enumType; if (customEnumElement != null) { customEnumElement = customEnumElement.trim(); if (customEnumElement.isEmpty()) { customEnumElement = null; } } LinkedHashSet selectorSet = new LinkedHashSet(); Collections.addAll(selectorSet, selectors); if ((selectorSet.contains(EnumSelector.CUSTOM_FIELD) || selectorSet.contains(EnumSelector.CUSTOM_METHOD)) && customEnumElement == null) { throw new IllegalArgumentException("Cannot create custom enum conversion without a field name to use"); } Field field = null; Method method = null; if (customEnumElement != null) { IllegalStateException fieldError = null; IllegalStateException methodError = null; try { field = enumType.getDeclaredField(customEnumElement); if (!field.isAccessible()) { field.setAccessible(true); } } catch (Throwable e) { fieldError = new IllegalStateException("Unable to access custom field '" + customEnumElement + "' in enumeration type " + enumType.getName(), e); } if (field == null) { try { try { method = enumType.getDeclaredMethod(customEnumElement); } catch (NoSuchMethodException e) { method = enumType.getDeclaredMethod(customEnumElement, String.class); if (!Modifier.isStatic(method.getModifiers())) { throw new IllegalArgumentException("Custom method '" + customEnumElement + "' in enumeration type " + enumType.getName() + " must be static"); } if(method.getReturnType() != enumType){ throw new IllegalArgumentException("Custom method '" + customEnumElement + "' in enumeration type " + enumType.getName() + " must return " + enumType.getName()); } } if (!method.isAccessible()) { method.setAccessible(true); } } catch (Throwable e) { methodError = new IllegalStateException("Unable to access custom method '" + customEnumElement + "' in enumeration type " + enumType.getName(), e); } if (method != null) { if (method.getReturnType() == void.class) { throw new IllegalArgumentException("Custom method '" + customEnumElement + "' in enumeration type " + enumType.getName() + " must return a value"); } if (!selectorSet.contains(EnumSelector.CUSTOM_METHOD)) { selectorSet.add(EnumSelector.CUSTOM_METHOD); } } } else if (!selectorSet.contains(EnumSelector.CUSTOM_FIELD)) { selectorSet.add(EnumSelector.CUSTOM_FIELD); } if (selectorSet.contains(EnumSelector.CUSTOM_FIELD) && fieldError != null) { throw fieldError; } if (selectorSet.contains(EnumSelector.CUSTOM_METHOD) && methodError != null) { throw methodError; } if (field == null && method == null) { throw new IllegalStateException("No method/field named '" + customEnumElement + "' found in enumeration type " + enumType.getName()); } } if (selectorSet.contains(EnumSelector.CUSTOM_FIELD) && selectorSet.contains(EnumSelector.CUSTOM_METHOD)) { throw new IllegalArgumentException("Cannot create custom enum conversion using both method and field values"); } if (selectorSet.isEmpty()) { throw new IllegalArgumentException("Selection of enum conversion types cannot be empty."); } this.customEnumField = field; this.customEnumMethod = method; this.selectors = selectorSet.toArray(new EnumSelector[selectorSet.size()]); this.conversions = new Map[selectorSet.size()]; initializeMappings(selectorSet); } private void initializeMappings(Set conversionTypes) { T[] constants = enumType.getEnumConstants(); int i = 0; for (EnumSelector conversionType : conversionTypes) { Map map = new HashMap(constants.length); conversions[i++] = map; for (T constant : constants) { String key = getKey(constant, conversionType); if (key != null) { if (map.containsKey(key)) { throw new IllegalArgumentException("Enumeration element type " + conversionType + " does not uniquely identify elements of " + enumType.getName() + ". Got duplicate value '" + key + "' from constants '" + constant + "' and '" + map.get(key) + "'."); } map.put(key, constant); } } } } private String getKey(T constant, EnumSelector conversionType) { switch (conversionType) { case NAME: return constant.name(); case ORDINAL: return String.valueOf(constant.ordinal()); case STRING: return constant.toString(); case CUSTOM_FIELD: try { return String.valueOf(customEnumField.get(constant)); } catch (Throwable e) { throw new IllegalStateException("Error reading custom field '" + customEnumField.getName() + "' from enumeration constant '" + constant + "' of type " + enumType.getName(), e); } case CUSTOM_METHOD: try { if (customEnumMethod.getParameterTypes().length == 0) { return String.valueOf(customEnumMethod.invoke(constant)); } else { return null; } } catch (Throwable e) { throw new IllegalStateException("Error reading custom method '" + customEnumMethod.getName() + "' from enumeration constant '" + constant + "' of type " + enumType.getName(), e); } default: throw new IllegalStateException("Unsupported enumeration selector type " + conversionType); } } @Override public String revert(T input) { if (input == null) { return super.revert(null); } return getKey(input, selectors[0]); } @Override protected T fromString(String input) { for (Map conversion : conversions) { T value = conversion.get(input); if (value != null) { return value; } } DataProcessingException exception = null; if (customEnumMethod != null && customEnumMethod.getParameterTypes().length == 1) { try { T out = (T) customEnumMethod.invoke(null, input); return out; } catch (Exception e) { exception = new DataProcessingException("Cannot convert '{value}' to enumeration of type " + enumType.getName() + " using method " + customEnumMethod.getName(), e); } } if (exception == null) { exception = new DataProcessingException("Cannot convert '{value}' to enumeration of type " + enumType.getName()); } exception.setValue(input); exception.markAsNonFatal(); throw exception; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/EnumSelector.java000066400000000000000000000030751400120543400317710ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.annotations.EnumOptions; /** * Identifies a property of an enumeration that should be used by {@link EnumOptions} to identify an input value. * When parsing a given input, values will be compared against one of the properties and if there's a match, the * corresponding enumeration value will be used to set the field of an annotated class. */ public enum EnumSelector { /** * Matches the result of {@link Enum#ordinal()} */ ORDINAL, /** * Matches the result of {@link Enum#name()} */ NAME, /** * Matches the result of {@link Enum#toString()} ()} */ STRING, /** * Matches the value of a field of the annotated enumeration */ CUSTOM_FIELD, /** * Matches the value of a method of the annotated enumeration */ CUSTOM_METHOD } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/FloatConversion.java000066400000000000000000000036471400120543400325040ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts Strings to Floats and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class FloatConversion extends ObjectConversion { /** * Creates a Conversion from String to Float with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public FloatConversion() { super(); } /** * Creates a Conversion from String to Float with default values to return when the input is null. * @param valueIfStringIsNull default Float value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Float input is null. Used when {@code revert(Float)} is invoked. */ public FloatConversion(Float valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to Float. */ @Override protected Float fromString(String input) { return Float.valueOf(input); } } FormattedBigDecimalConversion.java000066400000000000000000000114621400120543400352000ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import java.math.*; import java.text.*; /** * Converts formatted Strings to instances of {@link java.math.BigDecimal} and vice versa. * *

This class supports multiple numeric formats. For example, you can define conversions from numbers represented by different Strings such as "1,000,000.00 and $5.00". *

The reverse conversion from a BigDecimal to String (in {@code revert(BigDecimal)} will return a formatted String using the pattern provided in this class constructor *

The numeric patterns must follow the pattern rules of {@link java.text.DecimalFormat} * * @see java.text.DecimalFormat * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class FormattedBigDecimalConversion extends NumericConversion { /** * Defines a conversion from String to {@link java.math.BigDecimal} using a sequence of acceptable numeric patterns. * This constructor assumes the output of a conversion should be null when input is null * @param valueIfStringIsNull default BigDecimal to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a BigDecimal input is null. Used when {@code revert(BigDecimal)} is invoked. * @param numericFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a BigDecimal into a String in {@code revert(BigDecimal)}. */ public FormattedBigDecimalConversion(BigDecimal valueIfStringIsNull, String valueIfObjectIsNull, String... numericFormats) { super(valueIfStringIsNull, valueIfObjectIsNull, numericFormats); } /** * Defines a conversion from String to {@link java.math.BigDecimal} using a sequence of acceptable numeric patterns. The patterns * must be added to this conversion class through the {@link #addFormat(String, String...)} method. * * @param valueIfStringIsNull default BigDecimal to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a BigDecimal input is null. Used when {@link NumericConversion#revert(Number)} is invoked. */ public FormattedBigDecimalConversion(BigDecimal valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Defines a conversion from String to {@link java.math.BigDecimal} using a sequence of acceptable numeric patterns. * This constructor assumes the output of a conversion should be null when input is null * @param numericFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a BigDecimal into a String in {@link NumericConversion#revert(Number)}. */ public FormattedBigDecimalConversion(String... numericFormats) { super(null, null, numericFormats); } /** * Defines a conversion from String to{@link java.math.BigDecimal} using a sequence of acceptable numeric patterns. * This constructor assumes the output of a conversion should be null when input is null * @param numericFormatters list formatters of acceptable numeric patterns. The first formatter in this sequence will be used to convert a BigDecimal into a String in {@link NumericConversion#revert(Number)}. */ public FormattedBigDecimalConversion(DecimalFormat... numericFormatters) { super(numericFormatters); } /** * Defines a conversion from String to {@link java.math.BigDecimal} using a sequence of acceptable numeric patterns. The patterns * must be added to this conversion class through the {@link #addFormat(String, String...)} method. * * This constructor assumes the output of a conversion should be null when input is null * */ public FormattedBigDecimalConversion() { super(); } /** * Configures the Decimal format instance created by the parent class to parse BigDecimals. */ @Override protected void configureFormatter(DecimalFormat formatter) { formatter.setParseBigDecimal(true); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/FormattedConversion.java000066400000000000000000000030631400120543400333540ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.annotations.*; /** * This interface identifies conversions associated with the {@link Format} annotation. * It is used when {@link Format#options()} is defined to set any give properties of the underlying formatter. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @param The type of the formatter object (typically {@link java.text.DecimalFormat} for numeric values, and {@link java.text.SimpleDateFormat} for dates) * */ public interface FormattedConversion { /** * Returns the formatter objects * @return the formatter objects used to apply formatting to values to generate formatted Strings, and parsing formatted Strings into values */ T[] getFormatterObjects(); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/FormattedDateConversion.java000066400000000000000000000054661400120543400341630ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; import java.text.*; import java.util.*; /** * Converts objects of different date types ({@code java.util.Date} and {@code java.util.Calendar}) to a formatted * date {@code String}. * *

The reverse conversion is not supported. * *

The date patterns must follow the pattern rules of {@link SimpleDateFormat} * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see SimpleDateFormat */ public class FormattedDateConversion implements Conversion { private final SimpleDateFormat dateFormat; private final String valueIfObjectIsNull; /** * * @param format The pattern to be used to convert an input date into a String in {@link FormattedDateConversion#execute(Object)}. * @param locale the {@link Locale} that determines how the date mask should be formatted. * @param valueIfObjectIsNull default String value to be returned when an input is {@code null} . Used when {@link FormattedDateConversion#execute(Object)} is invoked with a {@code null} parameter. */ public FormattedDateConversion(String format, Locale locale, String valueIfObjectIsNull) { this.valueIfObjectIsNull = valueIfObjectIsNull; locale = locale == null ? Locale.getDefault() : locale; this.dateFormat = new SimpleDateFormat(format, locale); } @Override public String execute(Object input) { if (input == null) { return valueIfObjectIsNull; } Date date = null; if (input instanceof Date) { date = ((Date) input); } else if (input instanceof Calendar) { date = ((Calendar) input).getTime(); } if (date != null) { return dateFormat.format(date); } DataProcessingException exception = new DataProcessingException("Cannot format '{value}' to a date. Not an instance of java.util.Date or java.util.Calendar"); exception.setValue(input); throw exception; } /** * Unsupported operation. * * @param input the input be converted. * * @return throws a {@code UnsupportedOperationException} */ @Override public Object revert(String input) { throw new UnsupportedOperationException("Can't convert an input string into date type"); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/IntegerConversion.java000066400000000000000000000037031400120543400330250ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts Strings to Integers and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class IntegerConversion extends ObjectConversion { /** * Creates a Conversion from String to Integer with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public IntegerConversion() { super(); } /** * Creates a Conversion from String to Integer with default values to return when the input is null. * @param valueIfStringIsNull default Integer value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Integer input is null. Used when {@code revert(Integer)} is invoked. */ public IntegerConversion(Integer valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to Integer. */ @Override protected Integer fromString(String input) { return Integer.valueOf(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/LongConversion.java000066400000000000000000000036311400120543400323270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts Strings to Longs and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class LongConversion extends ObjectConversion { /** * Creates a Conversion from String to Long with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public LongConversion() { super(); } /** * Creates a Conversion from String to Long with default values to return when the input is null. * @param valueIfStringIsNull default Long value to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Long input is null. Used when {@code revert(Long)} is invoked. */ public LongConversion(Long valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to Long. */ @Override protected Long fromString(String input) { return Long.valueOf(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/LowerCaseConversion.java000066400000000000000000000037471400120543400333240ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts an input String to its lower case representation * * The {@link LowerCaseConversion#revert(String)} implements the same behavior of {@link LowerCaseConversion#execute(String)}. Null inputs produce null outputs. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class LowerCaseConversion implements Conversion { /** * Applies the toLowerCase operation in the input and returns the result. * Equivalent to {@link LowerCaseConversion#revert(String)} * @param input the String to be converted to lower case * @return the lower case representation of the given input, or null if the input is null. */ @Override public String execute(String input) { if (input == null) { return null; } return input.toLowerCase(); } /** * Applies the toLowerCase operation in the input and returns the result. * Equivalent to {@link LowerCaseConversion#execute(String)} * @param input the String to be converted to lower case * @return the lower case representation of the given input, or null if the input is null. */ @Override public String revert(String input) { return execute(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/NullConversion.java000066400000000000000000000121241400120543400323370ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Default implementation for conversions from input Objects of type I to output Objects of type O * *

Extending classes must implement a proper String to T conversion in {@link ObjectConversion#fromString(String)} *

This abstract class provides default results for conversions when the input is null. * * @author Univocity Software Pty Ltd - parsers@univocity.com * * @param The object type resulting from conversions of values of type O. * @param The object type resulting from conversions of values of type I. */ public abstract class NullConversion implements Conversion { private O valueOnNullInput; private I valueOnNullOutput; /** * Creates a Conversion from an object to another object of a different type, with default values to return when the input is null. * The default constructor assumes the output of a conversion should be null when input is null */ public NullConversion() { this(null, null); } /** * Creates a Conversion from an object to another object of a different type, with default values to return when the input is null. * * @param valueOnNullInput default value of type O to be returned when the input object I is null. Used when {@link NullConversion#execute(Object)} is invoked. * @param valueOnNullOutput default value of type I to be returned when an input of type I is null. Used when {@link NullConversion#revert(Object)} is invoked. */ public NullConversion(O valueOnNullInput, I valueOnNullOutput) { this.valueOnNullInput = valueOnNullInput; this.valueOnNullOutput = valueOnNullOutput; } /** * Converts the given instance of type I to an instance of O * * @param input the input value of type I to be converted to an object of type O * * @return the conversion result, or the value of {@link NullConversion#valueOnNullInput} if the input object is null. */ @Override public O execute(I input) { if (input == null) { return valueOnNullInput; } return fromInput(input); } /** * Creates an instance of O from a I object * * @param input The object of type I to be converted to O * * @return an instance of O, converted from the I input. */ protected abstract O fromInput(I input); /** * Converts a value of type O back to a value of type I * * @param input the input of type O to be converted to an output I * * @return the conversion result, or the value of {@link NullConversion#valueOnNullOutput} if the input object is null. */ @Override public I revert(O input) { if (input == null) { return valueOnNullOutput; } return undo(input); } /** * Converts a value of type O back to I. * @param input the input object to be converted to I * @return the conversion result */ protected abstract I undo(O input); /** * returns a default value of type O to be returned when the input of type I is null. Used when {@link NullConversion#execute(Object)} is invoked. * * @return the default value of type O used when converting from a null I */ public O getValueOnNullInput() { return valueOnNullInput; } /** * returns default instance of I to be returned when an input of type O is null. Used when {@link NullConversion#revert(Object)} is invoked. * * @return the default I instance used when converting from a null O */ public I getValueOnNullOutput() { return valueOnNullOutput; } /** * defines the default value of type O which should be returned when {@link NullConversion#execute(Object)} is invoked with a null I.. * * @param valueOnNullInput the default value of type T when converting from a null input */ public void setValueOnNullInput(O valueOnNullInput) { this.valueOnNullInput = valueOnNullInput; } /** * defines the default value of type I which should be returned when {@link NullConversion#revert(Object)} is invoked with a null O. * * @param valueOnNullOutput a default value of type I when converting from a null input */ public void setValueOnNullOutput(I valueOnNullOutput) { this.valueOnNullOutput = valueOnNullOutput; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/NullStringConversion.java000066400000000000000000000063151400120543400335330ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.common.*; import java.util.*; /** * Converts Strings to null and vice versa * *

This class supports multiple representations of null values. For example, you can define conversions from different Strings such as "N/A, ?, -" to null. * *

The reverse conversion from a null to String (in {@link NullStringConversion#revert(Object)} will return the first String provided in this class constructor if the object is null. *

Using the previous example, a call to {@link NullStringConversion#revert(Object)} will produce "N/A". * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class NullStringConversion implements Conversion { private final Set nullStrings = new HashSet(); private final String defaultNullString; /** * Creates conversions from Strings to null. *

The list of Strings that identify nulls are mandatory. * @param nullRepresentations Strings that identify a true value. The first element will be returned when executing {@link NullStringConversion#revert(Object)} */ public NullStringConversion(String... nullRepresentations) { ArgumentUtils.noNulls("Null representation strings", nullRepresentations); Collections.addAll(nullStrings, nullRepresentations); this.defaultNullString = nullRepresentations[0]; } /** * Converts an Object to null. The string representation of the object will be used to match the string elements provided in the constructor. * @param input an Object to be converted to null. * @return null if the string representation of the object matches any one of the Strings provided in the constructor of this class. Otherwise, the original object will be returned. */ @Override public Object execute(Object input) { if (input == null) { return null; } if (nullStrings.contains(String.valueOf(input))) { return null; } return input; } /** * Converts a null input to a String representation. The String returned will be the first element provided in the constructor of this class. * @param input an Object that, if null, will be transformed to a String. * @return If the input is null, the string representation for null objects. Otherwise, the original object will be returned. */ @Override public Object revert(Object input) { if (input == null) { return defaultNullString; } else { return input; } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/NumericConversion.java000066400000000000000000000265661400120543400330460ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import java.math.BigDecimal; import java.math.BigInteger; import java.text.*; import java.util.*; /** * Converts Strings to instances of {@link java.lang.Number} and vice versa. * *

This class supports multiple Number formats. For example, you can define conversions from Numbers represented by different Strings such as "1,000,000.00 and $5.00". *

Extending classes must implement the {@link NumericConversion#configureFormatter(DecimalFormat)} method to provide specific configuration to the DecimalFormat instance. *

The reverse conversion from a Number to String (in {@link NumericConversion#revert(Number)} will return a formatted String using the pattern provided in this class constructor *

The numeric patterns must follows the pattern rules of {@link java.text.DecimalFormat} * * @param The type of numbers supported by this conversion class. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see java.text.DecimalFormat */ public abstract class NumericConversion extends ObjectConversion implements FormattedConversion { private DecimalFormat[] formatters = new DecimalFormat[0]; private String[] formats = new String[0]; private final ParsePosition position = new ParsePosition(0); private Class numberType = Number.class; /** * Defines a conversion from String to {@link java.lang.Number} using a sequence of acceptable numeric patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param valueIfStringIsNull default Number to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Number input is null. Used when {@link NumericConversion#revert(Number)} is invoked. * @param numericFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. */ public NumericConversion(T valueIfStringIsNull, String valueIfObjectIsNull, String... numericFormats) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.noNulls("Numeric formats", numericFormats); this.formats = numericFormats.clone(); this.formatters = new DecimalFormat[numericFormats.length]; for (int i = 0; i < numericFormats.length; i++) { String numericFormat = numericFormats[i]; formatters[i] = new DecimalFormat(numericFormat); configureFormatter(formatters[i]); } } /** * Defines a conversion from String to {@link java.lang.Number} using a sequence of acceptable numeric patterns. * * @param valueIfStringIsNull default Number to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Number input is null. Used when {@link NumericConversion#revert(Number)} is invoked. * @param numericFormatters list formatters of acceptable numeric patterns. The first formatter in this sequence will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. */ public NumericConversion(T valueIfStringIsNull, String valueIfObjectIsNull, DecimalFormat... numericFormatters) { super(valueIfStringIsNull, valueIfObjectIsNull); ArgumentUtils.noNulls("Numeric formatters", numericFormatters); this.formatters = numericFormatters.clone(); this.formats = new String[numericFormatters.length]; for (int i = 0; i < numericFormatters.length; i++) { formats[i] = numericFormatters[i].toPattern(); } } /** * Defines a conversion from String to {@link java.lang.Number} using a sequence of acceptable numeric patterns. The patterns * must be added to this conversion class through the {@link #addFormat(String, String...)} method. * * @param valueIfStringIsNull default Number to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Number input is null. Used when {@link NumericConversion#revert(Number)} is invoked. */ public NumericConversion(T valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Defines a conversion from String to {@link java.lang.Number} using a sequence of acceptable numeric patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param numericFormats list of acceptable numeric patterns. The first pattern in this sequence will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. */ public NumericConversion(String... numericFormats) { this(null, null, numericFormats); } /** * Defines a conversion from String to {@link java.lang.Number} using a sequence of acceptable numeric patterns. * This constructor assumes the output of a conversion should be null when input is null * * @param numericFormatters list formatters of acceptable numeric patterns. The first formatter in this sequence will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. */ public NumericConversion(DecimalFormat... numericFormatters) { this(null, null, numericFormatters); } /** * Defines a conversion from String to {@link java.lang.Number} using a sequence of acceptable numeric patterns. The patterns * must be added to this conversion class through the {@link #addFormat(String, String...)} method. * * This constructor assumes the output of a conversion should be null when input is null */ public NumericConversion() { super(); } /** * Returns the implementation of {@code Number} that should be used when converting numeric data. * * @return the implementation of {@code Number} that should be used when converting numeric data. */ public Class getNumberType() { return this.numberType; } /** * Defines a specific implementation of {@code Number} that should be used when converting numeric data. * * @param numberType the implementation of {@code Number} that should be used when converting numeric data. */ public void setNumberType(Class numberType) { this.numberType = numberType; } @Override public DecimalFormat[] getFormatterObjects() { return formatters; } /** * Method called by the constructor of this class to apply custom configurations to each formatter instantiated with the numeric formats provided. * * @param formatter a DecimalFormat instance initialized with one of the patterns provided in the constructor of this class. */ protected abstract void configureFormatter(DecimalFormat formatter); /** * Converts a formatted numeric String to an instance of Number. *

The pattern in the formatted input must match one of the numeric patterns provided in the constructor of this class. * * @param input the String containing a formatted number which must be converted to a number * * @return the Number instance containing the value represented by the given String, or the value of {@link ObjectConversion#getValueIfStringIsNull()} if the String input is null. */ @SuppressWarnings("unchecked") @Override protected T fromString(String input) { for (int i = 0; i < formatters.length; i++) { position.setIndex(0); T out = (T) formatters[i].parse(input, position); if (formatters.length == 1 || position.getIndex() == input.length()) { if (out == null || numberType == Number.class) { return out; } else if (numberType == Double.class) { return (T) Double.valueOf(out.doubleValue()); } else if (numberType == Float.class) { return (T) Float.valueOf(out.floatValue()); } else if (numberType == BigDecimal.class) { if (out instanceof BigDecimal) { return out; } return (T) new BigDecimal(String.valueOf(out)); } else if (numberType == BigInteger.class) { if (out instanceof BigInteger) { return out; } return (T) BigInteger.valueOf(out.longValue()); } else if (numberType == Long.class) { return (T) Long.valueOf(out.longValue()); } else if (numberType == Integer.class) { return (T) Integer.valueOf(out.intValue()); } else if (numberType == Short.class) { return (T) Short.valueOf(out.shortValue()); } else if (numberType == Byte.class) { return (T) Byte.valueOf(out.byteValue()); } return out; } } DataProcessingException exception = new DataProcessingException("Cannot parse '{value}' as a valid number. Supported formats are: " + Arrays.toString(formats)); exception.setValue(input); throw exception; } /** * Converts Number to a formatted numeric String. *

The pattern used to generate the formatted number is the first numeric pattern provided in the constructor of this class * * @param input the Number to be converted to a String * * @return a formatted numeric String representing the value provided by the given Number, or the value of {@link ObjectConversion#getValueIfObjectIsNull()} if the Number parameter is null. */ @Override public String revert(T input) { if (input == null) { return super.revert(null); } for (DecimalFormat formatter : formatters) { try { return formatter.format(input); } catch (Throwable ex) { //ignore and continue } } DataProcessingException exception = new DataProcessingException("Cannot format '{value}'. No valid formatters were defined."); exception.setValue(input); throw exception; } /** * Adds a new numeric pattern to be used to parse input Strings and convert them to numbers. * * @param format a numeric pattern. The first pattern added to this class will be used to convert a Number into a String in {@link NumericConversion#revert(Number)}. * @param formatOptions a sequence of properties and their values, used to configure the underlying formatter. Each element must be specified as {@code property_name=property_value}, * e.g. options={"decimalSeparator=,", "maximumFractionDigits=3"} */ public void addFormat(String format, String... formatOptions) { DecimalFormat formatter = new DecimalFormat(format); configureFormatter(formatter); AnnotationHelper.applyFormatSettings(formatter, formatOptions); this.formats = Arrays.copyOf(formats, formats.length + 1); this.formatters = Arrays.copyOf(formatters, formatters.length + 1); formats[formats.length - 1] = format; formatters[formatters.length - 1] = formatter; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/ObjectConversion.java000066400000000000000000000122231400120543400326330ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Default implementation for conversions from an input String to Objects of a given type T * *

Extending classes must implement a proper String to T conversion in {@link ObjectConversion#fromString(String)} *

This abstract class provides default results for conversions when the input is null. *

It also provides a default implementation of {@link ObjectConversion#revert(Object)} that returns the result of input.toString() * * @param The object type resulting from conversions of String values. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public abstract class ObjectConversion extends NullConversion { /** * Creates a Conversion from String to an Object with default values to return when the input is null. * The default constructor assumes the output of a conversion should be null when input is null */ public ObjectConversion() { super(null, null); } /** * Creates a Conversion from String to an Object with default values to return when the input is null. * * @param valueIfStringIsNull default value of type T to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when an input of type T is null. Used when {@link ObjectConversion#revert(Object)} is invoked. */ public ObjectConversion(T valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts the given String to an instance of T * * @param input the input String to be converted to an object of type T * * @return the conversion result, or the value of {@link ObjectConversion#getValueIfStringIsNull()} if the input String is null. */ @Override public T execute(String input) { return super.execute(input); } /** * Creates an instance of T from a String representation. * * @param input The String to be converted to T * * @return an instance of T, converted from the String input. */ protected final T fromInput(String input) { return fromString(input); } /** * Creates an instance of T from a String representation. * * @param input The String to be converted to T * * @return an instance of T, converted from the String input. */ protected abstract T fromString(String input); /** * Converts a value of type T back to a String *

This is a general implementation that simply returns the result of input.toString() * * @param input the input of type T to be converted to a String * * @return the conversion result, or the value of {@link ObjectConversion#getValueIfObjectIsNull()} if the input object is null. */ @Override public String revert(T input) { return super.revert(input); } @Override protected final String undo(T input) { return String.valueOf(input); } /** * returns a default value of type T to be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * * @return the default value of type T used when converting from a null input */ public T getValueIfStringIsNull() { return getValueOnNullInput(); } /** * returns default String value to be returned when an input of type T is null. Used when {@link ObjectConversion#revert(Object)} is invoked. * * @return the default String value used when converting from a null input */ public String getValueIfObjectIsNull() { return getValueOnNullOutput(); } /** * defines a default value of type T which should be returned when the input String is null. Used when {@link ObjectConversion#execute(String)} is invoked. * * @param valueIfStringIsNull the default value of type T when converting from a null input */ public void setValueIfStringIsNull(T valueIfStringIsNull) { setValueOnNullInput(valueIfStringIsNull); } /** * returns default value of type T which should be returned when the input String is null. Used when {@link ObjectConversion#revert(Object)} is invoked. * * @param valueIfObjectIsNull a default value of type T when converting from a null input */ public void setValueIfObjectIsNull(String valueIfObjectIsNull) { setValueOnNullOutput(valueIfObjectIsNull); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/RegexConversion.java000066400000000000000000000054011400120543400324770ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Replaces contents of a given input String, identified by a regular expression, with a replacement String. * * The {@link RegexConversion#revert(String)} implements the same behavior of {@link RegexConversion#execute(String)}. Null inputs produce null outputs. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class RegexConversion implements Conversion { private final String replaceRegex; private final String replacement; /** * Creates a conversion that matches contents identified by the given regular expression and replaces them by the given replacement String. * @param replaceRegex the regular expression used to match contents of a given input String * @param replacement the replacement content to replace any contents matched by the given regular expression */ public RegexConversion(String replaceRegex, String replacement) { this.replaceRegex = replaceRegex; this.replacement = replacement; } /** * Executes the regular expression provided in the constructor of this class against the input and replaces any matched content with the replacement String. * Equivalent to {@link RegexConversion#revert(String)} * @param input The input to have contents matched by the regular expression and replaced * @return The String resulting from the content replacement */ @Override public String execute(String input) { if (input == null) { return null; } return input.replaceAll(replaceRegex, replacement); } /** * Executes the regular expression provided in the constructor of this class against the input and replaces any matched content with the replacement String. * Equivalent to {@link RegexConversion#execute(String)} * @param input The input to have contents matched by the regular expression and replaced * @return The String resulting from the content replacement */ @Override public String revert(String input) { return execute(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/ShortConversion.java000066400000000000000000000036461400120543400325350ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts Strings to Shorts and vice versa * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class ShortConversion extends ObjectConversion { /** * Creates a Conversion from String to Short with default values to return when the input is null. * This default constructor assumes the output of a conversion should be null when input is null */ public ShortConversion() { super(); } /** * Creates a Conversion from String to Short with default values to return when the input is null. * @param valueIfStringIsNull default Short value to be returned when the input String is null. Used when {@link ShortConversion#execute(String)} is invoked. * @param valueIfObjectIsNull default String value to be returned when a Short input is null. Used when {@code revert(Short)} is invoked. */ public ShortConversion(Short valueIfStringIsNull, String valueIfObjectIsNull) { super(valueIfStringIsNull, valueIfObjectIsNull); } /** * Converts a String to Short. */ @Override protected Short fromString(String input) { return Short.valueOf(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/ToStringConversion.java000066400000000000000000000022451400120543400332010ustar00rootroot00000000000000/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.conversions; /** * Converts any non-null object to its String representation. */ public class ToStringConversion extends NullConversion { public ToStringConversion() { } public ToStringConversion(Object valueOnNullInput, Object valueOnNullOutput) { super(valueOnNullInput, valueOnNullOutput); } @Override protected Object fromInput(Object input) { if (input != null) { return input.toString(); } return null; } @Override protected Object undo(Object input) { return execute(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/TrimConversion.java000066400000000000000000000062001400120543400323360ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Removes leading and trailing white spaces from an input String * * The {@link TrimConversion#revert(String)} implements the same behavior of {@link TrimConversion#execute(String)}. Null inputs produce null outputs. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class TrimConversion implements Conversion { private final int length; /** * Creates a trim conversion that removes leading and trailing whitespaces of any input String. */ public TrimConversion() { this.length = -1; } /** * Creates a trim-to-length conversion that limits the length of any resulting String. Input Strings are trimmed, and * if the resulting String has more characters than the given limit, any characters over the given limit will be discarded. * * @param length the maximum number of characters of any String returned by this conversion. */ public TrimConversion(int length) { if (length < 0) { throw new IllegalArgumentException("Maximum trim length must be positive"); } this.length = length; } /** * Removes leading and trailing white spaces from the input and returns the result. * Equivalent to {@link TrimConversion#revert(String)} * * @param input the String to be trimmed * * @return the input String without leading and trailing white spaces, or null if the input is null. */ @Override public String execute(String input) { if (input == null) { return null; } if (input.length() == 0) { return input; } if (length != -1) { int begin = 0; while (begin < input.length() && input.charAt(begin) <= ' ') { begin++; } if (begin == input.length()) { return ""; } int end = begin + (length < input.length() ? length : input.length()) - 1; if (end >= input.length()) { end = input.length() - 1; } while (input.charAt(end) <= ' ') { end--; } return input.substring(begin, end + 1); } return input.trim(); } /** * Removes leading and trailing white spaces from the input and returns the result. * Equivalent to {@link TrimConversion#execute(String)} * * @param input the String to be trimmed * * @return the input String without leading and trailing white spaces, or null if the input is null. */ @Override public String revert(String input) { return execute(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/UpperCaseConversion.java000066400000000000000000000037471400120543400333270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Converts an input String to its upper case representation * * The {@link UpperCaseConversion#revert(String)} implements the same behavior of {@link UpperCaseConversion#execute(String)}. Null inputs produce null outputs. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class UpperCaseConversion implements Conversion { /** * Applies the toUpperCase operation in the input and returns the result. * Equivalent to {@link UpperCaseConversion#revert(String)} * @param input the String to be converted to upper case * @return the upper case representation of the given input, or null if the input is null. */ @Override public String execute(String input) { if (input == null) { return null; } return input.toUpperCase(); } /** * Applies the toUpperCase operation in the input and returns the result. * Equivalent to {@link UpperCaseConversion#execute(String)} * @param input the String to be converted to upper case * @return the upper case representation of the given input, or null if the input is null. */ @Override public String revert(String input) { return execute(input); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/ValidatedConversion.java000066400000000000000000000111211400120543400333160ustar00rootroot00000000000000/* * Copyright (c) 2018. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.conversions; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import java.util.*; import java.util.regex.*; /** * Performs one or more validations against the values of a given record. */ public class ValidatedConversion implements Conversion { private final String regexToMatch; private final boolean nullable; private final boolean allowBlanks; private final Set oneOf; private final Set noneOf; private final Matcher matcher; private final Validator[] validators; public ValidatedConversion() { this(false, false, null, null, null); } public ValidatedConversion(String regexToMatch) { this(false, false, null, null, regexToMatch); } public ValidatedConversion(boolean nullable, boolean allowBlanks) { this(nullable, allowBlanks, null, null, null); } public ValidatedConversion(boolean nullable, boolean allowBlanks, String[] oneOf, String[] noneOf, String regexToMatch) { this(nullable, allowBlanks, oneOf, noneOf, regexToMatch, null); } public ValidatedConversion(boolean nullable, boolean allowBlanks, String[] oneOf, String[] noneOf, String regexToMatch, Class[] validators) { this.regexToMatch = regexToMatch; this.matcher = regexToMatch == null || regexToMatch.isEmpty() ? null : Pattern.compile(regexToMatch).matcher(""); this.nullable = nullable; this.allowBlanks = allowBlanks; this.oneOf = oneOf == null || oneOf.length == 0 ? null : new HashSet(Arrays.asList(oneOf)); this.noneOf = noneOf == null || noneOf.length == 0 ? null : new HashSet(Arrays.asList(noneOf)); this.validators = validators == null || validators.length == 0 ? new Validator[0] : instantiateValidators(validators); } private Validator[] instantiateValidators(Class[] validators) { Validator[] out = new Validator[validators.length]; for (int i = 0; i < validators.length; i++) { out[i] = (Validator) AnnotationHelper.newInstance(Validator.class, validators[i], ArgumentUtils.EMPTY_STRING_ARRAY); } return out; } @Override public Object execute(Object input) { validate(input); return input; } @Override public Object revert(Object input) { validate(input); return input; } protected void validate(Object value) { DataValidationException e = null; String str = null; if (value == null) { if (nullable) { if (noneOf != null && noneOf.contains(null)) { e = new DataValidationException("Value '{value}' is not allowed."); } else { return; } } else { if (oneOf != null && oneOf.contains(null)) { return; } else { e = new DataValidationException("Null values not allowed."); } } } else { str = String.valueOf(value); if (str.trim().isEmpty()) { if (allowBlanks) { if (noneOf != null && noneOf.contains(str)) { e = new DataValidationException("Value '{value}' is not allowed."); } else { return; } } else { if (oneOf != null && oneOf.contains(str)) { return; } else { e = new DataValidationException("Blanks are not allowed. '{value}' is blank."); } } } if (matcher != null && e == null) { boolean match; synchronized (matcher) { match = matcher.reset(str).matches(); } if (!match) { e = new DataValidationException("Value '{value}' does not match expected pattern: '" + regexToMatch + "'"); } } } if (oneOf != null && !oneOf.contains(str)) { e = new DataValidationException("Value '{value}' is not allowed. Expecting one of: " + oneOf); } if (e == null && noneOf != null && noneOf.contains(str)) { e = new DataValidationException("Value '{value}' is not allowed."); } for (int i = 0; e == null && i < validators.length; i++) { String error = validators[i].validate(value); if (error != null && !error.trim().isEmpty()) { e = new DataValidationException("Value '{value}' didn't pass validation: " + error); } } if (e != null) { e.setValue(value); throw e; } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/conversions/Validator.java000066400000000000000000000030741400120543400313100ustar00rootroot00000000000000/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.conversions; /** * Defines a custom validation process to be executed when reading or writing * values into a field of a java bean that is annotated with {@link com.univocity.parsers.annotations.Validate} * * @param the expected type of the value to be validated */ public interface Validator { /** * Executes the required validations over a given value, returning * any validation error messages that are applicable. * * If no validation errors are found, returns a blank {@code String} or {@code null} * * @param value the value to be validated * @return a validation error message if the given value fails the validation process. * If the value is acceptable this method can return either a blank {@code String} or {@code null} */ String validate(T value); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/000077500000000000000000000000001400120543400247375ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/Csv.java000066400000000000000000000062611400120543400263420ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; /** * This class provides default configurations using CSV formats commonly used for parsing/writing. */ public class Csv { /** * Provides a basic CSV configuration that allows parsing CSV files produced by Microsoft Excel. * * @return a pre-configured {@link CsvParserSettings} object with suggested settings * for parsing CSV files produced by Microsoft Excel. */ public static CsvParserSettings parseExcel() { CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\r\n"); settings.getFormat().setComment('\0'); settings.setParseUnescapedQuotes(false); settings.setSkipEmptyLines(false); settings.trimValues(false); return settings; } /** * Provides a basic CSV configuration for parsing CSV files in accordance with the * rules established by RFC 4180 * * @return a pre-configured {@link CsvParserSettings} object with suggested settings for parsing * CSV using the RFC 4180 rules. */ public static CsvParserSettings parseRfc4180() { CsvParserSettings settings = parseExcel(); settings.setNormalizeLineEndingsWithinQuotes(false); return settings; } /** * Provides a basic CSV configuration that allows writing CSV files that can be read by Microsoft Excel. * * @return a pre-configured {@link CsvWriterSettings} object with suggested settings for generating * CSV files that can be read by Microsoft Excel. */ public static CsvWriterSettings writeExcel() { CsvWriterSettings settings = new CsvWriterSettings(); settings.getFormat().setLineSeparator("\r\n"); settings.getFormat().setComment('\0'); settings.setEmptyValue(null); settings.setSkipEmptyLines(false); settings.trimValues(false); return settings; } /** * Provides a basic CSV configuration for writing CSV files in accordance with the * rules established by RFC 4180 * * @return a pre-configured {@link CsvWriterSettings} object with the settings required to generate * CSV files in accordance with the rules established by RFC 4180 */ public static CsvWriterSettings writeRfc4180() { CsvWriterSettings settings = writeExcel(); settings.setNormalizeLineEndingsWithinQuotes(false); settings.setQuoteEscapingEnabled(true); return settings; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvFormat.java000077500000000000000000000211051400120543400275100ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; import java.util.*; /** * The CSV format configuration. In addition to the default configuration in {@link Format}, the CSV format defines: * *

    *
  • delimiter (defaults to ','): the field delimiter character. Used to separate individual fields in a CSV record (where the record is usually a line of text with multiple fields). *

    e.g. the value a , b is parsed as [ a ][ b ]

  • *
  • quote (defaults to '"'): character used for escaping values where the field delimiter is part of the value. *

    e.g. the value " a , b " is parsed as [ a , b ] (instead of [ a ][ b ]

  • *
  • quoteEscape (defaults to '"'): character used for escaping the quote character inside an already quoted value *

    e.g. the value " "" a , b "" " is parsed as [ " a , b " ] (instead of [ " a ][ b " ] or [ "" a , b "" ])

  • *
  • charToEscapeQuoteEscaping (defaults to '\0' - undefined): character used for escaping the escape for the quote character *

    e.g. if the quoteEscape and charToEscapeQuoteEscaping are set to '\', the value " \\\" a , b \\\" " is parsed as [ \" a , b \" ]

  • *
* * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.Format */ public class CsvFormat extends Format { private char quote = '"'; private char quoteEscape = '"'; private String delimiter = ","; private Character charToEscapeQuoteEscaping = null; /** * Returns the character used for escaping values where the field delimiter is part of the value. Defaults to '"' * * @return the quote character */ public char getQuote() { return quote; } /** * Defines the character used for escaping values where the field delimiter is part of the value. Defaults to '"' * * @param quote the quote character */ public void setQuote(char quote) { this.quote = quote; } /** * Identifies whether or not a given character is used for escaping values where the field delimiter is part of the value * * @param ch the character to be verified * * @return true if the given character is the character used for escaping values, false otherwise */ public boolean isQuote(char ch) { return this.quote == ch; } /** * Returns the character used for escaping quotes inside an already quoted value. Defaults to '"' * * @return the quote escape character */ public char getQuoteEscape() { return quoteEscape; } /** * Defines the character used for escaping quotes inside an already quoted value. Defaults to '"' * * @param quoteEscape the quote escape character */ public void setQuoteEscape(char quoteEscape) { this.quoteEscape = quoteEscape; } /** * Identifies whether or not a given character is used for escaping quotes inside an already quoted value. * * @param ch the character to be verified * * @return true if the given character is the quote escape character, false otherwise */ public boolean isQuoteEscape(char ch) { return this.quoteEscape == ch; } /** * Returns the field delimiter character. Defaults to ',' * * @return the field delimiter character */ public char getDelimiter() { if (delimiter.length() > 1) { throw new UnsupportedOperationException("Delimiter '" + delimiter + "' has more than one character. Use method getDelimiterString()"); } return delimiter.charAt(0); } /** * Returns the field delimiter sequence. * * @return the field delimiter as a {@code String}. */ public String getDelimiterString() { return delimiter; } /** * Defines the field delimiter character. Defaults to ',' * * @param delimiter the field delimiter character */ public void setDelimiter(char delimiter) { this.delimiter = String.valueOf(delimiter); } /** * Defines the field delimiter as a sequence of characters. Defaults to ',' * * @param delimiter the field delimiter sequence. */ public void setDelimiter(String delimiter) { if (delimiter == null) { throw new IllegalArgumentException("Delimiter cannot be null"); } if (delimiter.isEmpty()) { throw new IllegalArgumentException("Delimiter cannot be empty"); } this.delimiter = delimiter; } /** * Identifies whether or not a given character represents a field delimiter * * @param ch the character to be verified * * @return true if the given character is the field delimiter character, false otherwise */ public boolean isDelimiter(char ch) { if (delimiter.length() > 1) { throw new UnsupportedOperationException("Delimiter '" + delimiter + "' has more than one character. Use method isDelimiter(String)"); } return this.delimiter.charAt(0) == ch; } /** * Identifies whether or not a given character represents a field delimiter * * @param sequence the character sequence to be verified * * @return true if the given sequence is the field delimiter character sequence, false otherwise */ public boolean isDelimiter(String sequence) { return this.delimiter.equals(sequence); } /** * Returns the character used to escape the character used for escaping quotes defined by {@link #getQuoteEscape()}. * For example, if the quote escape is set to '\', and the quoted value ends with: \", as in the following example: * *

* [ " a\\", b ] *

* * Then: *
    *
  • If the character to escape the '\' is undefined, the record won't be parsed. The parser will read characters: [a],[\],["],[,],[ ],[b] and throw an error because it cannot find a closing quote
  • *
  • If the character to escape the '\' is defined as '\', the record will be read with 2 values: [a\] and [b]
  • *
* Defaults to '\0' (undefined) * * @return the character to escape the character used for escaping quotes defined */ public final char getCharToEscapeQuoteEscaping() { if (charToEscapeQuoteEscaping == null) { //not provided by the user if (quote == quoteEscape) { return '\0'; //not required } else { return quoteEscape; } } return charToEscapeQuoteEscaping; } /** * Defines the character used to escape the character used for escaping quotes defined by {@link #getQuoteEscape()}. * For example, if the quote escape is set to '\', and the quoted value ends with: \", as in the following example: * *

* [ " a\\", b ] *

* * Then: *
    *
  • If the character to escape the '\' is undefined, the record won't be parsed. The parser will read characters: [a],[\],["],[,],[ ],[b] and throw an error because it cannot find a closing quote
  • *
  • If the character to escape the '\' is defined as '\', the record will be read with 2 values: [a\] and [b]
  • *
* Defaults to '\0' (undefined) * * @param charToEscapeQuoteEscaping the character to escape the character used for escaping quotes defined */ public final void setCharToEscapeQuoteEscaping(char charToEscapeQuoteEscaping) { this.charToEscapeQuoteEscaping = charToEscapeQuoteEscaping; } /** * Identifies whether or not a given character is used to escape the character used for escaping quotes defined by {@link #getQuoteEscape()}. * * @param ch the character to be verified * * @return true if the given character is used to escape the quote escape character, false otherwise */ public final boolean isCharToEscapeQuoteEscaping(char ch) { char current = getCharToEscapeQuoteEscaping(); return current != '\0' && current == ch; } @Override protected TreeMap getConfiguration() { TreeMap out = new TreeMap(); out.put("Quote character", quote); out.put("Quote escape character", quoteEscape); out.put("Quote escape escape character", charToEscapeQuoteEscaping); out.put("Field delimiter", delimiter); return out; } @Override public final CsvFormat clone() { return (CsvFormat) super.clone(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvFormatDetector.java000066400000000000000000000334671400120543400312150ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.*; import java.util.*; import java.util.Map.*; /** * An {@link InputAnalysisProcess} to detect column delimiters, quotes and quote escapes in a CSV input. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public abstract class CsvFormatDetector implements InputAnalysisProcess { private final int MAX_ROW_SAMPLES; private final char comment; private final char suggestedDelimiter; private final char normalizedNewLine; private final int whitespaceRangeStart; private char[] allowedDelimiters; private char[] delimiterPreference; private final char suggestedQuote; private final char suggestedQuoteEscape; /** * Builds a new {@code CsvFormatDetector} * * @param maxRowSamples the number of row samples to collect before analyzing the statistics * @param settings the configuration provided by the user with potential defaults in case the detection is unable to discover the proper column * delimiter or quote character. * @param whitespaceRangeStart starting range of characters considered to be whitespace. */ public CsvFormatDetector(int maxRowSamples, CsvParserSettings settings, int whitespaceRangeStart) { this.MAX_ROW_SAMPLES = maxRowSamples; this.whitespaceRangeStart = whitespaceRangeStart; allowedDelimiters = settings.getDelimitersForDetection(); if (allowedDelimiters != null && allowedDelimiters.length > 0) { suggestedDelimiter = allowedDelimiters[0]; delimiterPreference = allowedDelimiters.clone(); Arrays.sort(allowedDelimiters); } else { String delimiter = settings.getFormat().getDelimiterString(); suggestedDelimiter = delimiter.length() > 1 ? ',' : settings.getFormat().getDelimiter(); allowedDelimiters = new char[0]; delimiterPreference = allowedDelimiters; } normalizedNewLine = settings.getFormat().getNormalizedNewline(); comment = settings.getFormat().getComment(); suggestedQuote = settings.getFormat().getQuote(); suggestedQuoteEscape = settings.getFormat().getQuoteEscape(); } protected Map calculateTotals(List> symbolsPerRow) { Map out = new HashMap(); for (Map rowStats : symbolsPerRow) { for (Map.Entry symbolStats : rowStats.entrySet()) { Character symbol = symbolStats.getKey(); Integer count = symbolStats.getValue(); Integer total = out.get(symbol); if (total == null) { total = 0; } out.put(symbol, total + count); } } return out; } @Override public void execute(char[] characters, int length) { Set allSymbols = new HashSet(); Map symbols = new HashMap(); Map escape = new HashMap(); List> symbolsPerRow = new ArrayList>(); int doubleQuoteCount = 0; int singleQuoteCount = 0; int i; char inQuote = '\0'; boolean afterNewLine = true; for (i = 0; i < length; i++) { char ch = characters[i]; if (afterNewLine && ch == comment) { while (++i < length) { ch = characters[i]; if (ch == '\r' || ch == '\n' || ch == normalizedNewLine) { if (ch == '\r' && i + 1 < characters.length && characters[i + 1] == '\n') { i++; } break; } } continue; } if (ch == '"' || ch == '\'') { if (inQuote == ch) { //closing quotes (potentially) if (ch == '"') { doubleQuoteCount++; } else { singleQuoteCount++; } if (i + 1 < length) { char next = characters[i + 1]; if (Character.isLetterOrDigit(next) || (next <= ' ' && whitespaceRangeStart < next && next != '\n' && next != '\r')) { //no special characters after quote, might be escaping //special character before (potentially) closing quote, might be an escape char prev = characters[i - 1]; if (!Character.isLetterOrDigit(prev) && prev != '\n' && prev != '\r') { increment(escape, prev); } } } inQuote = '\0'; } else if (inQuote == '\0') { char prev = '\0'; int j = i; while (prev <= ' ' && --j >= 0) { prev = characters[j]; } if (j < 0 || !Character.isLetterOrDigit(prev)) { inQuote = ch; } } continue; } if (inQuote != '\0') { //keep looping until the quote is closed. continue; } afterNewLine = false; if (isSymbol(ch)) { //counts all symbols. Skips letters and digits allSymbols.add(ch); increment(symbols, ch); } else if ((ch == '\r' || ch == '\n' || ch == normalizedNewLine) && symbols.size() > 0) { //got a newline and collected some symbols? Good! afterNewLine = true; symbolsPerRow.add(symbols); if (symbolsPerRow.size() == MAX_ROW_SAMPLES) { break; } symbols = new HashMap(); } } if (symbols.size() > 0 && length < characters.length) { symbolsPerRow.add(symbols); } if (length >= characters.length && i >= length && symbolsPerRow.size() > 1) { // if got to the end of the buffer, discard last row. It's probably incomplete anyway. symbolsPerRow.remove(symbolsPerRow.size() - 1); } Map totals = calculateTotals(symbolsPerRow); Map sums = new HashMap(); Set toRemove = new HashSet(); //combines the number of symbols found in each row and sums the difference. for (Map prev : symbolsPerRow) { for (Map current : symbolsPerRow) { for (Character symbol : allSymbols) { Integer previousCount = prev.get(symbol); Integer currentCount = current.get(symbol); if (previousCount == null && currentCount == null) { // got a symbol that does not appear in all rows? Discard it. toRemove.add(symbol); } if (previousCount == null || currentCount == null) { continue; } increment(sums, symbol, Math.abs(previousCount - currentCount)); // we expect to always get 0 or close to 0 here, so the symbol occurs in all rows } } } if (toRemove.size() == sums.size()) { //will discard all symbols. Stick with the symbols that showed up more consistently across all rows. Map lineCount = new HashMap(); for (i = 0; i < symbolsPerRow.size(); i++) { for (Character symbolInRow : symbolsPerRow.get(i).keySet()) { Integer count = lineCount.get(symbolInRow); if (count == null) { count = 0; } lineCount.put(symbolInRow, count + 1); } } Integer highestLineCount = null; for (Map.Entry e : lineCount.entrySet()) { if (highestLineCount == null || highestLineCount < e.getValue()) { highestLineCount = e.getValue(); } } Character bestCandidate = null; for (Map.Entry e : lineCount.entrySet()) { if (e.getValue().equals(highestLineCount)) { if (bestCandidate == null) { bestCandidate = e.getKey(); } else { // multiple characters can be the delimiter, unable to detect reliably. bestCandidate = null; break; } } } if (bestCandidate != null) { toRemove.remove(bestCandidate); } } sums.keySet().removeAll(toRemove); if (allowedDelimiters.length > 0) { Set toRetain = new HashSet(); for (char c : allowedDelimiters) { toRetain.add(c); } sums.keySet().retainAll(toRetain); } char delimiter = pickDelimiter(sums, totals); char quote; if (doubleQuoteCount == 0 && singleQuoteCount == 0) { quote = suggestedQuote; } else { quote = doubleQuoteCount >= singleQuoteCount ? '"' : '\''; } escape.remove(delimiter); char quoteEscape = doubleQuoteCount == 0 && singleQuoteCount == 0 ? suggestedQuoteEscape : max(escape, totals, quote); apply(delimiter, quote, quoteEscape); } protected char pickDelimiter(Map sums, Map totals) { char delimiterMax = max(sums, totals, suggestedDelimiter); char delimiterMin = min(sums, totals, suggestedDelimiter); if (delimiterMin == ' ' || delimiterMax == ' ') { boolean hasOtherDelimiters = false; for (Map.Entry e : sums.entrySet()) { if (e.getValue() == 0 && e.getKey() != ' ') { hasOtherDelimiters = true; break; } } if (hasOtherDelimiters) { totals.remove(' '); delimiterMax = max(sums, totals, suggestedDelimiter); delimiterMin = min(sums, totals, suggestedDelimiter); } } char delimiter; out: if (delimiterMax != delimiterMin) { if (sums.get(delimiterMin) == 0 && sums.get(delimiterMax) != 0) { delimiter = delimiterMin; break out; } for (char c : delimiterPreference) { if (c == delimiterMin) { delimiter = delimiterMin; break out; } else if (c == delimiterMax) { delimiter = delimiterMax; break out; } } if (totals.get(delimiterMin) > totals.get(delimiterMax)) { delimiter = delimiterMin; break out; } delimiter = delimiterMax; } else { delimiter = delimiterMax; } return delimiter; } /** * Increments the number associated with a character in a map by 1 * * @param map the map of characters and their numbers * @param symbol the character whose number should be increment */ protected void increment(Map map, char symbol) { increment(map, symbol, 1); } /** * Increments the number associated with a character in a map * * @param map the map of characters and their numbers * @param symbol the character whose number should be increment * @param incrementSize the size of the increment */ protected void increment(Map map, char symbol, int incrementSize) { Integer count = map.get(symbol); if (count == null) { count = 0; } map.put(symbol, count + incrementSize); } /** * Returns the character with the lowest associated number. * * @param map the map of characters and their numbers * @param defaultChar the default character to return in case the map is empty * * @return the character with the lowest number associated. */ protected char min(Map map, Map totals, char defaultChar) { return getChar(map, totals, defaultChar, true); } /** * Returns the character with the highest associated number. * * @param map the map of characters and their numbers * @param defaultChar the default character to return in case the map is empty * * @return the character with the highest number associated. */ protected char max(Map map, Map totals, char defaultChar) { return getChar(map, totals, defaultChar, false); } /** * Returns the character with the highest or lowest associated number. * * @param map the map of characters and their numbers * @param defaultChar the default character to return in case the map is empty * @param min a flag indicating whether to return the character associated with the lowest number in the map. * If {@code false} then the character associated with the highest number found will be returned. * * @return the character with the highest/lowest number associated. */ protected char getChar(Map map, Map totals, char defaultChar, boolean min) { int val = min ? Integer.MAX_VALUE : Integer.MIN_VALUE; for (Entry e : map.entrySet()) { int sum = e.getValue(); if ((min && sum <= val) || (!min && sum >= val)) { char newChar = e.getKey(); if (val == sum) { Integer currentTotal = totals.get(defaultChar); Integer newTotal = totals.get(newChar); if (currentTotal != null && newTotal != null) { if (currentTotal.equals(newTotal)) { int defIndex = ArgumentUtils.indexOf(delimiterPreference, defaultChar, 0); int newIndex = ArgumentUtils.indexOf(delimiterPreference, newChar, 0); if (defIndex != -1 && newIndex != -1) { defaultChar = defIndex < newIndex ? defaultChar : newChar; } } else if ((min && newTotal > currentTotal) || (!min && newTotal > currentTotal)) { defaultChar = newChar; } } else if (isSymbol(newChar)) { defaultChar = newChar; } } else { val = sum; defaultChar = newChar; } } } return defaultChar; } protected boolean isSymbol(char ch) { return isAllowedDelimiter(ch) || ch != comment && !Character.isLetterOrDigit(ch) && (ch == '\t' || ch >= ' '); } protected boolean isAllowedDelimiter(char ch) { return Arrays.binarySearch(allowedDelimiters, ch) >= 0; } /** * Applies the discovered CSV format elements to the {@link CsvParser} * * @param delimiter the discovered delimiter character * @param quote the discovered quote character * @param quoteEscape the discovered quote escape character. */ protected abstract void apply(char delimiter, char quote, char quoteEscape); } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvParser.java000077500000000000000000000554241400120543400275270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.EOFException; import com.univocity.parsers.common.input.*; import java.io.*; import static com.univocity.parsers.csv.UnescapedQuoteHandling.*; /** * A very fast CSV parser implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see CsvFormat * @see CsvParserSettings * @see CsvWriter * @see AbstractParser */ public final class CsvParser extends AbstractParser { private boolean parseUnescapedQuotes; private boolean parseUnescapedQuotesUntilDelimiter; private boolean backToDelimiter; private final boolean doNotEscapeUnquotedValues; private final boolean keepEscape; private final boolean keepQuotes; private boolean unescaped; private char prev; private char delimiter; private char[] multiDelimiter; private char quote; private char quoteEscape; private char escapeEscape; private char newLine; private final DefaultCharAppender whitespaceAppender; private final boolean normalizeLineEndingsInQuotes; private UnescapedQuoteHandling quoteHandling; private final String nullValue; private final int maxColumnLength; private final String emptyValue; private final boolean trimQuotedLeading; private final boolean trimQuotedTrailing; private char[] delimiters; private int match = 0; private int formatDetectorRowSampleCount; /** * The CsvParser supports all settings provided by {@link CsvParserSettings}, and requires this configuration to be properly initialized. * * @param settings the parser configuration */ public CsvParser(CsvParserSettings settings) { super(settings); parseUnescapedQuotes = settings.isParseUnescapedQuotes(); parseUnescapedQuotesUntilDelimiter = settings.isParseUnescapedQuotesUntilDelimiter(); doNotEscapeUnquotedValues = !settings.isEscapeUnquotedValues(); keepEscape = settings.isKeepEscapeSequences(); keepQuotes = settings.getKeepQuotes(); normalizeLineEndingsInQuotes = settings.isNormalizeLineEndingsWithinQuotes(); nullValue = settings.getNullValue(); emptyValue = settings.getEmptyValue(); maxColumnLength = settings.getMaxCharsPerColumn(); trimQuotedTrailing = settings.getIgnoreTrailingWhitespacesInQuotes(); trimQuotedLeading = settings.getIgnoreLeadingWhitespacesInQuotes(); formatDetectorRowSampleCount = settings.getFormatDetectorRowSampleCount(); updateFormat(settings.getFormat()); whitespaceAppender = new ExpandingCharAppender(10, "", whitespaceRangeStart); this.quoteHandling = settings.getUnescapedQuoteHandling(); if (quoteHandling == null) { if (parseUnescapedQuotes) { if (parseUnescapedQuotesUntilDelimiter) { quoteHandling = STOP_AT_DELIMITER; } else { quoteHandling = STOP_AT_CLOSING_QUOTE; } } else { quoteHandling = RAISE_ERROR; } } else { backToDelimiter = quoteHandling == BACK_TO_DELIMITER; parseUnescapedQuotesUntilDelimiter = quoteHandling == STOP_AT_DELIMITER || quoteHandling == SKIP_VALUE || backToDelimiter; parseUnescapedQuotes = quoteHandling != RAISE_ERROR; } } @Override protected final void parseRecord() { if (multiDelimiter == null) { parseSingleDelimiterRecord(); } else { parseMultiDelimiterRecord(); } } private final void parseSingleDelimiterRecord() { if (ch <= ' ' && ignoreLeadingWhitespace && whitespaceRangeStart < ch) { ch = input.skipWhitespace(ch, delimiter, quote); } while (ch != newLine) { if (ch <= ' ' && ignoreLeadingWhitespace && whitespaceRangeStart < ch) { ch = input.skipWhitespace(ch, delimiter, quote); } if (ch == delimiter || ch == newLine) { output.emptyParsed(); } else { unescaped = false; prev = '\0'; if (ch == quote) { input.enableNormalizeLineEndings(normalizeLineEndingsInQuotes); int len = output.appender.length(); if (len == 0) { String value = input.getQuotedString(quote, quoteEscape, escapeEscape, maxColumnLength, delimiter, newLine, keepQuotes, keepEscape, trimQuotedLeading, trimQuotedTrailing); if (value != null) { output.valueParsed(value == "" ? emptyValue : value); input.enableNormalizeLineEndings(true); try { ch = input.nextChar(); if (ch == delimiter) { try { ch = input.nextChar(); if (ch == newLine) { output.emptyParsed(); } } catch (EOFException e) { output.emptyParsed(); return; } } } catch (EOFException e) { return; } continue; } } else if (len == -1 && input.skipQuotedString(quote, quoteEscape, delimiter, newLine)) { output.valueParsed(); try { ch = input.nextChar(); if (ch == delimiter) { try { ch = input.nextChar(); if (ch == newLine) { output.emptyParsed(); } } catch (EOFException e) { output.emptyParsed(); return; } } } catch (EOFException e) { return; } continue; } output.trim = trimQuotedTrailing; parseQuotedValue(); input.enableNormalizeLineEndings(true); if (!(unescaped && quoteHandling == BACK_TO_DELIMITER && output.appender.length() == 0)) { output.valueParsed(); } } else if (doNotEscapeUnquotedValues) { String value = null; int len = output.appender.length(); if (len == 0) { value = input.getString(ch, delimiter, ignoreTrailingWhitespace, nullValue, maxColumnLength); } if (value != null) { output.valueParsed(value); ch = input.getChar(); } else { if (len != -1) { output.trim = ignoreTrailingWhitespace; ch = output.appender.appendUntil(ch, input, delimiter, newLine); } else { if (input.skipString(ch, delimiter)) { ch = input.getChar(); } else { ch = output.appender.appendUntil(ch, input, delimiter, newLine); } } output.valueParsed(); } } else { output.trim = ignoreTrailingWhitespace; parseValueProcessingEscape(); output.valueParsed(); } } if (ch != newLine) { ch = input.nextChar(); if (ch == newLine) { output.emptyParsed(); } } } } private void skipValue() { output.appender.reset(); output.appender = NoopCharAppender.getInstance(); if (multiDelimiter == null) { ch = NoopCharAppender.getInstance().appendUntil(ch, input, delimiter, newLine); } else { for (; match < multiDelimiter.length && ch != newLine; ch = input.nextChar()) { if (multiDelimiter[match] == ch) { match++; } else { match = 0; } } } } private void handleValueSkipping(boolean quoted) { switch (quoteHandling) { case SKIP_VALUE: skipValue(); break; case RAISE_ERROR: throw new TextParsingException(context, "Unescaped quote character '" + quote + "' inside " + (quoted ? "quoted" : "") + " value of CSV field. To allow unescaped quotes, set 'parseUnescapedQuotes' to 'true' in the CSV parser settings. Cannot parse CSV input."); } } private void handleUnescapedQuoteInValue() { switch (quoteHandling) { case BACK_TO_DELIMITER: case STOP_AT_CLOSING_QUOTE: case STOP_AT_DELIMITER: output.appender.append(quote); prev = ch; parseValueProcessingEscape(); break; default: handleValueSkipping(false); break; } } private int nextDelimiter() { if (multiDelimiter == null) { return output.appender.indexOfAny(delimiters, 0); } else { int lineEnd = output.appender.indexOf(newLine, 0); int delimiter = output.appender.indexOf(multiDelimiter, 0); return lineEnd != -1 && lineEnd < delimiter ? lineEnd : delimiter; } } private boolean handleUnescapedQuote() { unescaped = true; switch (quoteHandling) { case BACK_TO_DELIMITER: int pos; int lastPos = 0; while ((pos = nextDelimiter()) != -1) { lastPos = pos; String value = output.appender.substring(0, pos); if (keepQuotes && output.appender.charAt(pos - 1) == quote) { value += quote; } output.valueParsed(value); if (output.appender.charAt(pos) == newLine) { output.pendingRecords.add(output.rowParsed()); output.appender.remove(0, pos + 1); continue; } if (multiDelimiter == null) { output.appender.remove(0, pos + 1); } else { output.appender.remove(0, pos + multiDelimiter.length); } } if (keepQuotes && input.lastIndexOf(quote) > lastPos) { output.appender.append(quote); } output.appender.append(ch); prev = '\0'; if (multiDelimiter == null) { parseQuotedValue(); } else { parseQuotedValueMultiDelimiter(); } return true; case STOP_AT_CLOSING_QUOTE: case STOP_AT_DELIMITER: output.appender.append(quote); output.appender.append(ch); prev = ch; if (multiDelimiter == null) { parseQuotedValue(); } else { parseQuotedValueMultiDelimiter(); } return true; //continue; default: handleValueSkipping(true); return false; } } private void processQuoteEscape() { if (ch == quoteEscape && prev == escapeEscape && escapeEscape != '\0') { if (keepEscape) { output.appender.append(escapeEscape); } output.appender.append(quoteEscape); ch = '\0'; } else if (prev == quoteEscape) { if (ch == quote) { if (keepEscape) { output.appender.append(quoteEscape); } output.appender.append(quote); ch = '\0'; } else { output.appender.append(prev); } } else if (ch == quote && prev == quote) { output.appender.append(quote); } else if (prev == quote) { //unescaped quote detected handleUnescapedQuoteInValue(); } } private void parseValueProcessingEscape() { while (ch != delimiter && ch != newLine) { if (ch != quote && ch != quoteEscape) { if (prev == quote) { //unescaped quote detected handleUnescapedQuoteInValue(); return; } output.appender.append(ch); } else { processQuoteEscape(); } prev = ch; ch = input.nextChar(); } } private void parseQuotedValue() { if (prev != '\0' && parseUnescapedQuotesUntilDelimiter) { if (quoteHandling == SKIP_VALUE) { skipValue(); return; } if (!keepQuotes) { output.appender.prepend(quote); } ch = input.nextChar(); output.trim = ignoreTrailingWhitespace; ch = output.appender.appendUntil(ch, input, delimiter, newLine); } else { if (keepQuotes && prev == '\0') { output.appender.append(quote); } ch = input.nextChar(); if (trimQuotedLeading && ch <= ' ' && output.appender.length() == 0) { while ((ch = input.nextChar()) <= ' ') ; } while (true) { if (prev == quote && (ch <= ' ' && whitespaceRangeStart < ch || ch == delimiter || ch == newLine)) { break; } if (ch != quote && ch != quoteEscape) { if (prev == quote) { //unescaped quote detected if (handleUnescapedQuote()) { if (quoteHandling == SKIP_VALUE) { break; } else { return; } } else { return; } } if (prev == quoteEscape && quoteEscape != '\0') { output.appender.append(quoteEscape); } ch = output.appender.appendUntil(ch, input, quote, quoteEscape, escapeEscape); prev = ch; ch = input.nextChar(); } else { processQuoteEscape(); prev = ch; ch = input.nextChar(); if (unescaped && (ch == delimiter || ch == newLine)) { return; } } } // handles whitespaces after quoted value: whitespaces are ignored. Content after whitespaces may be parsed if 'parseUnescapedQuotes' is enabled. if (ch != delimiter && ch != newLine && ch <= ' ' && whitespaceRangeStart < ch) { whitespaceAppender.reset(); do { //saves whitespaces after value whitespaceAppender.append(ch); ch = input.nextChar(); //found a new line, go to next record. if (ch == newLine) { if (keepQuotes) { output.appender.append(quote); } return; } } while (ch <= ' ' && whitespaceRangeStart < ch && ch != delimiter); //there's more stuff after the quoted value, not only empty spaces. if (ch != delimiter && parseUnescapedQuotes) { if (output.appender instanceof DefaultCharAppender) { //puts the quote before whitespaces back, then restores the whitespaces output.appender.append(quote); ((DefaultCharAppender) output.appender).append(whitespaceAppender); } //the next character is not the escape character, put it there if (parseUnescapedQuotesUntilDelimiter || ch != quote && ch != quoteEscape) { output.appender.append(ch); } //sets this character as the previous character (may be escaping) //calls recursively to keep parsing potentially quoted content prev = ch; parseQuotedValue(); } else if (keepQuotes) { output.appender.append(quote); } } else if (keepQuotes) { output.appender.append(quote); } if (ch != delimiter && ch != newLine) { throw new TextParsingException(context, "Unexpected character '" + ch + "' following quoted value of CSV field. Expecting '" + delimiter + "'. Cannot parse CSV input."); } } } @Override protected final InputAnalysisProcess getInputAnalysisProcess() { if (settings.isDelimiterDetectionEnabled() || settings.isQuoteDetectionEnabled()) { return new CsvFormatDetector(formatDetectorRowSampleCount, settings, whitespaceRangeStart) { @Override protected void apply(char delimiter, char quote, char quoteEscape) { if (settings.isDelimiterDetectionEnabled()) { CsvParser.this.delimiter = delimiter; CsvParser.this.delimiters[0] = delimiter; } if (settings.isQuoteDetectionEnabled()) { CsvParser.this.quote = quote; CsvParser.this.quoteEscape = quoteEscape; } } }; } return null; } /** * Returns the CSV format detected when one of the following settings is enabled: *
    *
  • {@link CommonParserSettings#isLineSeparatorDetectionEnabled()}
  • *
  • {@link CsvParserSettings#isDelimiterDetectionEnabled()}
  • *
  • {@link CsvParserSettings#isQuoteDetectionEnabled()}
  • *
* * The detected format will be available once the parsing process is initialized (i.e. when {@link AbstractParser#beginParsing(Reader) runs}. * * @return the detected CSV format, or {@code null} if no detection has been enabled or if the parsing process has not been started yet. */ public final CsvFormat getDetectedFormat() { CsvFormat out = null; if (settings.isDelimiterDetectionEnabled()) { out = settings.getFormat().clone(); out.setDelimiter(this.delimiter); } if (settings.isQuoteDetectionEnabled()) { out = out == null ? settings.getFormat().clone() : out; out.setQuote(quote); out.setQuoteEscape(quoteEscape); } if (settings.isLineSeparatorDetectionEnabled()) { out = out == null ? settings.getFormat().clone() : out; out.setLineSeparator(input.getLineSeparator()); } return out; } @Override protected final boolean consumeValueOnEOF() { if (ch == quote) { if (prev == quote) { if (keepQuotes) { output.appender.append(quote); } return true; } else { if (!unescaped) { output.appender.append(quote); } } } boolean out = prev != '\0' && ch != delimiter && ch != newLine && ch != comment; ch = prev = '\0'; if (match > 0) { saveMatchingCharacters(); return true; } return out; } /** * Allows changing the format of the input on the fly. * * @param format the new format to use. */ public final void updateFormat(CsvFormat format) { newLine = format.getNormalizedNewline(); multiDelimiter = format.getDelimiterString().toCharArray(); if (multiDelimiter.length == 1) { multiDelimiter = null; delimiter = format.getDelimiter(); delimiters = new char[]{delimiter, newLine}; } else { delimiters = new char[]{multiDelimiter[0], newLine}; } quote = format.getQuote(); quoteEscape = format.getQuoteEscape(); escapeEscape = format.getCharToEscapeQuoteEscaping(); } private void skipWhitespace() { while (ch <= ' ' && match < multiDelimiter.length && ch != newLine && ch != quote && whitespaceRangeStart < ch) { ch = input.nextChar(); if (multiDelimiter[match] == ch) { if (matchDelimiter()) { output.emptyParsed(); ch = input.nextChar(); } } } saveMatchingCharacters(); } private void saveMatchingCharacters() { if (match > 0) { if (match < multiDelimiter.length) { output.appender.append(multiDelimiter, 0, match); } match = 0; } } private boolean matchDelimiter() { while (ch == multiDelimiter[match]) { match++; if (match == multiDelimiter.length) { break; } ch = input.nextChar(); } if (multiDelimiter.length == match) { match = 0; return true; } if (match > 0) { saveMatchingCharacters(); } return false; } private boolean matchDelimiterAfterQuote() { while (ch == multiDelimiter[match]) { match++; if (match == multiDelimiter.length) { break; } ch = input.nextChar(); } if (multiDelimiter.length == match) { match = 0; return true; } return false; } private void parseMultiDelimiterRecord() { if (ch <= ' ' && ignoreLeadingWhitespace && whitespaceRangeStart < ch) { skipWhitespace(); } while (ch != newLine) { if (ch <= ' ' && ignoreLeadingWhitespace && whitespaceRangeStart < ch) { skipWhitespace(); } if (ch == newLine || matchDelimiter()) { output.emptyParsed(); } else { unescaped = false; prev = '\0'; if (ch == quote && output.appender.length() == 0) { input.enableNormalizeLineEndings(normalizeLineEndingsInQuotes); output.trim = trimQuotedTrailing; parseQuotedValueMultiDelimiter(); input.enableNormalizeLineEndings(true); if (!(unescaped && quoteHandling == BACK_TO_DELIMITER && output.appender.length() == 0)) { output.valueParsed(); } } else if (doNotEscapeUnquotedValues) { appendUntilMultiDelimiter(); if (ignoreTrailingWhitespace) { output.appender.updateWhitespace(); } output.valueParsed(); } else { output.trim = ignoreTrailingWhitespace; parseValueProcessingEscapeMultiDelimiter(); output.valueParsed(); } } if (ch != newLine) { ch = input.nextChar(); if (ch == newLine) { output.emptyParsed(); } } } } private void appendUntilMultiDelimiter() { while (match < multiDelimiter.length && ch != newLine) { if (multiDelimiter[match] == ch) { match++; if (match == multiDelimiter.length) { break; } } else { if (match > 0) { saveMatchingCharacters(); continue; } output.appender.append(ch); } ch = input.nextChar(); } saveMatchingCharacters(); } private void parseQuotedValueMultiDelimiter() { if (prev != '\0' && parseUnescapedQuotesUntilDelimiter) { if (quoteHandling == SKIP_VALUE) { skipValue(); return; } if (!keepQuotes) { output.appender.prepend(quote); } ch = input.nextChar(); output.trim = ignoreTrailingWhitespace; appendUntilMultiDelimiter(); } else { if (keepQuotes && prev == '\0') { output.appender.append(quote); } ch = input.nextChar(); if (trimQuotedLeading && ch <= ' ' && output.appender.length() == 0) { while ((ch = input.nextChar()) <= ' ') ; } while (true) { if (prev == quote && (ch <= ' ' && whitespaceRangeStart < ch || ch == newLine)) { break; } if (prev == quote && matchDelimiter()) { if (keepQuotes) { output.appender.append(quote); } return; } if (ch != quote && ch != quoteEscape) { if (prev == quote) { //unescaped quote detected if (handleUnescapedQuote()) { if (quoteHandling == SKIP_VALUE) { break; } else { return; } } else { return; } } if (prev == quoteEscape && quoteEscape != '\0') { output.appender.append(quoteEscape); } ch = output.appender.appendUntil(ch, input, quote, quoteEscape, escapeEscape); prev = ch; ch = input.nextChar(); } else { processQuoteEscape(); prev = ch; ch = input.nextChar(); if (unescaped && (ch == newLine || matchDelimiter())) { return; } } } } // handles whitespaces after quoted value: whitespaces are ignored. Content after whitespaces may be parsed if 'parseUnescapedQuotes' is enabled. if (ch != newLine && ch <= ' ' && whitespaceRangeStart < ch && !matchDelimiterAfterQuote()) { whitespaceAppender.reset(); do { //saves whitespaces after value whitespaceAppender.append(ch); ch = input.nextChar(); //found a new line, go to next record. if (ch == newLine) { if (keepQuotes) { output.appender.append(quote); } return; } if (matchDelimiterAfterQuote()) { return; } } while (ch <= ' ' && whitespaceRangeStart < ch); //there's more stuff after the quoted value, not only empty spaces. if (parseUnescapedQuotes && !matchDelimiterAfterQuote()) { if (output.appender instanceof DefaultCharAppender) { //puts the quote before whitespaces back, then restores the whitespaces output.appender.append(quote); ((DefaultCharAppender) output.appender).append(whitespaceAppender); } //the next character is not the escape character, put it there if (parseUnescapedQuotesUntilDelimiter || ch != quote && ch != quoteEscape) { output.appender.append(ch); } //sets this character as the previous character (may be escaping) //calls recursively to keep parsing potentially quoted content prev = ch; parseQuotedValue(); } else if (keepQuotes) { output.appender.append(quote); } } else if (keepQuotes && (!unescaped || quoteHandling == STOP_AT_CLOSING_QUOTE)) { output.appender.append(quote); } } private void parseValueProcessingEscapeMultiDelimiter() { while (ch != newLine && !matchDelimiter()) { if (ch != quote && ch != quoteEscape) { if (prev == quote) { //unescaped quote detected handleUnescapedQuoteInValue(); return; } output.appender.append(ch); } else { processQuoteEscape(); } prev = ch; ch = input.nextChar(); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvParserSettings.java000077500000000000000000000560301400120543400312420ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.*; import java.util.*; /** * This is the configuration class used by the CSV parser ({@link CsvParser}) * *

In addition to the configuration options provided by {@link CommonParserSettings}, the CSVParserSettings include: * *

    *
  • emptyValue (defaults to null): Defines a replacement string to signify an empty value (which is not a null value) *

    When reading, if the parser does not read any character from the input, and the input is within quotes, the empty is used instead of an empty string

  • *
* * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.csv.CsvParser * @see com.univocity.parsers.csv.CsvFormat * @see com.univocity.parsers.common.CommonParserSettings */ public class CsvParserSettings extends CommonParserSettings { private String emptyValue = null; private boolean parseUnescapedQuotes = true; private boolean parseUnescapedQuotesUntilDelimiter = true; private boolean escapeUnquotedValues = false; private boolean keepEscapeSequences = false; private boolean keepQuotes = false; private boolean normalizeLineEndingsWithinQuotes = true; private boolean ignoreTrailingWhitespacesInQuotes = false; private boolean ignoreLeadingWhitespacesInQuotes = false; private boolean delimiterDetectionEnabled = false; private boolean quoteDetectionEnabled = false; private UnescapedQuoteHandling unescapedQuoteHandling = null; private char[] delimitersForDetection = null; private int formatDetectorRowSampleCount = 20; /** * Returns the String representation of an empty value (defaults to null) * *

When reading, if the parser does not read any character from the input, and the input is within quotes, the empty is used instead of an empty string * * @return the String representation of an empty value */ public String getEmptyValue() { return emptyValue; } /** * Sets the String representation of an empty value (defaults to null) * *

When reading, if the parser does not read any character from the input, and the input is within quotes, the empty is used instead of an empty string * * @param emptyValue the String representation of an empty value */ public void setEmptyValue(String emptyValue) { this.emptyValue = emptyValue; } /** * Returns an instance of CharAppender with the configured limit of maximum characters per column and the default value used to represent an empty value * (when the String parsed from the input, within quotes, is empty) * *

This overrides the parent's version because the CSV parser does not rely on the appender to identify null values, but on the other hand, the appender * is required to identify empty values. * * @return an instance of CharAppender with the configured limit of maximum characters per column and the default value used to represent an empty value * (when the String parsed from the input, within quotes, is empty) */ @Override protected CharAppender newCharAppender() { int chars = getMaxCharsPerColumn(); if (chars != -1) { return new DefaultCharAppender(chars, emptyValue, getWhitespaceRangeStart()); } else { return new ExpandingCharAppender(emptyValue, getWhitespaceRangeStart()); } } /** * Returns the default CsvFormat configured to handle CSV inputs compliant to the RFC4180 standard. * * @return and instance of CsvFormat configured to handle CSV inputs compliant to the RFC4180 standard. */ @Override protected CsvFormat createDefaultFormat() { return new CsvFormat(); } /** * Indicates whether the CSV parser should accept unescaped quotes inside quoted values and parse them normally. Defaults to {@code true}. * * @return a flag indicating whether or not the CSV parser should accept unescaped quotes inside quoted values. * * @deprecated use {@link #getUnescapedQuoteHandling()} instead. The configuration returned by {@link #getUnescapedQuoteHandling()} will override this * setting if not null. */ @Deprecated public boolean isParseUnescapedQuotes() { return parseUnescapedQuotes || (unescapedQuoteHandling != null && unescapedQuoteHandling != UnescapedQuoteHandling.RAISE_ERROR); } /** * Configures how to handle unescaped quotes inside quoted values. If set to {@code true}, the parser will parse the quote normally as part of the value. * If set the {@code false}, a {@link TextParsingException} will be thrown. Defaults to {@code true}. * * @param parseUnescapedQuotes indicates whether or not the CSV parser should accept unescaped quotes inside quoted values. * * @deprecated use {@link #setUnescapedQuoteHandling(UnescapedQuoteHandling)} instead. The configuration returned by {@link #getUnescapedQuoteHandling()} * will override this setting if not null. */ @Deprecated public void setParseUnescapedQuotes(boolean parseUnescapedQuotes) { this.parseUnescapedQuotes = parseUnescapedQuotes; } /** * Configures the parser to process values with unescaped quotes, and stop accumulating characters and consider the value parsed when a delimiter is found. * (defaults to {@code true}) * * @param parseUnescapedQuotesUntilDelimiter a flag indicating that the parser should stop accumulating values when a field delimiter character is * found when parsing unquoted and unescaped values. * * @deprecated use {@link #setUnescapedQuoteHandling(UnescapedQuoteHandling)} instead. The configuration returned by {@link #getUnescapedQuoteHandling()} * will override this setting if not null. */ @Deprecated public void setParseUnescapedQuotesUntilDelimiter(boolean parseUnescapedQuotesUntilDelimiter) { if (parseUnescapedQuotesUntilDelimiter) { parseUnescapedQuotes = true; } this.parseUnescapedQuotesUntilDelimiter = parseUnescapedQuotesUntilDelimiter; } /** * When parsing unescaped quotes, indicates the parser should stop accumulating characters and consider the value parsed when a delimiter is found. * (defaults to {@code true}) * * @return a flag indicating that the parser should stop accumulating values when a field delimiter character is * found when parsing unquoted and unescaped values. * * @deprecated use {@link #getUnescapedQuoteHandling()} instead. The configuration returned by {@link #getUnescapedQuoteHandling()} will override this * setting if not null. */ @Deprecated public boolean isParseUnescapedQuotesUntilDelimiter() { return (parseUnescapedQuotesUntilDelimiter && isParseUnescapedQuotes()) || (unescapedQuoteHandling == UnescapedQuoteHandling.STOP_AT_DELIMITER || unescapedQuoteHandling == UnescapedQuoteHandling.SKIP_VALUE); } /** * Indicates whether escape sequences should be processed in unquoted values. Defaults to {@code false}. * *

By default, this is disabled and if the input is {@code A""B,C}, the resulting value will be * {@code [A""B] and [C]} (i.e. the content is read as-is). However, if the parser is configured * to process escape sequences in unquoted values, the result will be {@code [A"B] and [C]}

* * @return true if escape sequences should be processed in unquoted values, otherwise false */ public boolean isEscapeUnquotedValues() { return escapeUnquotedValues; } /** * Configures the parser to process escape sequences in unquoted values. Defaults to {@code false}. * *

By default, this is disabled and if the input is {@code A""B,C}, the resulting value will be * {@code [A""B] and [C]} (i.e. the content is read as-is). However, if the parser is configured * to process escape sequences in unquoted values, the result will be {@code [A"B] and [C]}

* * @param escapeUnquotedValues a flag indicating whether escape sequences should be processed in unquoted values */ public void setEscapeUnquotedValues(boolean escapeUnquotedValues) { this.escapeUnquotedValues = escapeUnquotedValues; } /** * Indicates whether the parser should keep any escape sequences if they are present in the input (i.e. a quote escape sequence such as two double quotes * {@code ""} won't be replaced by a single double quote {@code "}). *

This is disabled by default

* * @return a flag indicating whether escape sequences should be kept (and not replaced) by the parser. */ public final boolean isKeepEscapeSequences() { return keepEscapeSequences; } /** * Configures the parser to keep any escape sequences if they are present in the input (i.e. a quote escape sequence such as 2 double quotes {@code ""} * won't be replaced by a single double quote {@code "}). *

This is disabled by default

* * @param keepEscapeSequences the flag indicating whether escape sequences should be kept (and not replaced) by the parser. */ public final void setKeepEscapeSequences(boolean keepEscapeSequences) { this.keepEscapeSequences = keepEscapeSequences; } /** * Returns a flag indicating whether the parser should analyze the input to discover the column delimiter character. *

Note that the detection process is not guaranteed to discover the correct column delimiter. In this case the delimiter provided by {@link * CsvFormat#getDelimiter()} will be used

* * @return a flag indicating whether the parser should analyze the input to discover the column delimiter character. */ public final boolean isDelimiterDetectionEnabled() { return delimiterDetectionEnabled; } /** * Configures the parser to analyze the input before parsing to discover the column delimiter character. *

Note that the detection process is not guaranteed to discover the correct column delimiter. * The first character in the list of delimiters allowed for detection will be used, if available, otherwise * the delimiter returned by {@link CsvFormat#getDelimiter()} will be used.

* * @param separatorDetectionEnabled the flag to enable/disable discovery of the column delimiter character. * to {@code true}, in order of priority. */ public final void setDelimiterDetectionEnabled(boolean separatorDetectionEnabled) { this.setDelimiterDetectionEnabled(separatorDetectionEnabled, new char[0]); } /** * Configures the parser to analyze the input before parsing to discover the column delimiter character. *

Note that the detection process is not guaranteed to discover the correct column delimiter. * The first character in the list of delimiters allowed for detection will be used, if available, otherwise * the delimiter returned by {@link CsvFormat#getDelimiter()} will be used.

* * @param separatorDetectionEnabled the flag to enable/disable discovery of the column delimiter character. * @param delimitersForDetection possible delimiters for detection when {@link #isDelimiterDetectionEnabled()} evaluates * to {@code true}, in order of priority. */ public final void setDelimiterDetectionEnabled(boolean separatorDetectionEnabled, char... delimitersForDetection) { this.delimiterDetectionEnabled = separatorDetectionEnabled; this.delimitersForDetection = delimitersForDetection; } /** * Returns a flag indicating whether the parser should analyze the input to discover the quote character. The quote escape will also be detected as part of * this process. *

Note that the detection process is not guaranteed to discover the correct quote & escape. * In this case the characters provided by {@link CsvFormat#getQuote()} and {@link CsvFormat#getQuoteEscape()} will be used

* * @return a flag indicating whether the parser should analyze the input to discover the quote character. The quote escape will also be detected as part of * this process. */ public final boolean isQuoteDetectionEnabled() { return quoteDetectionEnabled; } /** * Configures the parser to analyze the input before parsing to discover the quote character. The quote escape will also be detected as part of this * process. *

Note that the detection process is not guaranteed to discover the correct quote & escape. * In this case the characters provided by {@link CsvFormat#getQuote()} and {@link CsvFormat#getQuoteEscape()} will be used

* * @param quoteDetectionEnabled the flag to enable/disable discovery of the quote character. The quote escape will also be detected as part of this process. */ public final void setQuoteDetectionEnabled(boolean quoteDetectionEnabled) { this.quoteDetectionEnabled = quoteDetectionEnabled; } /** * Convenience method to turn on all format detection features in a single method call, namely: *
    *
  • {@link #setDelimiterDetectionEnabled(boolean, char[])}
  • *
  • {@link #setQuoteDetectionEnabled(boolean)}
  • *
  • {@link #setLineSeparatorDetectionEnabled(boolean)}
  • *
*/ public final void detectFormatAutomatically() { this.detectFormatAutomatically(new char[0]); } /** * Convenience method to turn on all format detection features in a single method call, namely: *
    *
  • {@link #setDelimiterDetectionEnabled(boolean, char[])}
  • *
  • {@link #setQuoteDetectionEnabled(boolean)}
  • *
  • {@link #setLineSeparatorDetectionEnabled(boolean)}
  • *
* * @param delimitersForDetection possible delimiters for detection, in order of priority. */ public final void detectFormatAutomatically(char... delimitersForDetection) { this.setDelimiterDetectionEnabled(true, delimitersForDetection); this.setQuoteDetectionEnabled(true); this.setLineSeparatorDetectionEnabled(true); } /** * Flag indicating whether the parser should replace line separators, specified in {@link Format#getLineSeparator()} * by the normalized line separator character specified in {@link Format#getNormalizedNewline()}, even on quoted values. * * This is enabled by default and is used to ensure data be read on any platform without introducing unwanted blank lines. * * For example, consider the quoted value {@code "Line1 \r\n Line2"}. If this is parsed using {@code "\r\n"} as * the line separator sequence, and the normalized new line is set to {@code '\n'} (the default), the output will be: * * {@code [Line1 \n Line2]} * * However, if the value is meant to be kept untouched, and the original line separator should be maintained, set * the {@link #normalizeLineEndingsWithinQuotes} to {@code false}. This will make the parser read the value as-is, producing: * * {@code [Line1 \r\n Line2]} * * @return {@code true} if line separators in quoted values will be normalized, {@code false} otherwise */ public boolean isNormalizeLineEndingsWithinQuotes() { return normalizeLineEndingsWithinQuotes; } /** * Configures the parser to replace line separators, specified in {@link Format#getLineSeparator()} * by the normalized line separator character specified in {@link Format#getNormalizedNewline()}, even on quoted values. * * This is enabled by default and is used to ensure data be read on any platform without introducing unwanted blank lines. * * For example, consider the quoted value {@code "Line1 \r\n Line2"}. If this is parsed using {@code "\r\n"} as * the line separator sequence, and the normalized new line is set to {@code '\n'} (the default), the output will be: * * {@code [Line1 \n Line2]} * * However, if the value is meant to be kept untouched, and the original line separator should be maintained, set * the {@link #normalizeLineEndingsWithinQuotes} to {@code false}. This will make the parser read the value as-is, producing: * * {@code [Line1 \r\n Line2]} * * @param normalizeLineEndingsWithinQuotes flag indicating whether line separators in quoted values should be replaced by * the the character specified in {@link Format#getNormalizedNewline()} . */ public void setNormalizeLineEndingsWithinQuotes(boolean normalizeLineEndingsWithinQuotes) { this.normalizeLineEndingsWithinQuotes = normalizeLineEndingsWithinQuotes; } /** * Configures the handling of values with unescaped quotes. * Defaults to {@code null}, for backward compatibility with {@link #isParseUnescapedQuotes()} and {@link #isParseUnescapedQuotesUntilDelimiter()}. * If set to a non-null value, this setting will override the configuration of {@link #isParseUnescapedQuotes()} and {@link * #isParseUnescapedQuotesUntilDelimiter()}. * * @param unescapedQuoteHandling the handling method to be used when unescaped quotes are found in the input. */ public void setUnescapedQuoteHandling(UnescapedQuoteHandling unescapedQuoteHandling) { this.unescapedQuoteHandling = unescapedQuoteHandling; } /** * Returns the method of handling values with unescaped quotes. * Defaults to {@code null}, for backward compatibility with {@link #isParseUnescapedQuotes()} and {@link #isParseUnescapedQuotesUntilDelimiter()} * If set to a non-null value, this setting will override the configuration of {@link #isParseUnescapedQuotes()} and {@link * #isParseUnescapedQuotesUntilDelimiter()}. * * @return the handling method to be used when unescaped quotes are found in the input, or {@code null} if not set. */ public UnescapedQuoteHandling getUnescapedQuoteHandling() { return this.unescapedQuoteHandling; } /** * Flag indicating whether the parser should keep enclosing quote characters in the values parsed from the input. *

Defaults to {@code false}

* * @return a flag indicating whether enclosing quotes should be maintained when parsing quoted values. */ public boolean getKeepQuotes() { return keepQuotes; } /** * Configures the parser to keep enclosing quote characters in the values parsed from the input. *

Defaults to {@code false}

* * @param keepQuotes flag indicating whether enclosing quotes should be maintained when parsing quoted values. */ public void setKeepQuotes(boolean keepQuotes) { this.keepQuotes = keepQuotes; } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); out.put("Empty value", emptyValue); out.put("Unescaped quote handling", unescapedQuoteHandling); out.put("Escape unquoted values", escapeUnquotedValues); out.put("Keep escape sequences", keepEscapeSequences); out.put("Keep quotes", keepQuotes); out.put("Normalize escaped line separators", normalizeLineEndingsWithinQuotes); out.put("Autodetect column delimiter", delimiterDetectionEnabled); out.put("Autodetect quotes", quoteDetectionEnabled); out.put("Delimiters for detection", Arrays.toString(delimitersForDetection)); out.put("Ignore leading whitespaces in quotes", ignoreLeadingWhitespacesInQuotes); out.put("Ignore trailing whitespaces in quotes", ignoreTrailingWhitespacesInQuotes); } @Override public final CsvParserSettings clone() { return (CsvParserSettings) super.clone(); } @Override public final CsvParserSettings clone(boolean clearInputSpecificSettings) { return (CsvParserSettings) super.clone(clearInputSpecificSettings); } /** * Returns the sequence of possible delimiters for detection when {@link #isDelimiterDetectionEnabled()} evaluates * to {@code true}, in order of priority. * * @return the possible delimiter characters, in order of priority. */ public final char[] getDelimitersForDetection() { return this.delimitersForDetection; } /** * Returns whether or not trailing whitespaces from within quoted values should be skipped (defaults to false) * * Note: if {@link #keepQuotes} evaluates to {@code true}, values won't be trimmed. * * @return true if trailing whitespaces from quoted values should be skipped, false otherwise */ public boolean getIgnoreTrailingWhitespacesInQuotes() { return ignoreTrailingWhitespacesInQuotes; } /** * Defines whether or not trailing whitespaces from quoted values should be skipped (defaults to false) * * Note: if {@link #keepQuotes} evaluates to {@code true}, values won't be trimmed. * * @param ignoreTrailingWhitespacesInQuotes whether trailing whitespaces from quoted values should be skipped */ public void setIgnoreTrailingWhitespacesInQuotes(boolean ignoreTrailingWhitespacesInQuotes) { this.ignoreTrailingWhitespacesInQuotes = ignoreTrailingWhitespacesInQuotes; } /** * Returns whether or not leading whitespaces from quoted values should be skipped (defaults to false) * * Note: if {@link #keepQuotes} evaluates to {@code true}, values won't be trimmed. * * @return true if leading whitespaces from quoted values should be skipped, false otherwise */ public boolean getIgnoreLeadingWhitespacesInQuotes() { return ignoreLeadingWhitespacesInQuotes; } /** * Defines whether or not leading whitespaces from quoted values should be skipped (defaults to false) * * Note: if {@link #keepQuotes} evaluates to {@code true}, values won't be trimmed. * * @param ignoreLeadingWhitespacesInQuotes whether leading whitespaces from quoted values should be skipped */ public void setIgnoreLeadingWhitespacesInQuotes(boolean ignoreLeadingWhitespacesInQuotes) { this.ignoreLeadingWhitespacesInQuotes = ignoreLeadingWhitespacesInQuotes; } /** * Configures the parser to trim any whitespaces around values extracted from within quotes. Shorthand for * {@link #setIgnoreLeadingWhitespacesInQuotes(boolean)} and {@link #setIgnoreTrailingWhitespacesInQuotes(boolean)} * * Note: if {@link #keepQuotes} evaluates to {@code true}, values won't be trimmed. * * @param trim a flag indicating whether whitespaces around values extracted from a quoted field should be removed */ public final void trimQuotedValues(boolean trim) { setIgnoreTrailingWhitespacesInQuotes(trim); setIgnoreLeadingWhitespacesInQuotes(trim); } /** * Returns the number of sample rows used in the CSV format auto-detection process (defaults to 20) * * @return the number of sample rows used in the CSV format auto-detection process */ public int getFormatDetectorRowSampleCount() { return formatDetectorRowSampleCount; } /** * Updates the number of sample rows used in the CSV format auto-detection process (defaults to 20) * * @param formatDetectorRowSampleCount the number of sample rows used in the CSV format auto-detection process */ public void setFormatDetectorRowSampleCount(int formatDetectorRowSampleCount) { this.formatDetectorRowSampleCount = formatDetectorRowSampleCount <= 0 ? 20 : formatDetectorRowSampleCount; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvRoutines.java000066400000000000000000000046761400120543400301030ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.routine.*; import java.io.*; /** * A collection of common routines involving the processing of CSV data. */ public class CsvRoutines extends AbstractRoutines { /** * Creates a new instance of the CSV routine class without any predefined parsing/writing configuration. */ public CsvRoutines() { this(null, null); } /** * Creates a new instance of the CSV routine class. * * @param parserSettings configuration to use for CSV parsing */ public CsvRoutines(CsvParserSettings parserSettings) { this(parserSettings, null); } /** * Creates a new instance of the CSV routine class. * * @param writerSettings configuration to use for CSV writing */ public CsvRoutines(CsvWriterSettings writerSettings) { this(null, writerSettings); } /** * Creates a new instance of the CSV routine class. * * @param parserSettings configuration to use for CSV parsing * @param writerSettings configuration to use for CSV writing */ public CsvRoutines(CsvParserSettings parserSettings, CsvWriterSettings writerSettings) { super("CSV parsing/writing routine", parserSettings, writerSettings); } @Override protected CsvParser createParser(CsvParserSettings parserSettings) { return new CsvParser(parserSettings); } @Override protected CsvWriter createWriter(Writer output, CsvWriterSettings writerSettings) { return new CsvWriter(output, writerSettings); } @Override protected CsvParserSettings createDefaultParserSettings() { return new CsvParserSettings(); } @Override protected CsvWriterSettings createDefaultWriterSettings() { return new CsvWriterSettings(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvWriter.java000066400000000000000000000340161400120543400275360ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import java.io.*; import java.nio.charset.*; import java.util.*; /** * A powerful and flexible CSV writer implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see CsvFormat * @see CsvWriterSettings * @see CsvParser * @see AbstractWriter */ public class CsvWriter extends AbstractWriter { private char delimiter; private char[] multiDelimiter; private char quoteChar; private char escapeChar; private char escapeEscape; private boolean quoteAllFields; private boolean escapeUnquoted; private boolean inputNotEscaped; private char newLine; private boolean dontProcessNormalizedNewLines; private boolean[] quotationTriggers; private char maxTrigger; private Set quotedColumns; private FieldSelector quotedFieldSelector; private boolean quoteNulls; /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. *

Important: by not providing an instance of {@link java.io.Writer} to this constructor, only the operations that write to Strings are * available.

* * @param settings the CSV writer configuration */ public CsvWriter(CsvWriterSettings settings) { this((Writer) null, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param writer the output resource that will receive CSV records produced by this class. * @param settings the CSV writer configuration */ public CsvWriter(Writer writer, CsvWriterSettings settings) { super(writer, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive CSV records produced by this class. * @param settings the CSV writer configuration */ public CsvWriter(File file, CsvWriterSettings settings) { super(file, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive CSV records produced by this class. * @param encoding the encoding of the file * @param settings the CSV writer configuration */ public CsvWriter(File file, String encoding, CsvWriterSettings settings) { super(file, encoding, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive CSV records produced by this class. * @param encoding the encoding of the file * @param settings the CSV writer configuration */ public CsvWriter(File file, Charset encoding, CsvWriterSettings settings) { super(file, encoding, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the CSV records produced by this class. * @param settings the CSV writer configuration */ public CsvWriter(OutputStream output, CsvWriterSettings settings) { super(output, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the CSV records produced by this class. * @param encoding the encoding of the stream * @param settings the CSV writer configuration */ public CsvWriter(OutputStream output, String encoding, CsvWriterSettings settings) { super(output, encoding, settings); } /** * The CsvWriter supports all settings provided by {@link CsvWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the CSV records produced by this class. * @param encoding the encoding of the stream * @param settings the CSV writer configuration */ public CsvWriter(OutputStream output, Charset encoding, CsvWriterSettings settings) { super(output, encoding, settings); } /** * Initializes the CSV writer with CSV-specific configuration * * @param settings the CSV writer configuration */ protected final void initialize(CsvWriterSettings settings) { CsvFormat format = settings.getFormat(); this.multiDelimiter = format.getDelimiterString().toCharArray(); if (multiDelimiter.length == 1) { delimiter = multiDelimiter[0]; multiDelimiter = null; } this.quoteChar = format.getQuote(); this.escapeChar = format.getQuoteEscape(); this.escapeEscape = settings.getFormat().getCharToEscapeQuoteEscaping(); this.newLine = format.getNormalizedNewline(); this.quoteAllFields = settings.getQuoteAllFields(); this.quoteNulls = settings.getQuoteNulls(); this.escapeUnquoted = settings.isEscapeUnquotedValues(); this.inputNotEscaped = !settings.isInputEscaped(); this.dontProcessNormalizedNewLines = !settings.isNormalizeLineEndingsWithinQuotes(); this.quotationTriggers = null; this.quotedColumns = null; this.maxTrigger = 0; quotedColumns = Collections.emptySet(); quotedFieldSelector = settings.getQuotedFieldSelector(); char[] sep = format.getLineSeparator(); int triggerCount = 3 + settings.getQuotationTriggers().length + sep.length; int offset = settings.isQuoteEscapingEnabled() ? 1 : 0; char[] tmp = Arrays.copyOf(settings.getQuotationTriggers(), triggerCount + offset); if (offset == 1) { tmp[triggerCount] = quoteChar; } tmp[triggerCount - 1] = '\n'; tmp[triggerCount - 2] = '\r'; tmp[triggerCount - 3] = newLine; tmp[triggerCount - 4] = sep[0]; if (sep.length > 1) { tmp[triggerCount - 5] = sep[1]; } for (int i = 0; i < tmp.length; i++) { if (maxTrigger < tmp[i]) { maxTrigger = tmp[i]; } } if (maxTrigger != 0) { maxTrigger++; this.quotationTriggers = new boolean[maxTrigger]; Arrays.fill(quotationTriggers, false); for (int i = 0; i < tmp.length; i++) { quotationTriggers[tmp[i]] = true; } } } @Override protected void processRow(Object[] row) { if (recordCount == 0L && quotedFieldSelector != null) { int[] quotedIndexes = quotedFieldSelector.getFieldIndexes(headers); if (quotedIndexes.length > 0) { quotedColumns = new HashSet(); for (int idx : quotedIndexes) { quotedColumns.add(idx); } } } for (int i = 0; i < row.length; i++) { if (i != 0) { if (multiDelimiter == null) { appendToRow(delimiter); } else { appendToRow(multiDelimiter); } } if (dontProcessNormalizedNewLines) { appender.enableDenormalizedLineEndings(false); } boolean allowTrim = allowTrim(i); String nextElement = getStringValue(row[i]); boolean quoteOn = quoteNulls || row[i] != null; int originalLength = appender.length(); boolean isElementQuoted = append(i, quoteOn && (quoteAllFields || quotedColumns.contains(i)), allowTrim, nextElement) && quoteOn; //skipped all whitespaces and wrote nothing if (appender.length() == originalLength && !usingNullOrEmptyValue) { if (isElementQuoted) { if (nextElement == null) { append(i, false, allowTrim, nullValue); } else { append(i, true, allowTrim, emptyValue); } } else if (nextElement == null) { append(i, false, allowTrim, nullValue); } else { append(i, false, allowTrim, emptyValue); } } if (isElementQuoted) { appendToRow(quoteChar); appendValueToRow(); appendToRow(quoteChar); if (dontProcessNormalizedNewLines) { appender.enableDenormalizedLineEndings(true); } } else { appendValueToRow(); } } } private boolean matchMultiDelimiter(String element, int from) { if (from + multiDelimiter.length - 2 >= element.length()) { return false; } for (int j = 1; j < multiDelimiter.length; j++, from++) { if (element.charAt(from) != multiDelimiter[j]) { return false; } } return true; } private boolean quoteElement(int start, String element) { final int length = element.length(); if (multiDelimiter == null) { if (maxTrigger == 0) { for (int i = start; i < length; i++) { char nextChar = element.charAt(i); if (nextChar == delimiter || nextChar == newLine) { return true; } } } else { for (int i = start; i < length; i++) { char nextChar = element.charAt(i); if (nextChar == delimiter || nextChar < maxTrigger && quotationTriggers[nextChar]) { return true; } } } } else { if (maxTrigger == 0) { for (int i = start; i < length; i++) { char nextChar = element.charAt(i); if ((nextChar == multiDelimiter[0] && matchMultiDelimiter(element, i + 1)) || nextChar == newLine) { return true; } } } else { for (int i = start; i < length; i++) { char nextChar = element.charAt(i); if ((nextChar == multiDelimiter[0] && matchMultiDelimiter(element, i + 1)) || nextChar < maxTrigger && quotationTriggers[nextChar]) { return true; } } } } return false; } private boolean append(int columnIndex, boolean isElementQuoted, boolean allowTrim, String element) { if (element == null) { if (nullValue == null) { return isElementQuoted; } element = nullValue; } int start = 0; if (allowTrim && this.ignoreLeading) { start = skipLeadingWhitespace(whitespaceRangeStart, element); } final int length = element.length(); if (start < length && (element.charAt(start) == quoteChar || columnIndex == 0 && element.charAt(0) == comment)) { isElementQuoted = true; } if (isElementQuoted) { if (usingNullOrEmptyValue && length >= 2) { if (element.charAt(0) == quoteChar && element.charAt(length - 1) == quoteChar) { appender.append(element); return false; } else { appendQuoted(start, allowTrim, element); return true; } } else { appendQuoted(start, allowTrim, element); return true; } } int i = start; char ch = '\0'; if (multiDelimiter == null) { for (; i < length; i++) { ch = element.charAt(i); if (ch == quoteChar || ch == delimiter || ch == escapeChar || (ch < maxTrigger && quotationTriggers[ch])) { appender.append(element, start, i); start = i + 1; if (ch == quoteChar || ch == escapeChar) { if (quoteElement(i, element)) { appendQuoted(i, allowTrim, element); return true; } else if (escapeUnquoted) { appendQuoted(i, allowTrim, element); } else { appender.append(element, i, length); if (allowTrim && ignoreTrailing && element.charAt(length - 1) <= ' ' && whitespaceRangeStart < element.charAt(length - 1)) { appender.updateWhitespace(); } } return isElementQuoted; } else if (ch == escapeChar && inputNotEscaped && escapeEscape != '\0' && escapeUnquoted) { appender.append(escapeEscape); } else if (ch == delimiter || ch < maxTrigger && quotationTriggers[ch]) { appendQuoted(i, allowTrim, element); return true; } appender.append(ch); } } } else { for (; i < length; i++) { ch = element.charAt(i); if (ch == quoteChar || (ch == multiDelimiter[0] && matchMultiDelimiter(element, i + 1)) || ch == escapeChar || (ch < maxTrigger && quotationTriggers[ch])) { appender.append(element, start, i); start = i + 1; if (ch == quoteChar || ch == escapeChar) { if (quoteElement(i, element)) { appendQuoted(i, allowTrim, element); return true; } else if (escapeUnquoted) { appendQuoted(i, allowTrim, element); } else { appender.append(element, i, length); if (allowTrim && ignoreTrailing && element.charAt(length - 1) <= ' ' && whitespaceRangeStart < element.charAt(length - 1)) { appender.updateWhitespace(); } } return isElementQuoted; } else if (ch == escapeChar && inputNotEscaped && escapeEscape != '\0' && escapeUnquoted) { appender.append(escapeEscape); } else if ((ch == multiDelimiter[0] && matchMultiDelimiter(element, i + 1)) || ch < maxTrigger && quotationTriggers[ch]) { appendQuoted(i, allowTrim, element); return true; } appender.append(ch); } } } appender.append(element, start, i); if (allowTrim && ch <= ' ' && ignoreTrailing && whitespaceRangeStart < ch) { appender.updateWhitespace(); } return isElementQuoted; } private void appendQuoted(int start, boolean allowTrim, String element) { final int length = element.length(); int i = start; char ch = '\0'; for (; i < length; i++) { ch = element.charAt(i); if (ch == quoteChar || ch == newLine || ch == escapeChar) { appender.append(element, start, i); start = i + 1; if (ch == quoteChar && inputNotEscaped) { appender.append(escapeChar); } else if (ch == escapeChar && inputNotEscaped && escapeEscape != '\0') { appender.append(escapeEscape); } appender.append(ch); } } appender.append(element, start, i); if (allowTrim && ch <= ' ' && ignoreTrailing && whitespaceRangeStart < ch) { appender.updateWhitespace(); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/CsvWriterSettings.java000066400000000000000000000347001400120543400312570ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; import com.univocity.parsers.common.fields.*; import java.util.*; /** * This is the configuration class used by the CSV writer ({@link CsvWriter}) * *

In addition to the configuration options provided by {@link CommonWriterSettings}, the CsvWriterSettings include: * *

    *
  • emptyValue (defaults to null): Defines a replacement string to signify an empty value (which is not a null value) *

    If the writer has an empty String to write to the output, the emptyValue is used instead of an empty string

  • *
  • quoteAllFields (defaults to false): By default, only values that contain a field separator are enclosed within quotes. *

    If this property is set to true, this indicates that all written values should be enclosed within quotes (as defined in {@link CsvFormat})

  • *
* * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.csv.CsvWriter * @see com.univocity.parsers.csv.CsvFormat * @see com.univocity.parsers.common.CommonWriterSettings */ public class CsvWriterSettings extends CommonWriterSettings { private boolean escapeUnquotedValues = false; private boolean quoteAllFields = false; private boolean isInputEscaped = false; private boolean normalizeLineEndingsWithinQuotes = true; private char[] quotationTriggers = new char[0]; private boolean quoteEscapingEnabled = false; private boolean quoteNulls = true; private FieldSelector quotedFieldSelector = null; /** * Indicates that all written values should be enclosed within quotes (as defined in {@link CsvFormat}) *

(Defaults to false) * * @return true if all written values should be enclosed within quotes, false otherwise */ public boolean getQuoteAllFields() { return quoteAllFields; } /** * Indicates indicates whether or not all written values should be enclosed within quotes (as defined in {@link CsvFormat}) * *

(Defaults to false) *

By default, only values that contain a field separator are enclosed within quotes. * * @param quoteAllFields a flag indicating whether to enclose all fields within quotes */ public void setQuoteAllFields(boolean quoteAllFields) { this.quoteAllFields = quoteAllFields; } /** * Indicates whether escape sequences should be written in unquoted values. Defaults to {@code false}. * *

By default, this is disabled and if the input is {@code A""B,C}, the resulting value will be * {@code [A""B] and [C]} (i.e. the content is written as-is). However, if the writer is configured * to process escape sequences in unquoted values, the values will be written as {@code [A""""B] and [C]}

* * @return true if escape sequences should be processed in unquoted values, otherwise false */ public boolean isEscapeUnquotedValues() { return escapeUnquotedValues; } /** * Configures the writer to process escape sequences in unquoted values. Defaults to {@code false}. * *

By default, this is disabled and if the input is {@code A""B,C}, the result will be written as * {@code [A""B] and [C]} (i.e. the quotes written as-is). However, if the writer is configured * to process escape sequences in unquoted values, the values will written as {@code [A""""B] and [C]}

* * @param escapeUnquotedValues a flag indicating whether escape sequences should be processed in unquoted values */ public void setEscapeUnquotedValues(boolean escapeUnquotedValues) { this.escapeUnquotedValues = escapeUnquotedValues; } /** * Indicates that the user will provide escaped input, and the writer will not try to introduce escape sequences. The input will be written as-is. *

Warning! ensure your data is properly escaped, otherwise the writer will produce invalid CSV.

*

This is disabled by default

* * @return a flag indicating whether the escape sequences should not be introduced by the writer. */ public final boolean isInputEscaped() { return isInputEscaped; } /** * Configures the writer to prevent it to introduce escape sequences. The writer will assume the user is providing escaped input, and it will be written * as-is. *

Warning! ensure your data is properly escaped, otherwise the writer will produce invalid CSV.

*

This is disabled by default

* * @param isInputEscaped a flag indicating whether the input that will be written is already properly escaped. */ public final void setInputEscaped(boolean isInputEscaped) { this.isInputEscaped = isInputEscaped; } /** * Flag indicating whether the writer should replace the the normalized line separator character specified in {@link Format#getNormalizedNewline()} * by the sequence specified in {@link Format#getLineSeparator()}, when the value is enclosed within quotes. * * This is enabled by default and is used to ensure data be read on any platform without introducing unwanted blank lines. * * For example, consider the quoted value {@code "Line1 \n Line2"}. If this is written using {@code "\r\n"} as * the line separator sequence, and the normalized new line is set to {@code '\n'} (the default), the output will be: * * {@code [Line1 \r\n Line2]} * * However, if the value is meant to be kept untouched, and the original line separator should be maintained, set * the {@link #normalizeLineEndingsWithinQuotes} to {@code false}. This will make the writer output the value as-is, producing: * * {@code [Line1 \n Line2]} * * @return {@code true} if line separator characters in quoted values should be considered 'normalized' and replaced by the * sequence specified in {@link Format#getLineSeparator()}, {@code false} otherwise */ public boolean isNormalizeLineEndingsWithinQuotes() { return normalizeLineEndingsWithinQuotes; } /** * Flag indicating whether the writer should replace the the normalized line separator character specified in {@link Format#getNormalizedNewline()} * by the sequence specified in {@link Format#getLineSeparator()}, when the value is enclosed within quotes. * * This is enabled by default and is used to ensure data can be used on any platform without producing unrecognized line endings. * * For example, consider the quoted value {@code "Line1 \n Line2"}. If this is written using {@code "\r\n"} as * the line separator sequence, and the normalized new line is set to {@code '\n'} (the default), the output will be: * * {@code [Line1 \r\n Line2]} * * However, if the value is meant to be kept untouched, and the original line separator should be maintained, set * the {@link #normalizeLineEndingsWithinQuotes} to {@code false}. This will make the writer output the value as-is, producing: * * {@code [Line1 \n Line2]} * * @param normalizeLineEndingsWithinQuotes flag indicating that line separator characters in quoted values should be * considered 'normalized' and occurrences of {@link Format#getNormalizedNewline()} * should be replaced by the sequence specified in {@link Format#getLineSeparator()} */ public void setNormalizeLineEndingsWithinQuotes(boolean normalizeLineEndingsWithinQuotes) { this.normalizeLineEndingsWithinQuotes = normalizeLineEndingsWithinQuotes; } /** * Returns the default CsvFormat configured to produce CSV outputs compliant to the RFC4180 standard. * * @return and instance of CsvFormat configured to produce CSV outputs compliant to the RFC4180 standard. */ @Override protected CsvFormat createDefaultFormat() { return new CsvFormat(); } /** * Returns the list of characters that when present in a value to be written, will * force the output value to be enclosed in quotes. * * @return the characters that will trigger values to be quoted when present in a value to be written. */ public char[] getQuotationTriggers() { return quotationTriggers; } /** * Defines one or more "triggers" for enclosing a value within quotes. If one of the characters in the quotation trigger * list is found in a value to be written, the entire value will be enclosed in quotes. * * @param quotationTriggers a list of characters that when present in a value to be written, will * force the output value to be enclosed in quotes. */ public void setQuotationTriggers(char... quotationTriggers) { this.quotationTriggers = quotationTriggers == null ? new char[0] : quotationTriggers; } /** * Queries if a given character is a quotation trigger, i.e. a character that if present in a value to be written, * will make the CSV writer enclose the entire value within quotes. * * @param ch the character to be tested * * @return {@code true} if the given character is a quotation trigger, {@code false} otherwise. */ public boolean isQuotationTrigger(char ch) { for (int i = 0; i < quotationTriggers.length; i++) { if (quotationTriggers[i] == ch) { return true; } } return false; } /** * Indicates whether the CSV writer should escape values that contain the quote character, by enclosing the entire * value in quotes. * * For example, consider a value such as {@code [My "precious" value]}. * When quote escaping is enabled, the output will be: * * {@code ["My ""precious"" value"]} * * If disabled (the default), the value will be written as-is. Note that the CSV output will not conform to the RFC 4180 standard, * but it will still be valid as the value does not contain line separators nor the delimiter character. * * @return a flag indicating whether values containing quotes should be enclosed in quotes. */ public boolean isQuoteEscapingEnabled() { return quoteEscapingEnabled; } /** * Configures the CSV writer to escape values that contain the quote character, by enclosing the entire * value in quotes. * * For example, consider a value such as {@code [My "precious" value]}. * When quote escaping is enabled, the output will be: * * {@code ["My ""precious"" value"]} * * If disabled (the default), the value will be written as-is. Note that the CSV output will not conform to the RFC 4180 standard, * but it will still be valid as the value does not contain line separators nor the delimiter character. * * @param quoteEscapingEnabled a flag indicating whether values containing quotes should be enclosed in quotes. */ public void setQuoteEscapingEnabled(boolean quoteEscapingEnabled) { this.quoteEscapingEnabled = quoteEscapingEnabled; } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); out.put("Quote all fields", quoteAllFields); out.put("Escape unquoted values", escapeUnquotedValues); out.put("Normalize escaped line separators", normalizeLineEndingsWithinQuotes); out.put("Input escaped", isInputEscaped); out.put("Quote escaping enabled", quoteEscapingEnabled); out.put("Quotation triggers", Arrays.toString(quotationTriggers)); } @Override public final CsvWriterSettings clone() { return (CsvWriterSettings) super.clone(); } @Override public final CsvWriterSettings clone(boolean clearInputSpecificSettings) { return (CsvWriterSettings) super.clone(clearInputSpecificSettings); } /** * Returns the current selection of quoted fields (if any) * * @return the current selection of quoted fields */ final FieldSelector getQuotedFieldSelector() { return quotedFieldSelector; } /** * Replaces the current quoted field selection * * @param fieldSet the new set of selected fields * @param values the values to include to the selection * * @return the set of selected fields given in as a parameter. */ private FieldSet setFieldSet(FieldSet fieldSet, T... values) { this.quotedFieldSelector = (FieldSelector) fieldSet; fieldSet.add(values); return fieldSet; } /** * Selects fields whose values should always be written within quotes * * @param columns a selection of columns that will always be quoted * * @return the (modifiable) set of selected fields to be quoted. */ public final FieldSet quoteFields(Enum... columns) { return setFieldSet(new FieldEnumSelector(), columns); } /** * Selects fields whose values should always be written within quotes * * @param columns a selection of columns that will always be quoted * * @return the (modifiable) set of selected fields to be quoted. */ public final FieldSet quoteFields(String... columns) { return setFieldSet(new FieldNameSelector(), columns); } /** * Selects field positions whose values should always be written within quotes * * @param columns a selection of column indexes that will always be quoted * * @return the (modifiable) set of column positions to be quoted. */ public final FieldSet quoteIndexes(Integer... columns) { return setFieldSet(new FieldIndexSelector(), columns); } /** * Configures whether to quote {@code null} values sent to the {@link CsvWriter} when the corresponding column * is configured to be quoted via {@link #quoteFields(String...)}, or {@link #getQuoteAllFields()} evaluates to {@code true}. * * @param quoteNulls flag indicating whether {@code null} values should be quoted. */ public void setQuoteNulls(boolean quoteNulls) { this.quoteNulls = quoteNulls; } /** * Flag whether to quote {@code null} values sent to the {@link CsvWriter} when the corresponding column * is configured to be quoted via {@link #quoteFields(String...)}, or {@link #getQuoteAllFields()} evaluates to {@code true}. * * @return a flag indicating whether {@code null} values should be quoted. */ public boolean getQuoteNulls() { return quoteNulls; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/csv/UnescapedQuoteHandling.java000066400000000000000000000052751400120543400322050ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.common.*; /** * This enumeration is used to determine how the ({@link CsvParser}) will handle values with unescaped quotes. * * Use {@link CsvParserSettings#setUnescapedQuoteHandling(UnescapedQuoteHandling)} to configure the appropriate * handling of unescaped quotes on your input. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.csv.CsvParserSettings * @see com.univocity.parsers.csv.CsvFormat * @see com.univocity.parsers.csv.CsvParser * @see com.univocity.parsers.common.CommonParserSettings */ public enum UnescapedQuoteHandling { /** * If unescaped quotes are found in the input, accumulate the quote character and proceed parsing the value * as a quoted value, until a closing quote is found. */ STOP_AT_CLOSING_QUOTE, /** * If unescaped quotes are found in the input, consider the value as an unquoted value. This will make the parser * accumulate all characters of the current parsed value until the delimiter defined by {@link CsvFormat#getDelimiter()} is found. * If no delimiter is found in the value, the parser will continue accumulating characters from the input * until a delimiter or line ending is found. */ BACK_TO_DELIMITER, /** * If unescaped quotes are found in the input, consider the value as an unquoted value. This will make the parser * accumulate all characters until the delimiter defined by {@link CsvFormat#getDelimiter()}, or a line ending is found in the input. */ STOP_AT_DELIMITER, /** * If unescaped quotes are found in the input, the content parsed for the given value will be skipped * (until the next delimiter is found) and the value set in {@link CommonSettings#getNullValue()} will be produced instead. */ SKIP_VALUE, /** * If unescaped quotes are found in the input, a {@link TextParsingException} will be thrown. */ RAISE_ERROR, } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/000077500000000000000000000000001400120543400252435ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FieldAlignment.java000066400000000000000000000035211400120543400307710ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; /** * Alignment of text in a fixed-width field. * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public enum FieldAlignment { /** * Aligns values to the left */ LEFT, /** * Centralizes values */ CENTER, /** * Aligns values to the right */ RIGHT; /** * Calculates how many whites paces to introduce before a value so it is printed as specified by the alignment type. * @param totalLength the total length available to write for a given field * @param lengthToWrite the length of a value that will be written to the field * @return the number of whites paces to introduce in before the value to make it align as specified. */ public int calculatePadding(int totalLength, int lengthToWrite) { if (this == LEFT || totalLength <= lengthToWrite) { return 0; } if (this == RIGHT) { return totalLength - lengthToWrite; } int padding = (totalLength / 2) - (lengthToWrite / 2); if (lengthToWrite + padding > totalLength) { padding--; } return padding; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthFieldLengths.java000066400000000000000000000052341400120543400324420ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import java.util.*; /** * This class provides the name, length, alignment and padding of each field in a fixed-width record. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @deprecated This class has been modified over time and its name became misleading. Use {@link FixedWidthFields} instead. */ @Deprecated public class FixedWidthFieldLengths extends FixedWidthFields { /** * Defines a sequence of field names used to refer to columns in the input/output text of an entity, along with their lengths. * The field names defined will be used as headers, having the same effect of a call to {@link FixedWidthParserSettings#setHeaders(String...)}. * * @param fields a {@link LinkedHashMap} containing the sequence of fields to be associated with each column in the input/output, with their respective length. */ public FixedWidthFieldLengths(LinkedHashMap fields) { super(fields); } /** * Defines a sequence of field names used to refer to columns in the input/output text of an entity, along with their lengths. * The field names defined will be used as headers, having the same effect of a call to {@link FixedWidthParserSettings#setHeaders(String...)}. * * @param headers the sequence of fields to be associated with each column in the input/output * @param lengths the sequence of lengths to be associated with each given header. The size of this array must match the number of given headers. */ public FixedWidthFieldLengths(String[] headers, int[] lengths) { super(headers, lengths); } /** * Creates a new instance initialized with the lengths of all fields in a fixed-width record. * * @param fieldLengths The number lengths of all fields in a fixed-width record. All lengths must be greater than 0. */ public FixedWidthFieldLengths(int... fieldLengths) { super(fieldLengths); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthFields.java000066400000000000000000000753621400120543400313110ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import java.util.*; import java.util.Map.*; /** * This class provides the name, length, alignment and padding of each field in a fixed-width record. * * @author Univocity Software Pty Ltd - parsers@univocity.com */ public class FixedWidthFields implements Cloneable { private List fieldLengths = new ArrayList(); private List fieldsToIgnore = new ArrayList(); private List fieldNames = new ArrayList(); private List fieldAlignment = new ArrayList(); private List fieldPadding = new ArrayList(); private List paddingsToKeep = new ArrayList(); private boolean noNames = true; private int totalLength = 0; /** * Defines a sequence of field names used to refer to columns in the input/output text of an entity, along with their lengths. * The field names defined will be used as headers, having the same effect of a call to {@link FixedWidthParserSettings#setHeaders(String...)}. * * @param fields a {@link LinkedHashMap} containing the sequence of fields to be associated with each column in the input/output, with their respective length. */ public FixedWidthFields(LinkedHashMap fields) { if (fields == null || fields.isEmpty()) { throw new IllegalArgumentException("Map of fields and their lengths cannot be null/empty"); } for (Entry entry : fields.entrySet()) { String fieldName = entry.getKey(); Integer fieldLength = entry.getValue(); addField(fieldName, fieldLength); } } /** * Defines a sequence of field names used to refer to columns in the input/output text of an entity, along with their lengths. * The field names defined will be used as headers, having the same effect of a call to {@link FixedWidthParserSettings#setHeaders(String...)}. * * @param headers the sequence of fields to be associated with each column in the input/output * @param lengths the sequence of lengths to be associated with each given header. The size of this array must match the number of given headers. */ public FixedWidthFields(String[] headers, int[] lengths) { if (headers == null || headers.length == 0) { throw new IllegalArgumentException("Headers cannot be null/empty"); } if (lengths == null || lengths.length == 0) { throw new IllegalArgumentException("Field lengths cannot be null/empty"); } if (headers.length != lengths.length) { throw new IllegalArgumentException("Sequence of headers and their respective lengths must match. Got " + headers.length + " headers but " + lengths.length + " lengths"); } for (int i = 0; i < headers.length; i++) { addField(headers[i], lengths[i]); } } /** * Creates a new instance initialized with the lengths of all fields in a fixed-width record. * * @param fieldLengths The number lengths of all fields in a fixed-width record. All lengths must be greater than 0. */ public FixedWidthFields(int... fieldLengths) { for (int i = 0; i < fieldLengths.length; i++) { addField(fieldLengths[i]); } } /** * Creates a new instance initialized from {@link FixedWidth} annotations in the fields and methods of a given class. Note that * all fields should additionally have the {@link Parsed} annotation to configure header names and/or their positions. * * @param beanClass the class whose {@link FixedWidth} annotations will be processed to configure this field list. * * @deprecated use {@link #forParsing(Class)} and {@link #forWriting(Class)} to initialize the fields from the given * class and filter out getters and setters that target the same field. If the given class has any annotated methods * only the setters will be used, making it usable only for parsing. */ @Deprecated public FixedWidthFields(Class beanClass) { this(beanClass, MethodFilter.ONLY_SETTERS); } /** * Creates a new instance initialized from {@link FixedWidth} annotations in the fields and methods of a given class. Note that * all fields should additionally have the {@link Parsed} annotation to configure header names and/or their positions. * * Only setter methods will be considered as fields. * * @param beanClass the class whose {@link FixedWidth} annotations will be processed to configure this field list. * * @return a new {@link FixedWidthFields} instance built with the {@link FixedWidth} annotations found in the given class' attributes and methods (excluding getters) */ public static FixedWidthFields forParsing(Class beanClass) { return new FixedWidthFields(beanClass, MethodFilter.ONLY_SETTERS); } /** * Creates a new instance initialized from {@link FixedWidth} annotations in the fields and methods of a given class. Note that * all fields should additionally have the {@link Parsed} annotation to configure header names and/or their positions. * * Only getter methods will be considered as fields. * * @param beanClass the class whose {@link FixedWidth} annotations will be processed to configure this field list. * * @return a new {@link FixedWidthFields} instance built with the {@link FixedWidth} annotations found in the given class' attributes and methods (excluding setters) */ public static FixedWidthFields forWriting(Class beanClass) { return new FixedWidthFields(beanClass, MethodFilter.ONLY_GETTERS); } /** * Creates a new instance initialized from {@link FixedWidth} annotations in the fields of a given class. Note that * all fields should additionally have the {@link Parsed} annotation to configure header names and/or their positions. * * @param beanClass the class whose {@link FixedWidth} annotations will be processed to configure this field list. * @param methodFilter filter to apply over annotated methods when the fixed-width writer is reading data from beans (to write values to an output) * or writing values into beans (when parsing). It is used to choose either a "get" or a "set" * method annotated with {@link Parsed}, when both methods target the same field. */ private FixedWidthFields(Class beanClass, MethodFilter methodFilter) { if (beanClass == null) { throw new IllegalArgumentException("Class must not be null."); } List fieldSequence = AnnotationHelper.getFieldSequence(beanClass, true, null, methodFilter); if (fieldSequence.isEmpty()) { throw new IllegalArgumentException("Can't derive fixed-width fields from class '" + beanClass.getName() + "'. No @Parsed annotations found."); } Set fieldNamesWithoutConfig = new LinkedHashSet(); for (TransformedHeader field : fieldSequence) { if (field == null) { continue; } String fieldName = field.getHeaderName(); FixedWidth fw = AnnotationHelper.findAnnotation(field.getTarget(), FixedWidth.class); if (fw == null) { fieldNamesWithoutConfig.add(field.getTargetName()); continue; } int length = AnnotationRegistry.getValue(field.getTarget(), fw, "value", fw.value()); int from = AnnotationRegistry.getValue(field.getTarget(), fw, "from", fw.from()); int to = AnnotationRegistry.getValue(field.getTarget(), fw, "to", fw.to()); FieldAlignment alignment = AnnotationRegistry.getValue(field.getTarget(), fw, "alignment", fw.alignment()); char padding = AnnotationRegistry.getValue(field.getTarget(), fw, "padding", fw.padding()); if (length != -1) { if (from != -1 || to != -1) { throw new IllegalArgumentException("Can't initialize fixed-width field from " + field.describe() + ". " + "Can't have field length (" + length + ") defined along with position from (" + from + ") and to (" + to + ")"); } addField(fieldName, length, alignment, padding); } else if (from != -1 && to != -1) { addField(fieldName, from, to, alignment, padding); } else { throw new IllegalArgumentException("Can't initialize fixed-width field from " + field.describe() + "'. " + "Field length/position undefined defined"); } boolean keepPadding = AnnotationRegistry.getValue(field.getTarget(), fw, "keepPadding", fw.keepPadding()); setKeepPaddingFlag(keepPadding, fieldLengths.size() - 1); } if (fieldNamesWithoutConfig.size() > 0) { throw new IllegalArgumentException("Can't derive fixed-width fields from class '" + beanClass.getName() + "'. " + "The following fields don't have a @FixedWidth annotation: " + fieldNamesWithoutConfig); } } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param startPosition starting position of the field. * @param endPosition ending position of the field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int startPosition, int endPosition) { return addField(null, startPosition, endPosition, FieldAlignment.LEFT, '\0'); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param name the name of the next field. It is not validated. * @param startPosition starting position of the field. * @param endPosition ending position of the field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int startPosition, int endPosition) { return addField(name, startPosition, endPosition, FieldAlignment.LEFT, '\0'); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param name the name of the next field. It is not validated. * @param startPosition starting position of the field. * @param endPosition ending position of the field * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int startPosition, int endPosition, char padding) { return addField(name, startPosition, endPosition, FieldAlignment.LEFT, padding); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param name the name of the next field. It is not validated. * @param startPosition starting position of the field. * @param endPosition ending position of the field * @param alignment the alignment of the field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int startPosition, int endPosition, FieldAlignment alignment) { return addField(name, startPosition, endPosition, alignment, '\0'); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param startPosition starting position of the field. * @param endPosition ending position of the field * @param alignment the alignment of the field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int startPosition, int endPosition, FieldAlignment alignment) { return addField(null, startPosition, endPosition, alignment, '\0'); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param startPosition starting position of the field. * @param endPosition ending position of the field * @param alignment the alignment of the field * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int startPosition, int endPosition, FieldAlignment alignment, char padding) { return addField(null, startPosition, endPosition, alignment, padding); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param startPosition starting position of the field. * @param endPosition ending position of the field * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int startPosition, int endPosition, char padding) { return addField(null, startPosition, endPosition, FieldAlignment.LEFT, padding); } /** * Adds the range of the next field in a fixed-width record. The given range cannot overlap with previously defined fields. * Blanks will be used to fill any "gap" between record ranges when writing. * * @param name the name of the next field. It is not validated. * @param startPosition starting position of the field. * @param endPosition ending position of the field * @param alignment the alignment of the field * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int startPosition, int endPosition, FieldAlignment alignment, char padding) { int length = endPosition - startPosition; if (startPosition < totalLength) { throw new IllegalArgumentException("Start position '" + startPosition + "' overlaps with one or more fields"); } else if (startPosition > totalLength) { addField(null, startPosition - totalLength, FieldAlignment.LEFT, '\0'); fieldsToIgnore.set(fieldsToIgnore.size() - 1, Boolean.TRUE); } return addField(name, length, alignment, padding); } /** * Returns the sequence of fields to ignore. * * @return the sequence of fields to ignore. */ boolean[] getFieldsToIgnore() { boolean[] out = new boolean[fieldsToIgnore.size()]; for (int i = 0; i < fieldsToIgnore.size(); i++) { out[i] = fieldsToIgnore.get(i).booleanValue(); } return out; } /** * Returns the sequence of fields whose padding character must/must not be retained in the parsed value * @return the sequence that have an explicit 'keepPadding' flag. */ Boolean[] getKeepPaddingFlags() { return paddingsToKeep.toArray(new Boolean[0]); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField(5).addField(6)... * * @param length the length of the next field. It must be greater than 0. * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int length) { return addField(null, length, FieldAlignment.LEFT, '\0'); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField(5).addField(6)... * * @param length the length of the next field. It must be greater than 0. * @param alignment the alignment of the field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int length, FieldAlignment alignment) { return addField(null, length, alignment, '\0'); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField("field_1", 5).addField("field_2", 6)... * * @param name the name of the next field. It is not validated. * @param length the length of the next field. It must be greater than 0. * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int length) { return addField(name, length, FieldAlignment.LEFT, '\0'); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField("field_1", 5).addField("field_2", 6)... * * @param name the name of the next field. It is not validated. * @param length the length of the next field. It must be greater than 0. * @param alignment the alignment of the field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int length, FieldAlignment alignment) { return addField(name, length, alignment, '\0'); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField(5).addField(6)... * * @param length the length of the next field. It must be greater than 0. * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int length, char padding) { return addField(null, length, FieldAlignment.LEFT, padding); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField(5).addField(6)... * * @param length the length of the next field. It must be greater than 0. * @param alignment the alignment of the field * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(int length, FieldAlignment alignment, char padding) { return addField(null, length, alignment, padding); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField("field_1", 5).addField("field_2", 6)... * * @param name the name of the next field. It is not validated. * @param length the length of the next field. It must be greater than 0. * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int length, char padding) { return addField(name, length, FieldAlignment.LEFT, padding); } /** * Adds the length of the next field in a fixed-width record. This method can be chained like this: addField("field_1", 5).addField("field_2", 6)... * * @param name the name of the next field. It is not validated. * @param length the length of the next field. It must be greater than 0. * @param alignment the alignment of the field * @param padding the representation of unused space in this field * * @return the FixedWidthFields instance itself for chaining. */ public FixedWidthFields addField(String name, int length, FieldAlignment alignment, char padding) { validateLength(name, length); fieldLengths.add(length); fieldsToIgnore.add(Boolean.FALSE); fieldNames.add(NormalizedString.valueOf(name)); fieldPadding.add(padding); paddingsToKeep.add(null); if (name != null) { noNames = false; } fieldAlignment.add(alignment); totalLength += length; return this; } private void validateLength(String name, int length) { if (length < 1) { if (name == null) { throw new IllegalArgumentException("Invalid field length: " + length + " for field at index " + fieldLengths.size()); } else { throw new IllegalArgumentException("Invalid field length: " + length + " for field " + name); } } } /** * Returns the number of fields in a fixed-width record * * @return the number of fields in a fixed-width record */ public int getFieldsPerRecord() { return fieldLengths.size(); } /** * Returns the name of each field in a fixed-width record, if any * * @return the name of each field in a fixed-width record, or null if no name has been defined. */ public NormalizedString[] getFieldNames() { if (noNames) { return null; } return getSelectedElements(fieldNames).toArray(ArgumentUtils.EMPTY_NORMALIZED_STRING_ARRAY); } private List getSelectedElements(List elements) { List out = new ArrayList(); for (int i = 0; i < elements.size(); i++) { if (!fieldsToIgnore.get(i)) { out.add(elements.get(i)); } } return out; } /** * Returns a copy of the sequence of field lengths of a fixed-width record * * @return a copy of the sequence of field lengths of a fixed-width record */ public int[] getFieldLengths() { return ArgumentUtils.toIntArray(getSelectedElements(fieldLengths)); } int[] getAllLengths() { return ArgumentUtils.toIntArray(fieldLengths); } /** * Modifies the length of a given field * * @param name the name of the field whose length must be altered * @param newLength the new length of the given field */ public void setFieldLength(String name, int newLength) { if (name == null) { throw new IllegalArgumentException("Field name cannot be null"); } int index = fieldNames.indexOf(name); if (index == -1) { throw new IllegalArgumentException("Cannot find field with name '" + name + '\''); } validateLength(name, newLength); fieldLengths.set(index, newLength); } /** * Modifies the length of a given field * * @param position the position of the field whose length must be altered * @param newLength the new length of the given field */ public void setFieldLength(int position, int newLength) { validateIndex(position); validateLength("at index " + position, newLength); fieldLengths.set(position, newLength); } /** * Applies alignment to a given list of fields * * @param alignment the alignment to apply * @param positions the positions of the fields that should be aligned */ public void setAlignment(FieldAlignment alignment, int... positions) { for (int position : positions) { setAlignment(position, alignment); } } /** * Applies alignment to a given list of fields * * @param alignment the alignment to apply * @param names the names of the fields that should be aligned */ public void setAlignment(FieldAlignment alignment, String... names) { for (String name : names) { int position = indexOf(name); setAlignment(position, alignment); } } private void validateIndex(int position) { if (position < 0 && position >= fieldLengths.size()) { throw new IllegalArgumentException("No field defined at index " + position); } } /** * Returns the index of a field name. An {@code IllegalArgumentException} will be thrown if no names have been defined. * * @param fieldName the name of the field to be searched * * @return the index of the field, or -1 if it does not exist. */ public int indexOf(String fieldName) { if (noNames) { throw new IllegalArgumentException("No field names defined"); } if (fieldName == null || fieldName.trim().isEmpty()) { throw new IllegalArgumentException("Field name cannot be null/empty"); } NormalizedString normalizedFieldName = NormalizedString.valueOf(fieldName); int i = 0; for (NormalizedString name : this.fieldNames) { if (name.equals(normalizedFieldName)) { return i; } i++; } return -1; } private void setAlignment(int position, FieldAlignment alignment) { if (alignment == null) { throw new IllegalArgumentException("Alignment cannot be null"); } validateIndex(position); this.fieldAlignment.set(position, alignment); } /** * Returns the alignment of a given field. * * @param position the index of the field whose alignment will be returned * * @return the alignment of the field */ public FieldAlignment getAlignment(int position) { validateIndex(position); return fieldAlignment.get(position); } /** * Returns the alignment of a given field. An {@code IllegalArgumentException} will be thrown if no names have been defined. * * @param fieldName the name of the field whose alignment will be returned * * @return the alignment of the given field */ public FieldAlignment getAlignment(String fieldName) { int index = indexOf(fieldName); if (index == -1) { throw new IllegalArgumentException("Field '" + fieldName + "' does not exist. Available field names are: " + this.fieldNames); } return getAlignment(index); } /** * Returns a copy of the sequence of alignment settings to apply over each field in the fixed-width record. * * @return the sequence of alignment settings to apply over each field in the fixed-width record. */ public FieldAlignment[] getFieldAlignments() { return fieldAlignment.toArray(new FieldAlignment[fieldAlignment.size()]); } /** * Returns a copy of the sequence of padding characters to apply over each field in the fixed-width record. * * The null character ({@code '\0'}) is used to inform no padding has been explicitly set for a field, and that the * default padding character defined in {@link FixedWidthFormat#getPadding()} should be used. * * @return the sequence of padding characters to apply over each field in the fixed-width record. */ public char[] getFieldPaddings() { return ArgumentUtils.toCharArray(fieldPadding); } char[] getFieldPaddings(FixedWidthFormat format) { char[] out = getFieldPaddings(); for (int i = 0; i < out.length; i++) { if (out[i] == '\0') { out[i] = format.getPadding(); } } return out; } /** * Applies a custom padding character to a given list of fields * * @param padding the padding to apply * @param positions the positions of the fields that should use the given padding character */ public void setPadding(char padding, int... positions) { for (int position : positions) { setPadding(position, padding); } } /** * Applies a custom padding character to a given list of fields * * @param padding the padding to apply * @param names the names of the fields that should use the given padding character */ public void setPadding(char padding, String... names) { for (String name : names) { int position = indexOf(name); setPadding(position, padding); } } private void setPadding(int position, char padding) { if (padding == '\0') { throw new IllegalArgumentException("Cannot use the null character as padding"); } validateIndex(position); fieldPadding.set(position, padding); } /** * Configures a given list of fields to retain the padding character in any parsed values, * overriding the any default set for the whole input in {@link FixedWidthParserSettings#getKeepPadding()} * * @param position the positions of the fields that should keep the padding character * @param positions additional positions */ public void keepPaddingOn(int position, int... positions) { setKeepPaddingFlag(true, position, positions); } /** * Configures a given list of fields to retain the padding character in any parsed values, * overriding the any default set for the whole input in {@link FixedWidthParserSettings#getKeepPadding()} * * @param name the name of the first field that should keep the padding character * @param names the names of more fields that should keep the padding character */ public void keepPaddingOn(String name, String... names) { setKeepPaddingFlag(true, name, names); } /** * Configures a given list of fields to remove the padding character in any parsed values, * overriding the any default set for the whole input in {@link FixedWidthParserSettings#getKeepPadding()} * * @param position the positions of the fields that should keep the padding character * @param positions additional positions */ public void stripPaddingFrom(int position, int... positions) { setKeepPaddingFlag(false, position, positions); } /** * Configures a given list of fields to remove the padding character in any parsed values, * overriding the any default set for the whole input in {@link FixedWidthParserSettings#getKeepPadding()} * * @param name the name of the first field that have any padding characters removed * @param names the names of the fields that should have any padding characters removed */ public void stripPaddingFrom(String name, String... names) { setKeepPaddingFlag(false, name, names); } private void setKeepPaddingFlag(boolean keep, int position, int... positions) { setPaddingToKeep(position, keep); for (int p : positions) { setPaddingToKeep(p, keep); } } private void setKeepPaddingFlag(boolean keep, String name, String... names) { int position = indexOf(name); setPaddingToKeep(position, keep); for (String n : names) { position = indexOf(n); setPaddingToKeep(position, keep); } } private void setPaddingToKeep(int position, boolean keepPaddingFlag) { validateIndex(position); paddingsToKeep.set(position, keepPaddingFlag); } @Override public String toString() { StringBuilder out = new StringBuilder(); int i = 0; for (Integer length : fieldLengths) { out.append("\n\t\t").append(i + 1).append('\t'); if (i < fieldNames.size()) { out.append(fieldNames.get(i)); } out.append(", length: ").append(length); out.append(", align: ").append(fieldAlignment.get(i)); out.append(", padding: ").append(fieldPadding.get(i)); out.append(", keepPadding: ").append(paddingsToKeep.get(i)); i++; } return out.toString(); } static void setHeadersIfPossible(FixedWidthFields fieldLengths, CommonSettings settings) { if (fieldLengths != null && settings.getHeaders() == null) { NormalizedString[] headers = fieldLengths.getFieldNames(); if (headers != null) { int[] lengths = fieldLengths.getFieldLengths(); if (lengths.length == headers.length) { settings.setHeaders(NormalizedString.toArray(headers)); } } } } @Override protected FixedWidthFields clone() { try { FixedWidthFields out = (FixedWidthFields) super.clone(); out.fieldLengths = new ArrayList(fieldLengths); out.fieldNames = new ArrayList(fieldNames); out.fieldAlignment = new ArrayList(fieldAlignment); out.fieldPadding = new ArrayList(fieldPadding); out.paddingsToKeep = new ArrayList(paddingsToKeep); return out; } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthFormat.java000066400000000000000000000070751400120543400313270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.common.*; import java.util.*; /** * The Fixed-Width format configuration. In addition to the default configuration in {@link Format}, the fixed-width format defines: * *
    *
  • padding (defaults to ' '): the character used for filling unwritten spaces in a fixed-width record. *

    e.g. if a field has a length of 5 characters, but the value is 'ZZ', the field should contain [ZZ ] (i.e. ZZ followed by 3 unwritten spaces). *
    If the padding is set to '_', then the field will be written as [ZZ___]

  • *
* * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.common.Format */ public class FixedWidthFormat extends Format { private char padding = ' '; private char lookupWildcard = '?'; /** * Returns the padding character used to represent unwritten spaces. Defaults to ' ' * * @return the padding character */ public char getPadding() { return padding; } /** * Defines the padding character used to represent unwritten spaces. Defaults to ' ' * * @param padding the padding character */ public void setPadding(char padding) { this.padding = padding; } /** * Identifies whether or not a given character represents a padding character * * @param padding the character to be verified * * @return true if the given character is the padding character, false otherwise */ public boolean isPadding(char padding) { return this.padding == padding; } @Override protected TreeMap getConfiguration() { TreeMap out = new TreeMap(); out.put("Padding", padding); return out; } @Override public final FixedWidthFormat clone() { return (FixedWidthFormat) super.clone(); } /** * Returns the lookup wildcard character to accept any character in look-ahead or look-behind patterns defined * using {@link FixedWidthParserSettings#addFormatForLookahead(String, FixedWidthFields)} or * {@link FixedWidthParserSettings#addFormatForLookbehind(String, FixedWidthFields)}. * * Defaults to {@code '?'} * * @return the wildcard character to be used in lookahead/behind patterns. */ public char getLookupWildcard() { return lookupWildcard; } /** * Defines the lookup wildcard character to accept any character in look-ahead or look-behind patterns defined * using {@link FixedWidthParserSettings#addFormatForLookahead(String, FixedWidthFields)} or * {@link FixedWidthParserSettings#addFormatForLookbehind(String, FixedWidthFields)}. * * Defaults to {@code '?'} * * @param lookupWildcard the wildcard character to be used in lookahead/behind patterns. */ public void setLookupWildcard(char lookupWildcard) { this.lookupWildcard = lookupWildcard; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthParser.java000066400000000000000000000251611400120543400313270ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.*; import com.univocity.parsers.common.record.*; /** * A fast and flexible fixed-with parser implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see FixedWidthFormat * @see FixedWidthFields * @see FixedWidthParserSettings * @see FixedWidthWriter * @see AbstractParser */ public class FixedWidthParser extends AbstractParser { private int[] lengths; private int[] rootLengths; private boolean[] ignore; private boolean[] rootIgnore; private FieldAlignment[] alignments; private FieldAlignment[] rootAlignments; private char[] paddings; private char[] rootPaddings; private Boolean[] keepPaddingFlags; private Boolean[] rootKeepPaddingFlags; private final Lookup[] lookaheadFormats; private final Lookup[] lookbehindFormats; private Lookup lookupFormat; private Lookup lookbehindFormat; private int maxLookupLength; private final boolean skipToNewLine; private final boolean recordEndsOnNewLine; private final boolean skipEmptyLines; private final boolean keepPadding; private boolean useDefaultPadding; private final char defaultPadding; private char padding; private FieldAlignment alignment; private final char newLine; private int length; private boolean initializeLookaheadInput = false; private LookaheadCharInputReader lookaheadInput; private final char wildcard; /** * The FixedWidthParser supports all settings provided by {@link FixedWidthParserSettings}, and requires this configuration to be properly initialized. * * @param settings the parser configuration */ public FixedWidthParser(FixedWidthParserSettings settings) { super(settings); skipToNewLine = settings.getSkipTrailingCharsUntilNewline(); recordEndsOnNewLine = settings.getRecordEndsOnNewline(); skipEmptyLines = settings.getSkipEmptyLines(); lengths = settings.getAllLengths(); alignments = settings.getFieldAlignments(); paddings = settings.getFieldPaddings(); ignore = settings.getFieldsToIgnore(); keepPaddingFlags = settings.getKeepPaddingFlags(); keepPadding = settings.getKeepPadding(); lookaheadFormats = settings.getLookaheadFormats(); lookbehindFormats = settings.getLookbehindFormats(); wildcard = settings.getFormat().getLookupWildcard(); if (lookaheadFormats != null || lookbehindFormats != null) { initializeLookaheadInput = true; rootLengths = lengths; rootAlignments = alignments; rootPaddings = paddings; rootIgnore = ignore; rootKeepPaddingFlags = keepPaddingFlags; maxLookupLength = Lookup.calculateMaxLookupLength(lookaheadFormats, lookbehindFormats); } FixedWidthFormat format = settings.getFormat(); padding = format.getPadding(); defaultPadding = padding; newLine = format.getNormalizedNewline(); useDefaultPadding = settings.getUseDefaultPaddingForHeaders() && settings.isHeaderExtractionEnabled(); } @Override protected ParsingContext createParsingContext() { final ParsingContext context = super.createParsingContext(); if (lookaheadFormats != null || lookbehindFormats != null) { return new ParsingContextWrapper(context) { @Override public String[] headers() { return lookupFormat != null ? NormalizedString.toArray(lookupFormat.fieldNames) : super.headers(); } public Record toRecord(String[] row) { if (lookupFormat != null) { if (lookupFormat.context == null) { lookupFormat.initializeLookupContext(context, lookupFormat.fieldNames); } return lookupFormat.context.toRecord(row); } else { return super.toRecord(row); } } }; } else { return context; } } @Override protected void parseRecord() { if (ch == newLine && skipEmptyLines) { return; } boolean matched = false; if (lookaheadFormats != null || lookbehindFormats != null) { if (initializeLookaheadInput) { initializeLookaheadInput = false; this.lookaheadInput = new LookaheadCharInputReader(input, newLine, whitespaceRangeStart); this.input = lookaheadInput; } lookaheadInput.lookahead(maxLookupLength); if (lookaheadFormats != null) { for (int i = 0; i < lookaheadFormats.length; i++) { if (lookaheadInput.matches(ch, lookaheadFormats[i].value, wildcard)) { lengths = lookaheadFormats[i].lengths; alignments = lookaheadFormats[i].alignments; paddings = lookaheadFormats[i].paddings; ignore = lookaheadFormats[i].ignore; keepPaddingFlags = lookaheadFormats[i].keepPaddingFlags; lookupFormat = lookaheadFormats[i]; matched = true; break; } } if (lookbehindFormats != null && matched) { lookbehindFormat = null; for (int i = 0; i < lookbehindFormats.length; i++) { if (lookaheadInput.matches(ch, lookbehindFormats[i].value, wildcard)) { lookbehindFormat = lookbehindFormats[i]; break; } } } } else { for (int i = 0; i < lookbehindFormats.length; i++) { if (lookaheadInput.matches(ch, lookbehindFormats[i].value, wildcard)) { lookbehindFormat = lookbehindFormats[i]; matched = true; lengths = rootLengths; ignore = rootIgnore; keepPaddingFlags = rootKeepPaddingFlags; break; } } } if (!matched) { if (lookbehindFormat == null) { if (rootLengths == null) { throw new TextParsingException(context, "Cannot process input with the given configuration. No default field lengths defined and no lookahead/lookbehind value match '" + lookaheadInput.getLookahead(ch) + '\''); } lengths = rootLengths; alignments = rootAlignments; paddings = rootPaddings; ignore = rootIgnore; keepPaddingFlags = rootKeepPaddingFlags; lookupFormat = null; } else { lengths = lookbehindFormat.lengths; alignments = lookbehindFormat.alignments; paddings = lookbehindFormat.paddings; ignore = lookbehindFormat.ignore; keepPaddingFlags = lookbehindFormat.keepPaddingFlags; lookupFormat = lookbehindFormat; } } } int i; for (i = 0; i < lengths.length; i++) { final boolean ignorePadding = keepPaddingFlags[i] == null ? !keepPadding : !keepPaddingFlags[i]; length = lengths[i]; if (paddings != null) { padding = useDefaultPadding ? defaultPadding : paddings[i]; } if (alignments != null) { alignment = alignments[i]; } final boolean lastFieldOfRecord = (i + 1 >= lengths.length); if (ignorePadding) { skipPadding(lastFieldOfRecord); } if (ignoreLeadingWhitespace) { skipWhitespace(lastFieldOfRecord, ignorePadding); } if (recordEndsOnNewLine) { readValueUntilNewLine(ignorePadding); if (ch == newLine) { output.valueParsed(); useDefaultPadding = false; return; } } else if (length > 0) { readValue(ignorePadding); if (!lastFieldOfRecord) { ch = input.nextChar(); } } if (ignore[i]) { output.appender.reset(); } else { output.valueParsed(); } } if (skipToNewLine) { skipToNewLine(); } useDefaultPadding = false; } private void skipToNewLine() { try { while (ch != newLine) { ch = input.nextChar(); } } catch (EOFException e) { //ignore and let the EOF blow up again after the record is generated. } } private void skipPadding(boolean lastFieldOfRecord) { while (ch == padding && length-- > 0) { if (!lastFieldOfRecord || length > 0) { ch = input.nextChar(); } } } private void skipWhitespace(boolean lastFieldOfRecord, boolean ignorePadding) { while ((ch <= ' ' && whitespaceRangeStart < ch || ch == padding) && !(!ignorePadding && ch == padding) && length-- > 0) { if (!lastFieldOfRecord || length > 0) { ch = input.nextChar(); } } } private void readValueUntilNewLine(boolean ignorePadding) { if (ignoreTrailingWhitespace) { if (alignment == FieldAlignment.RIGHT) { while (length-- > 0 && ch != newLine) { output.appender.appendIgnoringWhitespace(ch); ch = input.nextChar(); } } else if (ignorePadding) { while (length-- > 0 && ch != newLine) { output.appender.appendIgnoringWhitespaceAndPadding(ch, padding); ch = input.nextChar(); } } else { while (length-- > 0 && ch != newLine) { output.appender.append(ch); ch = input.nextChar(); } } } else { if (alignment == FieldAlignment.RIGHT) { while (length-- > 0 && ch != newLine) { output.appender.append(ch); ch = input.nextChar(); } } else if (ignorePadding) { while (length-- > 0 && ch != newLine) { output.appender.appendIgnoringPadding(ch, padding); ch = input.nextChar(); } } else { while (length-- > 0 && ch != newLine) { output.appender.append(ch); ch = input.nextChar(); } } } } private void readValue(boolean ignorePadding) { length--; if (ignoreTrailingWhitespace) { if (alignment == FieldAlignment.RIGHT) { output.appender.appendIgnoringWhitespace(ch); while (length-- > 0) { output.appender.appendIgnoringWhitespace(ch = input.nextChar()); } } else if (ignorePadding) { output.appender.appendIgnoringWhitespaceAndPadding(ch, padding); while (length-- > 0) { output.appender.appendIgnoringWhitespaceAndPadding(ch = input.nextChar(), padding); } } else { output.appender.append(ch); while (length-- > 0) { output.appender.append(ch = input.nextChar()); } } } else { if (alignment == FieldAlignment.RIGHT) { output.appender.append(ch); while (length-- > 0) { output.appender.append(ch = input.nextChar()); } } else if (ignorePadding) { output.appender.appendIgnoringPadding(ch, padding); while (length-- > 0) { output.appender.appendIgnoringPadding(ch = input.nextChar(), padding); } } else { output.appender.append(ch); while (length-- > 0) { output.appender.append(ch = input.nextChar()); } } } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthParserSettings.java000066400000000000000000000436611400120543400330550ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.input.*; import java.util.*; /** * This is the configuration class used by the Fixed-Width parser ({@link FixedWidthParser}) * *

In addition to the configuration options provided by {@link CommonParserSettings}, the FixedWidthParserSettings include: * *

    *
  • skipTrailingCharsUntilNewline (defaults to {@code false}): Indicates whether or not any trailing characters beyond the record's length should be skipped until the newline is reached *

    For example, if the record length is 5, but the row contains "12345678\n", then portion containing "678" will be discarded and not considered part of the next record

  • *
  • recordEndsOnNewline (defaults to {@code false}): Indicates whether or not a record is considered parsed when a newline is reached. *

    For example, if recordEndsOnNewline is set to true, then given a record of length 4, and the input "12\n3456", the parser will identify [12] and [3456] *

    If recordEndsOnNewline is set to false, then given a record of length 4, and the input "12\n3456", the parser will identify a multi-line record [12\n3] and [456 ]

  • *
* *

The FixedWidthParserSettings need a definition of the field lengths of each record in the input. This must provided using an instance of {@link FixedWidthFields}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.fixed.FixedWidthParser * @see com.univocity.parsers.fixed.FixedWidthFormat * @see FixedWidthFields * @see com.univocity.parsers.common.CommonParserSettings */ public class FixedWidthParserSettings extends CommonParserSettings { protected boolean skipTrailingCharsUntilNewline = false; protected boolean recordEndsOnNewline = false; private boolean useDefaultPaddingForHeaders = true; private boolean keepPadding = false; private FixedWidthFields fieldLengths; private Map lookaheadFormats = new HashMap(); private Map lookbehindFormats = new HashMap(); /** * You can only create an instance of this class by providing a definition of the field lengths of each record in the input. *

This must provided using an instance of {@link FixedWidthFields}. * * @param fieldLengths the instance of {@link FixedWidthFields} which provides the lengths of each field in the fixed-width records to be parsed * * @see FixedWidthFields */ public FixedWidthParserSettings(FixedWidthFields fieldLengths) { if (fieldLengths == null) { throw new IllegalArgumentException("Field lengths cannot be null"); } this.fieldLengths = fieldLengths; NormalizedString[] names = fieldLengths.getFieldNames(); if (names != null) { setHeaders(NormalizedString.toArray(names)); } } /** * Creates a basic configuration object for the Fixed-Width parser with no field length configuration. * This constructor is intended to be used when the record length varies depending of the input row. * Refer to {@link #addFormatForLookahead(String, FixedWidthFields)}, {@link #addFormatForLookbehind(String, FixedWidthFields)} */ public FixedWidthParserSettings() { fieldLengths = null; } /** * Returns the sequence of lengths to be read by the parser to form a record. * * @return the sequence of lengths to be read by the parser to form a record. */ int[] getFieldLengths() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldLengths(); } int[] getAllLengths() { if (fieldLengths == null) { return null; } return fieldLengths.getAllLengths(); } /** * Returns the sequence of paddings used by each field of each record. * * @return the sequence of paddings used by each field of each record. */ char[] getFieldPaddings() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldPaddings(this.getFormat()); } /** * Returns the sequence of fields to ignore. * * @return the sequence of fields to ignore. */ boolean[] getFieldsToIgnore() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldsToIgnore(); } /** * Returns the sequence of alignments to consider for each field of each record. * * @return the sequence of alignments to consider for each field of each record. */ FieldAlignment[] getFieldAlignments() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldAlignments(); } /** * Indicates whether or not any trailing characters beyond the record's length should be skipped until the newline is reached (defaults to {@code false}) *

For example, if the record length is 5, but the row contains "12345678\n", then the portion containing "678\n" will be discarded and not considered part of the next record * * @return returns true if any trailing characters beyond the record's length should be skipped until the newline is reached, false otherwise */ public boolean getSkipTrailingCharsUntilNewline() { return skipTrailingCharsUntilNewline; } /** * Defines whether or not any trailing characters beyond the record's length should be skipped until the newline is reached (defaults to {@code false}) *

For example, if the record length is 5, but the row contains "12345678\n", then the portion containing "678\n" will be discarded and not considered part of the next record * * @param skipTrailingCharsUntilNewline a flag indicating if any trailing characters beyond the record's length should be skipped until the newline is reached */ public void setSkipTrailingCharsUntilNewline(boolean skipTrailingCharsUntilNewline) { this.skipTrailingCharsUntilNewline = skipTrailingCharsUntilNewline; } /** * Indicates whether or not a record is considered parsed when a newline is reached. Examples: *

    *
  • Consider two records of length 4, and the input 12\n3456
  • *
  • When {@link FixedWidthParserSettings#recordEndsOnNewline} is set to true: the first value will be read as 12 and the second 3456
  • *
  • When {@link FixedWidthParserSettings#recordEndsOnNewline} is set to false: the first value will be read as 12\n3 and the second 456
  • *
*

defaults to {@code false} * * @return true if a record should be considered parsed when a newline is reached; false otherwise */ public boolean getRecordEndsOnNewline() { return recordEndsOnNewline; } /** * Defines whether or not a record is considered parsed when a newline is reached. Examples: *

    *
  • Consider two records of length 4, and the input 12\n3456
  • *
  • When {@link FixedWidthParserSettings#recordEndsOnNewline} is set to true: the first value will be read as 12 and the second 3456
  • *
  • When {@link FixedWidthParserSettings#recordEndsOnNewline} is set to false: the first value will be read as 12\n3 and the second 456
  • *
* * @param recordEndsOnNewline a flag indicating whether or not a record is considered parsed when a newline is reached */ public void setRecordEndsOnNewline(boolean recordEndsOnNewline) { this.recordEndsOnNewline = recordEndsOnNewline; } /** * Returns the default FixedWidthFormat configured to handle Fixed-Width inputs * * @return and instance of FixedWidthFormat configured to handle Fixed-Width inputs */ @Override protected FixedWidthFormat createDefaultFormat() { return new FixedWidthFormat(); } /** * Returns an instance of CharAppender with the configured limit of maximum characters per column and, default value used to represent a null value (when the String parsed from the input is empty), and the padding character to handle unwritten positions * *

This overrides the parent implementation to create a CharAppender capable of handling padding characters that represent unwritten positions. * * @return an instance of CharAppender with the configured limit of maximum characters per column and, default value used to represent a null value (when the String parsed from the input is empty), and the padding character to handle unwritten positions */ @Override protected CharAppender newCharAppender() { return new DefaultCharAppender(getMaxCharsPerColumn(), getNullValue(), getWhitespaceRangeStart()); } /** * The maximum number of characters allowed for any given value being written/read. Used to avoid OutOfMemoryErrors (defaults to a minimum of 4096 characters). * *

This overrides the parent implementation and calculates the absolute minimum number of characters required to store the values of a record *

If the sum of all field lengths is greater than the configured maximum number of characters per column, the calculated amount will be returned. * * @return The maximum number of characters allowed for any given value being written/read */ @Override public int getMaxCharsPerColumn() { int max = super.getMaxCharsPerColumn(); int minimum = 0; for (int length : calculateMaxFieldLengths()) { //adding 2 to give room for line breaks in every record (e.g. "\r\n"). minimum += length + 2; } return max > minimum ? max : minimum; } /** * Returns the hard limit of how many columns a record can have (defaults to a maximum of 512). * You need this to avoid OutOfMemory errors in case of inputs that might be inconsistent with the format you are dealing with. * *

This overrides the parent implementation and calculates the absolute minimum number of columns required to store the values of a record *

If the sum of all fields is greater than the configured maximum number columns, the calculated amount will be returned. * * @return The maximum number of columns a record can have. */ @Override public int getMaxColumns() { int max = super.getMaxColumns(); int minimum = calculateMaxFieldLengths().length; return max > minimum ? max : minimum; } private int[] calculateMaxFieldLengths() { return Lookup.calculateMaxFieldLengths(fieldLengths, lookaheadFormats, lookbehindFormats); } Lookup[] getLookaheadFormats() { return Lookup.getLookupFormats(lookaheadFormats, getFormat()); } Lookup[] getLookbehindFormats() { return Lookup.getLookupFormats(lookbehindFormats, getFormat()); } /** * Defines the format of records identified by a lookahead symbol. * * @param lookahead the lookahead value that when found in the input, * will notify the parser to switch to a new record format, with different field lengths * @param lengths the field lengths of the record format identified by the given lookahead symbol. */ public void addFormatForLookahead(String lookahead, FixedWidthFields lengths) { Lookup.registerLookahead(lookahead, lengths, lookaheadFormats); } /** * Defines the format of records identified by a lookbehind symbol. * * @param lookbehind the lookbehind value that when found in the previous input row, * will notify the parser to switch to a new record format, with different field lengths * @param lengths the field lengths of the record format identified by the given lookbehind symbol. */ public void addFormatForLookbehind(String lookbehind, FixedWidthFields lengths) { Lookup.registerLookbehind(lookbehind, lengths, lookbehindFormats); } /** * Indicates whether headers should be parsed using the default padding specified in {@link FixedWidthFormat#getPadding()} * instead of any custom padding associated with a given field (in {@link FixedWidthFields#setPadding(char, int...)}) * Defaults to {@code true} * * @return {@code true} if the default padding is to be used when reading headers, otherwise {@code false} */ public boolean getUseDefaultPaddingForHeaders() { return useDefaultPaddingForHeaders; } /** * Defines whether headers should be parsed using the default padding specified in {@link FixedWidthFormat#getPadding()} * instead of any custom padding associated with a given field (in {@link FixedWidthFields#setPadding(char, int...)}) * * @param useDefaultPaddingForHeaders flag indicating whether the default padding is to be used when parsing headers */ public void setUseDefaultPaddingForHeaders(boolean useDefaultPaddingForHeaders) { this.useDefaultPaddingForHeaders = useDefaultPaddingForHeaders; } @Override protected void configureFromAnnotations(Class beanClass) { if (fieldLengths == null) { try { fieldLengths = FixedWidthFields.forParsing(beanClass); Headers headerAnnotation = AnnotationHelper.findHeadersAnnotation(beanClass); if (headerExtractionEnabled == null && headerAnnotation != null) { setHeaderExtractionEnabled(headerAnnotation.extract()); } } catch (IllegalArgumentException e) { throw e; } catch (Exception ex) { //ignore. } } if (headerExtractionEnabled == null) { setHeaderExtractionEnabled(false); } super.configureFromAnnotations(beanClass); if (!isHeaderExtractionEnabled()) { FixedWidthFields.setHeadersIfPossible(fieldLengths, this); } } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); out.put("Skip trailing characters until new line", skipTrailingCharsUntilNewline); out.put("Record ends on new line", recordEndsOnNewline); out.put("Field lengths", fieldLengths == null ? "" : fieldLengths.toString()); out.put("Lookahead formats", lookaheadFormats); out.put("Lookbehind formats", lookbehindFormats); } /** * Clones this configuration object to reuse all user-provided settings, including the fixed-width field configuration. * * @return a copy of all configurations applied to the current instance. */ @Override public final FixedWidthParserSettings clone() { return (FixedWidthParserSettings) super.clone(); } /** * Clones this configuration object to reuse most user-provided settings. This includes the fixed-width field configuration, * but doesn't include other input-specific settings. This method is meant to be used internally only. * * @return a copy of all configurations applied to the current instance. * * @deprecated doesn't really make sense for fixed-width. Use alternative method {@link #clone(FixedWidthFields)}. */ @Deprecated protected final FixedWidthParserSettings clone(boolean clearInputSpecificSettings) { return clone(clearInputSpecificSettings, fieldLengths == null ? null : fieldLengths.clone()); } /** * Clones this configuration object to reuse most user-provided settings. Properties that are specific to a given * input (such as header names and selection of fields) will be reset to their defaults. * * To obtain a full copy, use {@link #clone()}. * * @param fields the fixed-width field configuration to be used by the cloned settings object. * * @return a copy of the general configurations applied to the current instance. */ public final FixedWidthParserSettings clone(FixedWidthFields fields) { return clone(true, fields); } private FixedWidthParserSettings clone(boolean clearInputSpecificSettings, FixedWidthFields fields) { FixedWidthParserSettings out = (FixedWidthParserSettings) super.clone(clearInputSpecificSettings); out.fieldLengths = fields; if (clearInputSpecificSettings) { out.lookaheadFormats = new HashMap(); out.lookbehindFormats = new HashMap(); } else { out.lookaheadFormats = new HashMap(this.lookaheadFormats); out.lookbehindFormats = new HashMap(this.lookbehindFormats); } return out; } /** * Indicate the padding character should be kept in the parsed value * * (defaults to {@code false}) * * This setting can be overridden for individual fields through * {@link FixedWidthFields#stripPaddingFrom(String, String...)} and * {@link FixedWidthFields#keepPaddingOn(String, String...)} * * @return flag indicating the padding character should be kept in the parsed value */ public final boolean getKeepPadding() { return keepPadding; } /** * Configures the fixed-width parser to retain the padding character in any parsed values * * (defaults to {@code false}) * * This setting can be overridden for individual fields through * {@link FixedWidthFields#stripPaddingFrom(String, String...)} and * {@link FixedWidthFields#keepPaddingOn(String, String...)} * * @param keepPadding flag indicating the padding character should be kept in the parsed value */ public final void setKeepPadding(boolean keepPadding) { this.keepPadding = keepPadding; } /** * Returns the sequence of fields whose padding character must/must not be retained in the parsed value * @return the sequence that have an explicit 'keepPadding' flag. */ Boolean[] getKeepPaddingFlags() { if (fieldLengths == null) { return null; } Boolean[] keepFlags = fieldLengths.getKeepPaddingFlags(); Boolean[] out = new Boolean[keepFlags.length]; Arrays.fill(out, getKeepPadding()); for (int i = 0; i < keepFlags.length; i++) { Boolean flag = keepFlags[i]; if (flag != null) { out[i] = flag; } } return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthRoutines.java000066400000000000000000000055711400120543400317060ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.common.routine.*; import java.io.*; /** * A collection of common routines involving the processing of Fixed-Width data. */ public class FixedWidthRoutines extends AbstractRoutines { /** * Creates a new instance of the Fixed-width routine class without any predefined parsing/writing configuration. */ public FixedWidthRoutines() { this(null, null); } /** * Creates a new instance of the Fixed-width routine class. * * @param parserSettings configuration to use for Fixed-width parsing */ public FixedWidthRoutines(FixedWidthParserSettings parserSettings) { this(parserSettings, null); } /** * Creates a new instance of the Fixed-width routine class. * * @param writerSettings configuration to use for Fixed-width writing */ public FixedWidthRoutines(FixedWidthWriterSettings writerSettings) { this(null, writerSettings); } /** * Creates a new instance of the Fixed-width routine class. * * @param parserSettings configuration to use for Fixed-width parsing * @param writerSettings configuration to use for Fixed-width writing */ public FixedWidthRoutines(FixedWidthParserSettings parserSettings, FixedWidthWriterSettings writerSettings) { super("Fixed-width parsing/writing routine", parserSettings, writerSettings); } protected void adjustColumnLengths(String[] headers, int[] lengths){ if(getWriterSettings().getFieldLengths() == null) { getWriterSettings().setFieldLengths(new FixedWidthFields(headers, lengths)); } } @Override protected FixedWidthParser createParser(FixedWidthParserSettings parserSettings) { return new FixedWidthParser(parserSettings); } @Override protected FixedWidthWriter createWriter(Writer output, FixedWidthWriterSettings writerSettings) { return new FixedWidthWriter(output, writerSettings); } @Override protected FixedWidthParserSettings createDefaultParserSettings() { return new FixedWidthParserSettings(); } @Override protected FixedWidthWriterSettings createDefaultWriterSettings() { return new FixedWidthWriterSettings(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthWriter.java000066400000000000000000000270611400120543400313500ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.common.*; import java.io.*; import java.nio.charset.*; /** * A fast and flexible fixed-with writer implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see FixedWidthFormat * @see FixedWidthFields * @see FixedWidthWriterSettings * @see FixedWidthParser * @see AbstractWriter */ public class FixedWidthWriter extends AbstractWriter { private int[] fieldLengths; private FieldAlignment[] fieldAlignments; private char[] fieldPaddings; private char padding; private char defaultPadding; private int length; private FieldAlignment alignment; private Lookup[] lookaheadFormats; private Lookup[] lookbehindFormats; private char[] lookupChars; private Lookup lookbehindFormat; private int[] rootLengths; private FieldAlignment[] rootAlignments; private boolean[] ignore; private boolean[] rootIgnore; private int ignoreCount; private char[] rootPaddings; private boolean defaultHeaderPadding; private FieldAlignment defaultHeaderAlignment; /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. *

Important: by not providing an instance of {@link java.io.Writer} to this constructor, only the operations that write to Strings are available.

* * @param settings the fixed-width writer configuration */ public FixedWidthWriter(FixedWidthWriterSettings settings) { this((Writer) null, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param writer the output resource that will receive fixed-width records produced by this class. * @param settings the fixed-width writer configuration */ public FixedWidthWriter(Writer writer, FixedWidthWriterSettings settings) { super(writer, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive fixed-width records produced by this class. * @param settings the fixed-width writer configuration */ public FixedWidthWriter(File file, FixedWidthWriterSettings settings) { super(file, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive fixed-width records produced by this class. * @param encoding the encoding of the file * @param settings the fixed-width writer configuration */ public FixedWidthWriter(File file, String encoding, FixedWidthWriterSettings settings) { super(file, encoding, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive fixed-width records produced by this class. * @param encoding the encoding of the file * @param settings the fixed-width writer configuration */ public FixedWidthWriter(File file, Charset encoding, FixedWidthWriterSettings settings) { super(file, encoding, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the fixed-width records produced by this class. * @param settings the fixed-width writer configuration */ public FixedWidthWriter(OutputStream output, FixedWidthWriterSettings settings) { super(output, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the fixed-width records produced by this class. * @param encoding the encoding of the stream * @param settings the fixed-width writer configuration */ public FixedWidthWriter(OutputStream output, String encoding, FixedWidthWriterSettings settings) { super(output, encoding, settings); } /** * The FixedWidthWriter supports all settings provided by {@link FixedWidthWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the fixed-width records produced by this class. * @param encoding the encoding of the stream * @param settings the fixed-width writer configuration */ public FixedWidthWriter(OutputStream output, Charset encoding, FixedWidthWriterSettings settings) { super(output, encoding, settings); } /** * Initializes the Fixed-Width writer with CSV-specific configuration * * @param settings the Fixed-Width writer configuration */ protected final void initialize(FixedWidthWriterSettings settings) { FixedWidthFormat format = settings.getFormat(); this.padding = format.getPadding(); this.defaultPadding = padding; this.fieldLengths = settings.getAllLengths(); this.fieldAlignments = settings.getFieldAlignments(); this.fieldPaddings = settings.getFieldPaddings(); this.ignore = settings.getFieldsToIgnore(); if (ignore != null) { for (int i = 0; i < ignore.length; i++) { if (ignore[i]) { ignoreCount++; } } } this.lookaheadFormats = settings.getLookaheadFormats(); this.lookbehindFormats = settings.getLookbehindFormats(); this.defaultHeaderPadding = settings.getUseDefaultPaddingForHeaders(); this.defaultHeaderAlignment = settings.getDefaultAlignmentForHeaders(); super.enableNewlineAfterRecord(settings.getWriteLineSeparatorAfterRecord()); if (lookaheadFormats != null || lookbehindFormats != null) { lookupChars = new char[Lookup.calculateMaxLookupLength(lookaheadFormats, lookbehindFormats)]; rootLengths = fieldLengths; rootAlignments = fieldAlignments; rootPaddings = fieldPaddings; rootIgnore = ignore; } else { lookupChars = null; rootLengths = null; rootAlignments = null; rootPaddings = null; rootIgnore = null; } } @Override protected void processRow(Object[] row) { if (row.length > 0 && lookaheadFormats != null || lookbehindFormats != null) { int dstBegin = 0; for (int i = 0; i < row.length && dstBegin < lookupChars.length; i++) { String value = String.valueOf(row[i]); int len = value.length(); if (dstBegin + len > lookupChars.length) { len = lookupChars.length - dstBegin; } value.getChars(0, len, lookupChars, dstBegin); dstBegin += len; } for (int i = lookupChars.length - 1; i > dstBegin; i--) { lookupChars[i] = '\0'; } boolean matched = false; if (lookaheadFormats != null) { for (int i = 0; i < lookaheadFormats.length; i++) { if (lookaheadFormats[i].matches(lookupChars)) { fieldLengths = lookaheadFormats[i].lengths; fieldAlignments = lookaheadFormats[i].alignments; fieldPaddings = lookaheadFormats[i].paddings; ignore = lookaheadFormats[i].ignore; matched = true; break; } } if (lookbehindFormats != null && matched) { lookbehindFormat = null; for (int i = 0; i < lookbehindFormats.length; i++) { if (lookbehindFormats[i].matches(lookupChars)) { lookbehindFormat = lookbehindFormats[i]; break; } } } } else { for (int i = 0; i < lookbehindFormats.length; i++) { if (lookbehindFormats[i].matches(lookupChars)) { lookbehindFormat = lookbehindFormats[i]; matched = true; fieldLengths = rootLengths; fieldAlignments = rootAlignments; fieldPaddings = rootPaddings; ignore = rootIgnore; break; } } } if (!matched) { if (lookbehindFormat == null) { if (rootLengths == null) { throw new TextWritingException("Cannot write with the given configuration. No default field lengths defined and no lookahead/lookbehind value match '" + new String(lookupChars) + '\'', getRecordCount(), row); } fieldLengths = rootLengths; fieldAlignments = rootAlignments; fieldPaddings = rootPaddings; ignore = rootIgnore; } else { fieldLengths = lookbehindFormat.lengths; fieldAlignments = lookbehindFormat.alignments; fieldPaddings = lookbehindFormat.paddings; ignore = lookbehindFormat.ignore; } } } if (expandRows) { row = expand(row, fieldLengths.length - ignoreCount, null); } final int lastIndex = fieldLengths.length < row.length ? fieldLengths.length : row.length; int off = 0; for (int i = 0; i < lastIndex + off; i++) { length = fieldLengths[i]; if (ignore[i]) { off++; this.appender.fill(' ', length); } else { alignment = fieldAlignments[i]; padding = fieldPaddings[i]; if (writingHeaders) { if (defaultHeaderPadding) { padding = defaultPadding; } if (defaultHeaderAlignment != null) { alignment = defaultHeaderAlignment; } } String nextElement = getStringValue(row[i - off]); boolean allowTrim = allowTrim(i); processElement(nextElement, allowTrim); appendValueToRow(); } } } private void append(String element, boolean allowTrim) { int start = 0; if (allowTrim && this.ignoreLeading) { start = skipLeadingWhitespace(whitespaceRangeStart, element); } int padCount = alignment.calculatePadding(length, element.length() - start); length -= padCount; appender.fill(padding, padCount); if (allowTrim && this.ignoreTrailing) { int i = start; while (i < element.length() && length > 0) { for (; i < element.length() && length-- > 0; i++) { char nextChar = element.charAt(i); appender.appendIgnoringWhitespace(nextChar); } if (length == -1 && appender.whitespaceCount() > 0) { //if we got here then the value to write got truncated exactly after one or more whitespaces. //In this case, if the whitespaces are not at the end of the truncated value they will be put back to the output. for (int j = i; j < element.length(); j++) { if (element.charAt(j) > ' ') { //resets the whitespace count so the original whitespaces are printed to the output. appender.resetWhitespaceCount(); break; } } if (appender.whitespaceCount() > 0) { length = 0; } } length += appender.whitespaceCount(); appendValueToRow(); } } else { for (int i = start; i < element.length() && length-- > 0; i++) { char nextChar = element.charAt(i); appender.append(nextChar); } } } private void processElement(String element, boolean allowTrim) { if (element != null) { append(element, allowTrim); } appender.fill(padding, length); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/FixedWidthWriterSettings.java000066400000000000000000000325341400120543400330720ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.annotations.*; import com.univocity.parsers.annotations.helpers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.Format; import java.util.*; /** * This is the configuration class used by the Fixed-Width writer ({@link FixedWidthWriter}) * *

The FixedWidthWriterSettings provides all configuration options in {@link CommonWriterSettings} and currently does not require any additional setting. * *

The FixedWidthParserSettings requires a definition of the field lengths of each record in the input. This must provided using an instance of {@link FixedWidthFields}. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.fixed.FixedWidthWriter * @see com.univocity.parsers.fixed.FixedWidthFormat * @see FixedWidthFields * @see com.univocity.parsers.common.CommonWriterSettings */ public class FixedWidthWriterSettings extends CommonWriterSettings { private FixedWidthFields fieldLengths; private Map lookaheadFormats = new HashMap(); private Map lookbehindFormats = new HashMap(); private boolean useDefaultPaddingForHeaders = true; private FieldAlignment defaultAlignmentForHeaders = null; private boolean writeLineSeparatorAfterRecord = true; /** * You can only create an instance of this class by providing a definition of the field lengths of each record in the input. *

This must provided using an instance of {@link FixedWidthFields}. * * @param fieldLengths the instance of {@link FixedWidthFields} which provides the lengths of each field in the fixed-width records to be parsed * * @see FixedWidthFields */ public FixedWidthWriterSettings(FixedWidthFields fieldLengths) { setFieldLengths(fieldLengths); NormalizedString[] names = fieldLengths.getFieldNames(); if (names != null) { setHeaders(NormalizedString.toArray(names)); } } /** * Creates a basic configuration object for the Fixed-Width writer with no field length configuration. * This constructor is intended to be used when the record length varies depending of the input row. * Refer to {@link #addFormatForLookahead(String, FixedWidthFields)}, {@link #addFormatForLookbehind(String, FixedWidthFields)} */ public FixedWidthWriterSettings() { this.fieldLengths = null; } final void setFieldLengths(FixedWidthFields fieldLengths) { if (fieldLengths == null) { throw new IllegalArgumentException("Field lengths cannot be null"); } this.fieldLengths = fieldLengths; } /** * Returns the sequence of field lengths to be written to form a record. * * @return the sequence of field lengths to be written to form a record. */ int[] getFieldLengths() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldLengths(); } /** * Returns the sequence of field lengths to be written to form a record. * * @return the sequence of field lengths to be written to form a record. */ int[] getAllLengths() { if (fieldLengths == null) { return null; } return fieldLengths.getAllLengths(); } /** * Returns the sequence of field alignments to apply to each field in the record. * * @return the sequence of field alignments to apply to each field in the record. */ FieldAlignment[] getFieldAlignments() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldAlignments(); } /** * Returns the sequence of paddings used by each field of each record. * * @return the sequence of paddings used by each field of each record. */ char[] getFieldPaddings() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldPaddings(getFormat()); } /** * Returns the sequence of fields to ignore. * * @return the sequence of fields to ignore. */ boolean[] getFieldsToIgnore() { if (fieldLengths == null) { return null; } return fieldLengths.getFieldsToIgnore(); } /** * Returns the default FixedWidthFormat configured to handle Fixed-Width outputs * * @return and instance of FixedWidthFormat configured to handle Fixed-Width outputs */ @Override protected FixedWidthFormat createDefaultFormat() { return new FixedWidthFormat(); } @Override public int getMaxColumns() { int max = super.getMaxColumns(); int minimum = Lookup.calculateMaxFieldLengths(fieldLengths, lookaheadFormats, lookbehindFormats).length; return max > minimum ? max : minimum; } /** * Defines the format of records identified by a lookahead symbol. * * @param lookahead the lookahead value that when found in the output row, * will notify the writer to switch to a new record format, with different field lengths * @param lengths the field lengths of the record format identified by the given lookahead symbol. */ public void addFormatForLookahead(String lookahead, FixedWidthFields lengths) { Lookup.registerLookahead(lookahead, lengths, lookaheadFormats); } /** * Defines the format of records identified by a lookbehind symbol. * * @param lookbehind the lookbehind value that when present in the previous output row, * will notify the writer to switch to a new record format, with different field lengths * @param lengths the field lengths of the record format identified by the given lookbehind symbol. */ public void addFormatForLookbehind(String lookbehind, FixedWidthFields lengths) { Lookup.registerLookbehind(lookbehind, lengths, lookbehindFormats); } Lookup[] getLookaheadFormats() { return Lookup.getLookupFormats(lookaheadFormats, getFormat()); } Lookup[] getLookbehindFormats() { return Lookup.getLookupFormats(lookbehindFormats, getFormat()); } /** * Indicates whether headers should be written using the default padding specified in {@link FixedWidthFormat#getPadding()} * instead of any custom padding associated with a given field (in {@link FixedWidthFields#setPadding(char, int...)}) * Defaults to {@code true} * * @return {@code true} if the default padding is to be used when writing headers, otherwise {@code false} */ public boolean getUseDefaultPaddingForHeaders() { return useDefaultPaddingForHeaders; } /** * Defines whether headers should be written using the default padding specified in {@link FixedWidthFormat#getPadding()} * instead of any custom padding associated with a given field (in {@link FixedWidthFields#setPadding(char, int...)}) * * @param useDefaultPaddingForHeaders flag indicating whether the default padding is to be used when writing headers */ public void setUseDefaultPaddingForHeaders(boolean useDefaultPaddingForHeaders) { this.useDefaultPaddingForHeaders = useDefaultPaddingForHeaders; } /** * Returns the default alignment to use when writing headers. If none is specified (i.e. {@code null}), the headers will be aligned * according to the corresponding field alignment in {@link FixedWidthFields#getAlignment(String)}. * * Defaults to {@code null}. * * @return the default alignment for headers, or {@code null} if the headers should be aligned according to the column alignment. */ public FieldAlignment getDefaultAlignmentForHeaders() { return defaultAlignmentForHeaders; } /** * Defines the default alignment to use when writing headers. If none is specified (i.e. {@code null}), the headers will be aligned * according to the corresponding field alignment in {@link FixedWidthFields#getAlignment(String)}. * * Defaults to {@code null}. * * @param defaultAlignmentForHeaders the alignment to use when writing headers. {@code null} indicates that headers * should be aligned according to the column alignment. */ public void setDefaultAlignmentForHeaders(FieldAlignment defaultAlignmentForHeaders) { this.defaultAlignmentForHeaders = defaultAlignmentForHeaders; } /** * Returns a flag indicating whether each record, when written, should be followed by a line separator (as specified in {@link Format#getLineSeparator()}. * * Consider the records {@code [a,b]} and {@code [c,d]}, with field lengths {@code [2, 2]}, and line separator = {@code \n}: *

    *
  • When {@link #getWriteLineSeparatorAfterRecord()} is enabled, the output will be written as: {@code a b \nc d \n}
  • *
  • When {@link #getWriteLineSeparatorAfterRecord()} is disabled, the output will be written as: {@code a b c d }
  • *
* * Defaults to {@code true}. * * @return whether the writer should add a line separator after a record is written to the output. */ public boolean getWriteLineSeparatorAfterRecord() { return writeLineSeparatorAfterRecord; } /** * Defines whether each record, when written, should be followed by a line separator (as specified in {@link Format#getLineSeparator()}. * * Consider the records {@code [a,b]} and {@code [c,d]}, with field lengths {@code [2, 2]}, and line separator = {@code \n}: *
    *
  • When {@link #getWriteLineSeparatorAfterRecord()} is enabled, the output will be written as: {@code a b \nc d \n}
  • *
  • When {@link #getWriteLineSeparatorAfterRecord()} is disabled, the output will be written as: {@code a b c d }
  • *
* * Defaults to {@code true}. * * @param writeLineSeparatorAfterRecord flag indicating whether the writer should add a line separator after a record is written to the output. */ public void setWriteLineSeparatorAfterRecord(boolean writeLineSeparatorAfterRecord) { this.writeLineSeparatorAfterRecord = writeLineSeparatorAfterRecord; } @Override protected void configureFromAnnotations(Class beanClass) { if (fieldLengths != null) { return; } try { fieldLengths = FixedWidthFields.forWriting(beanClass); Headers headerAnnotation = AnnotationHelper.findHeadersAnnotation(beanClass); setHeaderWritingEnabled(headerAnnotation != null && headerAnnotation.write()); } catch (Exception ex) { //ignore. } super.configureFromAnnotations(beanClass); FixedWidthFields.setHeadersIfPossible(fieldLengths, this); } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); out.put("Write line separator after record", writeLineSeparatorAfterRecord); out.put("Field lengths", fieldLengths); out.put("Lookahead formats", lookaheadFormats); out.put("Lookbehind formats", lookbehindFormats); out.put("Use default padding for headers", useDefaultPaddingForHeaders); out.put("Default alignment for headers", defaultAlignmentForHeaders); } /** * Clones this configuration object to reuse all user-provided settings, including the fixed-width field configuration. * * @return a copy of all configurations applied to the current instance. */ @Override public final FixedWidthWriterSettings clone() { return (FixedWidthWriterSettings) super.clone(false); } /** * Clones this configuration object to reuse most user-provided settings. This includes the fixed-width field configuration, * but doesn't include other input-specific settings. This method is meant to be used internally only. * * @return a copy of all configurations applied to the current instance. * * @deprecated doesn't really make sense for fixed-width. . Use alternative method {@link #clone(FixedWidthFields)}. */ @Deprecated protected final FixedWidthWriterSettings clone(boolean clearInputSpecificSettings) { return clone(clearInputSpecificSettings, fieldLengths == null ? null : fieldLengths.clone()); } /** * Clones this configuration object to reuse most user-provided settings. Properties that are specific to a given * input (such as header names and selection of fields) will be reset to their defaults. * * To obtain a full copy, use {@link #clone()}. * * @param fields the fixed-width field configuration to be used by the cloned settings object. * * @return a copy of the general configurations applied to the current instance. */ public final FixedWidthWriterSettings clone(FixedWidthFields fields) { return clone(true, fields); } private FixedWidthWriterSettings clone(boolean clearInputSpecificSettings, FixedWidthFields fields) { FixedWidthWriterSettings out = (FixedWidthWriterSettings) super.clone(clearInputSpecificSettings); out.fieldLengths = fields; if (clearInputSpecificSettings) { out.lookaheadFormats = new HashMap(); out.lookbehindFormats = new HashMap(); } else { out.lookaheadFormats = new HashMap(this.lookaheadFormats); out.lookbehindFormats = new HashMap(this.lookbehindFormats); } return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/fixed/Lookup.java000066400000000000000000000126741400120543400273710ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.fixed; import com.univocity.parsers.common.*; import com.univocity.parsers.common.record.*; import java.util.*; import java.util.Map.*; class Lookup { final char[] value; final int[] lengths; final FieldAlignment[] alignments; final boolean[] ignore; final Boolean[] keepPaddingFlags; final char[] paddings; final NormalizedString[] fieldNames; final char wildcard; Context context; Lookup(String value, FixedWidthFields config, FixedWidthFormat format) { this.value = value.toCharArray(); this.lengths = config.getAllLengths(); this.alignments = config.getFieldAlignments(); this.fieldNames = config.getFieldNames(); this.paddings = config.getFieldPaddings(format); this.wildcard = format.getLookupWildcard(); this.ignore = config.getFieldsToIgnore(); this.keepPaddingFlags = config.getKeepPaddingFlags(); } void initializeLookupContext(ParsingContext context, final NormalizedString[] headersToUse) { final String[] headers = NormalizedString.toArray(headersToUse); this.context = new ParsingContextWrapper(context) { RecordFactory recordFactory; final ColumnMap columnMap = new ColumnMap(this, null); @Override public String[] headers() { return headers; } @Override public int indexOf(String header) { return columnMap.indexOf(header); } @Override public int indexOf(Enum header) { return columnMap.indexOf(header); } @Override public Record toRecord(String[] row) { if (recordFactory == null) { recordFactory = new RecordFactory(this); } return recordFactory.newRecord(row); } @Override public RecordMetaData recordMetaData(){ if(recordFactory == null){ recordFactory = new RecordFactory(this); } return recordFactory.getRecordMetaData(); } }; } boolean matches(char[] lookup) { if (value.length > lookup.length) { return false; } for (int i = 0; i < value.length; i++) { char ch = value[i]; if (ch != wildcard && ch != lookup[i]) { return false; } } return true; } static void registerLookahead(String lookup, FixedWidthFields lengths, Map map) { registerLookup("ahead", lookup, lengths, map); } static void registerLookbehind(String lookup, FixedWidthFields lengths, Map map) { registerLookup("behind", lookup, lengths, map); } private static void registerLookup(String direction, String lookup, FixedWidthFields lengths, Map map) { if (lookup == null) { throw new IllegalArgumentException("Look" + direction + " value cannot be null"); } if (lengths == null) { throw new IllegalArgumentException("Lengths of fields associated to look" + direction + " value '" + lookup + "' cannot be null"); } map.put(lookup, lengths); } static Lookup[] getLookupFormats(Map map, FixedWidthFormat format) { if (map.isEmpty()) { return null; } Lookup[] out = new Lookup[map.size()]; int i = 0; for (Entry e : map.entrySet()) { out[i++] = new Lookup(e.getKey(), e.getValue(), format); } Arrays.sort(out, new Comparator() { @Override public int compare(Lookup o1, Lookup o2) { //longer values go first. return o1.value.length < o2.value.length ? 1 : o1.value.length == o2.value.length ? 0 : -1; } }); return out; } static int calculateMaxLookupLength(Lookup[]... lookupArrays) { int max = 0; for (Lookup[] lookups : lookupArrays) { if (lookups != null) { for (Lookup lookup : lookups) { if (max < lookup.value.length) { max = lookup.value.length; } } } } return max; } static int[] calculateMaxFieldLengths(FixedWidthFields fieldLengths, Map lookaheadFormats, Map lookbehindFormats) { List allLengths = new ArrayList(); if (fieldLengths != null) { allLengths.add(fieldLengths.getFieldLengths()); } for (FixedWidthFields lengths : lookaheadFormats.values()) { allLengths.add(lengths.getFieldLengths()); } for (FixedWidthFields lengths : lookbehindFormats.values()) { allLengths.add(lengths.getFieldLengths()); } if (allLengths.isEmpty()) { throw new IllegalStateException("Cannot determine field lengths to use."); } int lastColumn = -1; for (int[] lengths : allLengths) { if (lastColumn < lengths.length) { lastColumn = lengths.length; } } int[] out = new int[lastColumn]; Arrays.fill(out, 0); for (int[] lengths : allLengths) { for (int i = 0; i < lastColumn; i++) { if (i < lengths.length) { int length = lengths[i]; if (out[i] < length) { out[i] = length; } } } } return out; } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/000077500000000000000000000000001400120543400247605ustar00rootroot00000000000000univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/TsvFormat.java000077500000000000000000000066371400120543400275670ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.tsv; import com.univocity.parsers.common.*; import java.util.*; /** * The TSV format configuration, for tab-separated inputs. It offers the options in the default configuration in {@link Format}, as well as * the {@link #escapeChar} character for escaping \t, \n, \r and \ in TSV values. * * Delimiters are defined as tab characters '\t' * * @see com.univocity.parsers.common.Format * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class TsvFormat extends Format { private char escapeChar = '\\'; private char escapedTabChar = 't'; /** * Defines the character used for escaping special characters in TSV inputs: \t, \n, \r and \ . Defaults to '\\' * @param escapeChar the escape character */ public void setEscapeChar(char escapeChar) { this.escapeChar = escapeChar; } /** * Returns the character used for escaping special characters in TSV inputs: \t, \n, \r and \ * * @return the escape character. */ public char getEscapeChar() { return escapeChar; } /** * Returns the character that should be used to represent an escaped tab, i.e. the character before the defined * {@link #getEscapeChar()}. For example, if {@link #getEscapeChar()} == '\\' and {@link #getEscapedTabChar() == 'X'}, * the sequence {@code '\X'} will identify a tab. * * Defaults to {@code 't'}. * * @return the character following the {@link #getEscapeChar()} that represents an escaped tab. */ public char getEscapedTabChar() { return escapedTabChar; } /** * Defines the character that should be used to represent an escaped tab, i.e. the character before the defined * {@link #getEscapeChar()}. For example, if {@link #getEscapeChar()} == '\\' and {@link #getEscapedTabChar() == 'X'}, * the sequence {@code '\X'} will identify a tab. * * Defaults to {@code 't'}. * * @param escapedTabChar the character following the {@link #getEscapeChar()} that represents an escaped tab. */ public void setEscapedTabChar(char escapedTabChar) { this.escapedTabChar = escapedTabChar; } /** * Identifies whether or not a given character is used for escaping special characters in TSV (\t, \n, \r and \). * @param ch the character to be verified * @return true if the given character is escape character, false otherwise */ public boolean isEscapeChar(char ch) { return this.escapeChar == ch; } @Override protected TreeMap getConfiguration() { TreeMap out = new TreeMap(); out.put("Escape character", escapeChar); return out; } @Override public final TsvFormat clone() { return (TsvFormat) super.clone(); } }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/TsvParser.java000077500000000000000000000063051400120543400275630ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.tsv; import com.univocity.parsers.common.*; /** * A very fast TSV parser implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see TsvFormat * @see TsvParserSettings * @see TsvWriter * @see AbstractParser */ public class TsvParser extends AbstractParser { private final boolean joinLines; private final char newLine; private final char escapeChar; private final char escapedTabChar; /** * The TsvParser supports all settings provided by {@link TsvParserSettings}, and requires this configuration to be properly initialized. * * @param settings the parser configuration */ public TsvParser(TsvParserSettings settings) { super(settings); joinLines = settings.isLineJoiningEnabled(); TsvFormat format = settings.getFormat(); newLine = format.getNormalizedNewline(); escapeChar = settings.getFormat().getEscapeChar(); escapedTabChar = format.getEscapedTabChar(); } @Override protected void initialize() { output.trim = ignoreTrailingWhitespace; } /** * {@inheritDoc} */ @Override protected void parseRecord() { if (ignoreLeadingWhitespace && ch != '\t' && ch <= ' ' && whitespaceRangeStart < ch) { ch = input.skipWhitespace(ch, '\t', escapeChar); } while (ch != newLine) { parseField(); if (ch != newLine) { ch = input.nextChar(); if (ch == newLine) { output.emptyParsed(); } } } } private void parseField() { if (ignoreLeadingWhitespace && ch != '\t' && ch <= ' ' && whitespaceRangeStart < ch) { ch = input.skipWhitespace(ch, '\t', escapeChar); } if (ch == '\t') { output.emptyParsed(); } else { while (ch != '\t' && ch != newLine) { if (ch == escapeChar) { ch = input.nextChar(); if (ch == 't' || ch == escapedTabChar) { output.appender.append('\t'); } else if (ch == 'n') { output.appender.append('\n'); } else if (ch == '\\') { output.appender.append('\\'); } else if (ch == 'r') { output.appender.append('\r'); } else if (ch == newLine && joinLines) { output.appender.append(newLine); } else { output.appender.append(escapeChar); if (ch == newLine || ch == '\t') { break; } output.appender.append(ch); } ch = input.nextChar(); } else { ch = output.appender.appendUntil(ch, input, '\t', escapeChar, newLine); } } output.valueParsed(); } } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/TsvParserSettings.java000077500000000000000000000105531400120543400313040ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.tsv; import com.univocity.parsers.common.*; import java.util.*; /** * This is the configuration class used by the TSV parser ({@link TsvParser}) * *

It supports the configuration options provided by {@link CommonParserSettings} only * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see com.univocity.parsers.tsv.TsvParser * @see com.univocity.parsers.tsv.TsvFormat * @see com.univocity.parsers.common.CommonParserSettings */ public class TsvParserSettings extends CommonParserSettings { private boolean lineJoiningEnabled = false; /** * Identifies whether or lines ending with the escape character (defined by {@link TsvFormat#getEscapeChar()} * and followed by a line separator character should be joined with the following line. * * Typical examples include inputs where lines end with sequences such as: {@code '\'+'\n'} and {@code '\'+'\r'+'\n'}. * * When line joining is disabled (the default), the {@link TsvParser} converts sequences containing * the escape character (typically '\') followed by characters 'n' or 'r' into a '\n' or '\r' character. * It will continue processing the contents found in the same line, until a new line character is found. * * If line joining is enabled, the {@link TsvParser} will convert sequences containing * the escape character, followed by characters '\n', '\r' or '\r\n', into a '\n' or '\r' character. * It will continue processing the contents found in the next line, until a new line character is found, given it is * not preceded by another escape character. * * @return {@code true} if line joining is enabled, otherwise {@code false} */ public boolean isLineJoiningEnabled() { return lineJoiningEnabled; } /** * Defines how the parser should handle escaped line separators. By enabling lines joining, * lines ending with the escape character (defined by {@link TsvFormat#getEscapeChar()} * and followed by a line separator character will be joined with the following line. * * Typical examples include inputs where lines end with sequences such as: {@code '\'+'\n'} and {@code '\'+'\r'+'\n'}. * * When line joining is disabled (the default), the {@link TsvParser} converts sequences containing * the escape character (typically '\') followed by characters 'n' or 'r' into a '\n' or '\r' character. * It will continue processing the contents found in the same line, until a new line character is found. * * If line joining is enabled, the {@link TsvParser} will convert sequences containing * the escape character, followed by characters '\n', '\r' or '\r\n', into a '\n' or '\r' character. * It will continue processing the contents found in the next line, until a new line character is found, given it is * not preceded by another escape character. * * @param lineJoiningEnabled a flag indicating whether or not to enable line joining. */ public void setLineJoiningEnabled(boolean lineJoiningEnabled) { this.lineJoiningEnabled = lineJoiningEnabled; } /** * Returns the default TsvFormat configured to handle TSV inputs * * @return and instance of TsvFormat configured to handle TSV */ @Override protected TsvFormat createDefaultFormat() { return new TsvFormat(); } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); } @Override public final TsvParserSettings clone() { return (TsvParserSettings) super.clone(); } @Override public final TsvParserSettings clone(boolean clearInputSpecificSettings) { return (TsvParserSettings) super.clone(clearInputSpecificSettings); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/TsvRoutines.java000066400000000000000000000046761400120543400301450ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.tsv; import com.univocity.parsers.common.routine.*; import java.io.*; /** * A collection of common routines involving the processing of TSV data. */ public class TsvRoutines extends AbstractRoutines { /** * Creates a new instance of the TSV routine class without any predefined parsing/writing configuration. */ public TsvRoutines() { this(null, null); } /** * Creates a new instance of the TSV routine class. * * @param parserSettings configuration to use for TSV parsing */ public TsvRoutines(TsvParserSettings parserSettings) { this(parserSettings, null); } /** * Creates a new instance of the TSV routine class. * * @param writerSettings configuration to use for TSV writing */ public TsvRoutines(TsvWriterSettings writerSettings) { this(null, writerSettings); } /** * Creates a new instance of the TSV routine class. * * @param parserSettings configuration to use for TSV parsing * @param writerSettings configuration to use for TSV writing */ public TsvRoutines(TsvParserSettings parserSettings, TsvWriterSettings writerSettings) { super("TSV parsing/writing routine", parserSettings, writerSettings); } @Override protected TsvParser createParser(TsvParserSettings parserSettings) { return new TsvParser(parserSettings); } @Override protected TsvWriter createWriter(Writer output, TsvWriterSettings writerSettings) { return new TsvWriter(output, writerSettings); } @Override protected TsvParserSettings createDefaultParserSettings() { return new TsvParserSettings(); } @Override protected TsvWriterSettings createDefaultWriterSettings() { return new TsvWriterSettings(); } } univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/TsvWriter.java000066400000000000000000000151451400120543400276020ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.tsv; import com.univocity.parsers.common.*; import java.io.*; import java.nio.charset.*; /** * A powerful and flexible TSV writer implementation. * * @author Univocity Software Pty Ltd - parsers@univocity.com * @see TsvFormat * @see TsvWriterSettings * @see TsvParser * @see AbstractWriter */ public class TsvWriter extends AbstractWriter { private boolean joinLines; private char escapeChar; private char escapedTabChar; private char newLine; /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. *

Important: by not providing an instance of {@link java.io.Writer} to this constructor, only the operations that write to Strings are available.

* * @param settings the TSV writer configuration */ public TsvWriter(TsvWriterSettings settings) { this((Writer) null, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param writer the output resource that will receive TSV records produced by this class. * @param settings the TSV writer configuration */ public TsvWriter(Writer writer, TsvWriterSettings settings) { super(writer, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive TSV records produced by this class. * @param settings the TSV writer configuration */ public TsvWriter(File file, TsvWriterSettings settings) { super(file, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive TSV records produced by this class. * @param encoding the encoding of the file * @param settings the TSV writer configuration */ public TsvWriter(File file, String encoding, TsvWriterSettings settings) { super(file, encoding, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param file the output file that will receive TSV records produced by this class. * @param encoding the encoding of the file * @param settings the TSV writer configuration */ public TsvWriter(File file, Charset encoding, TsvWriterSettings settings) { super(file, encoding, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the TSV records produced by this class. * @param settings the TSV writer configuration */ public TsvWriter(OutputStream output, TsvWriterSettings settings) { super(output, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the TSV records produced by this class. * @param encoding the encoding of the stream * @param settings the TSV writer configuration */ public TsvWriter(OutputStream output, String encoding, TsvWriterSettings settings) { super(output, encoding, settings); } /** * The TsvWriter supports all settings provided by {@link TsvWriterSettings}, and requires this configuration to be properly initialized. * * @param output the output stream that will be written with the TSV records produced by this class. * @param encoding the encoding of the stream * @param settings the TSV writer configuration */ public TsvWriter(OutputStream output, Charset encoding, TsvWriterSettings settings) { super(output, encoding, settings); } /** * Initializes the TSV writer with TSV-specific configuration * * @param settings the TSV writer configuration */ protected final void initialize(TsvWriterSettings settings) { this.escapeChar = settings.getFormat().getEscapeChar(); this.escapedTabChar = settings.getFormat().getEscapedTabChar(); this.joinLines = settings.isLineJoiningEnabled(); this.newLine = settings.getFormat().getNormalizedNewline(); } @Override protected void processRow(Object[] row) { for (int i = 0; i < row.length; i++) { if (i != 0) { appendToRow('\t'); } String nextElement = getStringValue(row[i]); boolean allowTrim = allowTrim(i); int originalLength = appender.length(); append(nextElement, allowTrim); //skipped all whitespaces and wrote nothing if (appender.length() == originalLength && nullValue != null && !nullValue.isEmpty()) { append(nullValue, allowTrim); } appendValueToRow(); } } private void append(String element, boolean allowTrim) { if (element == null) { element = nullValue; } if (element == null) { return; } int start = 0; if (allowTrim && this.ignoreLeading) { start = skipLeadingWhitespace(whitespaceRangeStart, element); } final int length = element.length(); int i = start; char ch = '\0'; for (; i < length; i++) { ch = element.charAt(i); if (ch == '\t' || ch == '\n' || ch == '\r' || ch == '\\') { appender.append(element, start, i); start = i + 1; appender.append(escapeChar); if (ch == '\t') { appender.append(escapedTabChar); } else if (ch == '\n') { appender.append(joinLines ? newLine : 'n'); } else if (ch == '\\') { appender.append('\\'); } else { appender.append(joinLines ? newLine : 'r'); } } } appender.append(element, start, i); if (allowTrim && ch <= ' ' && ignoreTrailing && whitespaceRangeStart < ch) { appender.updateWhitespace(); } } }univocity-parsers-2.9.1/src/main/java/com/univocity/parsers/tsv/TsvWriterSettings.java000066400000000000000000000116541400120543400313240ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.tsv; import com.univocity.parsers.common.*; import java.util.*; /** * This is the configuration class used by the TSV writer ({@link TsvWriter}) * *

It does not offer additional configuration options on top of the ones provided by the {@link CommonWriterSettings}

* * @see com.univocity.parsers.tsv.TsvWriter * @see com.univocity.parsers.tsv.TsvFormat * @see com.univocity.parsers.common.CommonWriterSettings * * @author Univocity Software Pty Ltd - parsers@univocity.com * */ public class TsvWriterSettings extends CommonWriterSettings { private boolean lineJoiningEnabled = false; /** * Identifies whether values containing line endings should have the line separator written using * the escape character (defined by {@link TsvFormat#getEscapeChar()} followed by the actual line separator character * instead of other characters such as the standard letters 'n' and 'r' * * When line joining is disabled (the default), the {@link TsvWriter} will convert new line characters into * sequences containing the escape character (typically '\') followed by characters 'n' or 'r'. * No matter how many line separators the values written contain, the will be escaped and the entire output * of a record will be written into a single line of text. For example, '\n' and '\r' characters will be * written as: {@code '\'+'n'} and {@code '\'+'r'}. * * If line joining is enabled, the {@link TsvWriter} will convert line new line characters into sequences * containing the escape character, followed by characters '\n', '\r' or both. * A new line of text will be generated for each line separator found in the value to be written, "marking" the end * of each line with the escape character to indicate the record continues on the next line. For example, '\n' and '\r' * characters will be written as: {@code '\'+'\n'} and {@code '\'+'\r'}. * * @return {@code true} if line joining is enabled, otherwise {@code false} */ public boolean isLineJoiningEnabled() { return lineJoiningEnabled; } /** * Defines how the writer should handle the escaping of line separators. * Values containing line endings should be escaped and the line separator characters can be written using * the escape character (defined by {@link TsvFormat#getEscapeChar()} followed by the actual line separator character * instead of other characters such as the standard letters 'n' and 'r' * * When line joining is disabled (the default), the {@link TsvWriter} will convert new line characters into * sequences containing the escape character (typically '\') followed by characters 'n' or 'r'. * No matter how many line separators the values written contain, the will be escaped and the entire output * of a record will be written into a single line of text. For example, '\n' and '\r' characters will be * written as: {@code '\'+'n'} and {@code '\'+'r'}. * * If line joining is enabled, the {@link TsvWriter} will convert line new line characters into sequences * containing the escape character, followed by characters '\n', '\r' or both. * A new line of text will be generated for each line separator found in the value to be written, "marking" the end * of each line with the escape character to indicate the record continues on the next line. For example, '\n' and '\r' * characters will be written as: {@code '\'+'\n'} and {@code '\'+'\r'}. * * @param lineJoiningEnabled a flag indicating whether or not to enable line joining. */ public void setLineJoiningEnabled(boolean lineJoiningEnabled) { this.lineJoiningEnabled = lineJoiningEnabled; } /** * Returns the default TsvFormat. * @return and instance of TsvFormat configured to produce TSV outputs. */ @Override protected TsvFormat createDefaultFormat() { return new TsvFormat(); } @Override protected void addConfiguration(Map out) { super.addConfiguration(out); } @Override public final TsvWriterSettings clone() { return (TsvWriterSettings) super.clone(); } @Override public final TsvWriterSettings clone(boolean clearInputSpecificSettings) { return (TsvWriterSettings) super.clone(clearInputSpecificSettings); } } univocity-parsers-2.9.1/src/test/000077500000000000000000000000001400120543400167705ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/000077500000000000000000000000001400120543400177115ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/000077500000000000000000000000001400120543400204675ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/000077500000000000000000000000001400120543400225205ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/000077500000000000000000000000001400120543400241775ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/ParserTestCase.java000066400000000000000000000045351400120543400277410ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.csv.*; import java.io.*; import java.net.*; import java.util.*; import static org.testng.Assert.*; public abstract class ParserTestCase { protected RowListProcessor processor = newRowListProcessor(); protected RowListProcessor newRowListProcessor() { return new RowListProcessor(); } public static Reader newReader(String path) throws UnsupportedEncodingException { Reader reader = new InputStreamReader(ParserTestCase.class.getResourceAsStream(path), "UTF-8"); return reader; } public void assertHeadersAndValuesMatch(RowListProcessor processor, String[] expectedHeaders, Object[][] expectedResult) { String[] headers = processor.getHeaders(); TestUtils.assertEquals(headers, expectedHeaders); List rows = processor.getRows(); assertEquals(rows.size(), expectedResult.length); for (int i = 0; i < expectedResult.length; i++) { String[] row = rows.get(i); Object[] expectedRow = expectedResult[i]; assertEquals(row, expectedRow, "Inconsistency found on row " + i); } } public void assertHeadersAndValuesMatch(String[] expectedHeaders, Object[][] expectedResult) { assertHeadersAndValuesMatch(processor, expectedHeaders, expectedResult); } public String readFileContent(File file) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(file)); String line; StringBuilder fileContent = new StringBuilder(); while((line = reader.readLine()) != null) { fileContent.append(line).append('\n'); } return fileContent.toString(); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/000077500000000000000000000000001400120543400265345ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/AnnotationHelperTest.java000066400000000000000000000043101400120543400335070ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations; import com.univocity.parsers.annotations.helpers.AnnotationHelper; import java.io.*; import java.lang.reflect.Constructor; import java.util.*; import com.univocity.parsers.annotations.meta.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import static org.testng.Assert.*; public class AnnotationHelperTest { @Test public void shouldCreateAnnotationHelper() throws Exception { Constructor c = AnnotationHelper.class.getDeclaredConstructor(); c.setAccessible(true); AnnotationHelper helper = c.newInstance(); assertNotNull(helper); } @Test public void testContentCleaner() { CsvWriterSettings settings = new CsvWriterSettings(); settings.getFormat().setDelimiter(';'); settings.getFormat().setLineSeparator("\n"); settings.setRowWriterProcessor(new BeanWriterProcessor(CleanBeanTest.class)); StringWriter out = new StringWriter(); CsvWriter writer = new CsvWriter(out, settings); List beans = new ArrayList(); beans.add(new CleanBeanTest("this;is;a;test", ";and;another;test;", 1)); beans.add(new CleanBeanTest("this;is;b;test", ";", 2)); beans.add(new CleanBeanTest("this;is;c;test", ";;", 3)); writer.processRecordsAndClose(beans); assertEquals(out.toString(), "" + "thisistest;andanothertest;1\n" + "thisisbtest;;2\n" + "thisisctest;;\n"); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/000077500000000000000000000000001400120543400274625ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/Clean.java000066400000000000000000000026421400120543400313530ustar00rootroot00000000000000/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; import com.univocity.parsers.annotations.*; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Convert(conversionClass = ContentCleaner.class) @Parsed @IntArray(ints = {}) public @interface Clean { @Copy(to = Parsed.class) String field() default ""; @Copy(to = Parsed.class) int index() default -1; @Copy(to = Convert.class, property = "args") String remove(); //not used for anything else than testing whether the AnnotationHelper will bomb @Copy(to = IntArray.class, property = "ints") int[] theInts(); } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/CleanBeanTest.java000066400000000000000000000020741400120543400330000ustar00rootroot00000000000000/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; public class CleanBeanTest { @Clean(remove = "a;", theInts = 0) String a; @Clean(remove = ";", theInts = {1,2}) String b; @Clean(remove = "3", theInts = {}) int c; public CleanBeanTest(String a, String b, int c) { this.a = a; this.b = b; this.c = c; } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/ContentCleaner.java000066400000000000000000000036231400120543400332350ustar00rootroot00000000000000/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; import com.univocity.parsers.common.input.*; import com.univocity.parsers.conversions.*; import java.util.*; public class ContentCleaner implements Conversion { private final char[] charsToRemove; private final char min; private final char max; public ContentCleaner(String[] args) { charsToRemove = args[0].toCharArray(); Arrays.sort(charsToRemove); min = charsToRemove[0]; max = charsToRemove[charsToRemove.length - 1]; } @Override public String execute(Object input) { return clean(input); } @Override public Object revert(String input) { return clean(input); } private String clean(Object input){ String result = String.valueOf(input); StringBuilder out = null; for(int i = 0; i < result.length(); i++){ char ch = result.charAt(i); if(ch >= min && ch <= max){ if(Arrays.binarySearch(charsToRemove, ch) >= 0){ if(out == null){ out = new StringBuilder(result.length()); out.append(result, 0, i); } } } else if (out != null){ out.append(ch); } } if(out != null){ result = out.toString(); } return result; } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/IntArray.java000066400000000000000000000017711400120543400320640ustar00rootroot00000000000000/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface IntArray { int[] ints(); } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/MetaAnnotationTest.java000066400000000000000000000050751400120543400341150ustar00rootroot00000000000000/******************************************************************************* * Copyright 2018 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.csv.*; import com.univocity.parsers.fixed.*; import org.testng.annotations.*; import java.io.*; import java.util.*; import static org.testng.Assert.*; /** * @author Univocity Software Pty Ltd - dev@univocity.com */ public class MetaAnnotationTest { @Test public void testParseWithMetaAnnotation() throws Exception { BeanListProcessor rowProcessor = new BeanListProcessor(ReplacementBean.class); CsvParserSettings parserSettings = new CsvParserSettings(); parserSettings.getFormat().setLineSeparator("\n"); parserSettings.setProcessor(rowProcessor); CsvParser parser = new CsvParser(parserSettings); parser.parse(new StringReader("a,BB,x,y,z\n`Va`,`Vb`,,,`vc`")); List beans = rowProcessor.getBeans(); assertEquals(beans.size(), 1); assertEquals(beans.get(0).a, "Va"); assertEquals(beans.get(0).b, "VB"); assertEquals(beans.get(0).c, "VC"); } @Test public void testWriteWithMetaAnnotation() throws Exception { BeanWriterProcessor rowProcessor = new BeanWriterProcessor(ReplacementBean.class); CsvWriterSettings settings = new CsvWriterSettings(); settings.getFormat().setLineSeparator("\n"); settings.setRowWriterProcessor(rowProcessor); StringWriter out = new StringWriter(); CsvWriter writer = new CsvWriter(out, settings); List beans = new ArrayList(); beans.add(new ReplacementBean("iii4", "blah blah", "`c`")); beans.add(new ReplacementBean("zzz7674", "etc", "`c`")); writer.processRecordsAndClose(beans); assertEquals(out.toString(), "" + "iii4,BLAH BLAH,,,C\n" + "zzz7674,ETC,,,C\n"); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/MyReplacement.java000066400000000000000000000024701400120543400330750ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; import com.univocity.parsers.annotations.*; import java.lang.annotation.*; /** * @author Univocity Software Pty Ltd - dev@univocity.com */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Replace(expression = "`", replacement = "") @Parsed public @interface MyReplacement { @Copy(to = Parsed.class) String field() default ""; @Copy(to = Parsed.class, property = "index") int myIndex() default -1; } MyReplacementUpperCase.java000066400000000000000000000024671400120543400346340ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; import com.univocity.parsers.annotations.*; import java.lang.annotation.*; /** * @author Univocity Software Pty Ltd - dev@univocity.com */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @MyReplacement @UpperCase @Trim public @interface MyReplacementUpperCase { @Copy(to = MyReplacement.class) String field() default ""; @Copy(to = MyReplacement.class, property = "myIndex") int idx() default -1; } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/annotations/meta/ReplacementBean.java000066400000000000000000000023141400120543400333520ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.annotations.meta; /** * @author Univocity Software Pty Ltd - dev@univocity.com */ public class ReplacementBean { @MyReplacement public String a; @MyReplacementUpperCase(field = "BB") public String b; @MyReplacementUpperCase(idx = 4) public String c; public ReplacementBean(String a, String b, String c) { this.a = a; this.b = b; this.c = c; } public ReplacementBean() { } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/000077500000000000000000000000001400120543400254675ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/AbstractWriterTest.java000066400000000000000000000124471400120543400321420ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.ParserTestCase; import com.univocity.parsers.csv.CsvWriter; import com.univocity.parsers.csv.CsvWriterSettings; import com.univocity.parsers.fixed.*; import com.univocity.parsers.fixed.FixedWidthFields; import org.testng.annotations.Test; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEqualsNoOrder; public class AbstractWriterTest extends ParserTestCase { @Test public void testWriteRowWithObjectCollection() throws IOException { FixedWidthWriterSettings settings = new FixedWidthWriterSettings(new FixedWidthFields(4, 4)); settings.getFormat().setLineSeparator("\n"); File file = File.createTempFile("test", "csv"); FixedWidthWriter writer = new FixedWidthWriter(file, settings); Collection objects = new ArrayList(); objects.add("A"); objects.add("B"); writer.writeRow(objects); writer.close(); assertEquals(readFileContent(file), "A B \n"); } @Test public void testWriteRowWithNullObjectCollection() throws IOException { FixedWidthWriterSettings settings = new FixedWidthWriterSettings(new FixedWidthFields(4, 4)); settings.getFormat().setLineSeparator("\n"); File file = File.createTempFile("test", "csv"); FixedWidthWriter writer = new FixedWidthWriter(file, settings); Collection objects = null; writer.writeRow(objects); writer.close(); assertEquals(readFileContent(file), ""); } @Test public void testWriteStringRows() throws IOException { FixedWidthWriterSettings settings = new FixedWidthWriterSettings(new FixedWidthFields(4, 4)); settings.getFormat().setLineSeparator("\n"); settings.addFormatForLookahead("MASTER", new FixedWidthFields(3, 3, 3, 3)); File file = File.createTempFile("test", "csv"); FixedWidthWriter writer = new FixedWidthWriter(file, settings); List> rows = new ArrayList>(); rows.add(Arrays.asList("A", "B")); rows.add(Arrays.asList("C", "D")); writer.writeStringRows(rows); writer.close(); assertEquals(readFileContent(file), "A B \nC D \n"); } @Test public void testWriteBufferedWriter() throws IOException { FixedWidthWriterSettings settings = new FixedWidthWriterSettings(new FixedWidthFields(3, 3)); settings.getFormat().setLineSeparator("\n"); File file = File.createTempFile("test", "csv"); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); FixedWidthWriter writer = new FixedWidthWriter(bufferedWriter, settings); writer.writeRow("Ã", "É"); writer.close(); assertEquals(readFileContent(file), "Ã É \n"); } @Test public void testRowExpansion() { StringWriter output = new StringWriter(); CsvWriterSettings settings = new CsvWriterSettings(); settings.setExpandIncompleteRows(true); settings.getFormat().setLineSeparator("\n"); settings.setHeaderWritingEnabled(true); settings.setHeaders("A", "B", "C", "D", "E", "F"); CsvWriter writer = new CsvWriter(output, settings); writer.writeRow(); writer.writeRow("V1", "V2", "V3"); writer.writeRow("V1", "V2", "V3", 4, 5); writer.writeRow("V1", "V2", "V3", 4, 5, 6); writer.close(); assertEquals(output.toString(), "A,B,C,D,E,F\n,,,,,\nV1,V2,V3,,,\nV1,V2,V3,4,5,\nV1,V2,V3,4,5,6\n"); } @Test public void testSelectFields() { StringWriter output = new StringWriter(); CsvWriterSettings settings = new CsvWriterSettings(); settings.setHeaderWritingEnabled(true); settings.setColumnReorderingEnabled(true); settings.setHeaders("A", "B", "C"); settings.selectFields("A", "C"); CsvWriter writer = new CsvWriter(output, settings); writer.writeRow("V1", "V2", "V3"); writer.writeRow("V1", "V2", "V3"); writer.writeRow("V1", "V2", "V3"); writer.close(); assertEquals(output.toString(), "A,C\nV1,V3\nV1,V3\nV1,V3\n"); } @Test public void testExcludeFields() { StringWriter output = new StringWriter(); CsvWriterSettings settings = new CsvWriterSettings(); settings.setHeaderWritingEnabled(true); settings.setColumnReorderingEnabled(true); settings.setHeaders("A", "B", "C"); settings.excludeFields("B"); CsvWriter writer = new CsvWriter(output, settings); writer.writeRow("V1", "V2", "V3"); writer.writeRow("V1", "V2", "V3"); writer.writeRow("V1", "V2", "V3"); writer.close(); assertEquals(output.toString(), "A,C\nV1,V3\nV1,V3\nV1,V3\n"); } }univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/DataProcessingExceptionTest.java000066400000000000000000000070661400120543400337700ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import com.univocity.parsers.annotations.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.util.*; import static org.testng.Assert.*; public class DataProcessingExceptionTest { @Test public void testRestrictionOfValuesDisplayedInErrorMessage() { String[] headers = new String[]{"aaaa", "bbbb", "cccc"}; DataProcessingException ex = new DataProcessingException("{x}boom: '{value}' is broken. Headers: {headers}"); assertEquals(ex.getMessage(), "" + "{x}boom: '{value}' is broken. Headers: {headers}"); ex.setValue("Mary had a little lamb"); assertEquals(ex.getMessage(), "" + "{x}boom: 'Mary had a little lamb' is broken. Headers: {headers}\n" + "Internal state when error was thrown: value=Mary had a little lamb"); ex.setErrorContentLength(14); assertEquals(ex.getMessage(), "" + "{x}boom: '... a little lamb' is broken. Headers: {headers}\n" + "Internal state when error was thrown: value=... a little lamb"); ex.setValue("headers", headers); assertEquals(ex.getMessage(), "" + "{x}boom: '... a little lamb}' is broken. Headers: ...a, bbbb, cccc]\n" + "Internal state when error was thrown: value=... a little lamb"); ex.setErrorContentLength(0); assertEquals(ex.getMessage(), "" + "{x}boom: '{value}' is broken. Headers: {headers}"); } @Test public void testRestrictionOfValuesDisplayedInErrorMessageWhileParsing() { CsvParserSettings parserSettings = new CsvParserSettings(); parserSettings.setErrorContentLength(0); BeanListProcessor beanProcessor = new BeanListProcessor(A.class); parserSettings.getFormat().setLineSeparator("\n"); parserSettings.setProcessor(beanProcessor); CsvParser parser = new CsvParser(parserSettings); try { parser.parse(new StringReader("AA,BB\nA,B\nC,D,\n1,true")); } catch (DataProcessingException e) { assertFalse(e.getMessage().contains("Unable to set value 'A'")); } } @Test public void testRestrictionOfValuesDisplayedInErrorMessageWhileWriting() { CsvWriterSettings writerSettings = new CsvWriterSettings(); writerSettings.setErrorContentLength(0); BeanWriterProcessor beanProcessor = new BeanWriterProcessor(A.class); writerSettings.getFormat().setLineSeparator("\n"); writerSettings.setHeaders("AA", "BB"); writerSettings.setRowWriterProcessor(beanProcessor); CsvWriter writer = new CsvWriter(new StringWriter(), writerSettings); try { writer.processRecordsAndClose(new Object[]{"I'm not a bean", null, new A(new Date())}); } catch (DataProcessingException e) { assertFalse(e.getMessage().contains("I'm not a bean")); } } public static class A { @Parsed(field = "AA") private Date a; public A(){ } public A(Date a) { this.a = a; } } }univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/NormalizedStringTest.java000066400000000000000000000176141400120543400324760ustar00rootroot00000000000000/******************************************************************************* * Copyright 2019 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common; import org.testng.annotations.*; import java.util.*; import static org.testng.Assert.*; public class NormalizedStringTest { private NormalizedString A = NormalizedString.valueOf("A"); private NormalizedString a = NormalizedString.valueOf("a"); private NormalizedString _a_ = NormalizedString.valueOf(" a "); private NormalizedString _A_ = NormalizedString.valueOf(" A "); NormalizedString[] normalized = new NormalizedString[]{A, a, _a_, _A_}; private NormalizedString dA = NormalizedString.literalValueOf("A"); private NormalizedString da = NormalizedString.literalValueOf("a"); private NormalizedString d_a_ = NormalizedString.literalValueOf(" a "); private NormalizedString d_A_ = NormalizedString.literalValueOf(" A "); NormalizedString[] literal = new NormalizedString[]{dA, da, d_a_, d_A_}; @Test public void testEqualsHashCodeContract() throws Exception { for (int i = 0; i < normalized.length; i++) { for (int j = 0; j < normalized.length; j++) { assertEquals(normalized[i], normalized[j]); assertEquals(normalized[j], normalized[i]); assertEquals(normalized[i].hashCode(), normalized[j].hashCode()); } } for (int i = 0; i < literal.length; i++) { for (int j = 0; j < literal.length; j++) { if (i == j) { assertEquals(literal[i], literal[j]); assertEquals(literal[j], literal[i]); assertEquals(literal[i].hashCode(), literal[j].hashCode()); } else { assertFalse(literal[i].equals(literal[j])); assertFalse(literal[j].equals(literal[i])); } } } for (int i = 0; i < normalized.length; i++) { if (normalized[i].equals(literal[i])) { assertEquals(literal[i], normalized[i]); assertEquals(normalized[i].hashCode(), literal[i].hashCode()); } else { assertNotEquals(literal[i], normalized[i]); } } } @Test public void testCompareToContract() throws Exception { for (int i = 0; i < normalized.length; i++) { for (int j = 0; j < normalized.length; j++) { assertEquals(normalized[i], normalized[j]); assertTrue(normalized[i].compareTo(normalized[j]) == 0); } } for (int i = 0; i < literal.length; i++) { for (int j = 0; j < literal.length; j++) { if (i == j) { assertEquals(literal[i], literal[j]); assertTrue(literal[i].compareTo(literal[j]) == 0); } else { assertFalse(literal[i].equals(literal[j])); assertFalse(literal[i].compareTo(literal[j]) == 0); } } } for (int i = 0; i < normalized.length; i++) { if (normalized[i].equals(literal[i])) { assertTrue(normalized[i].compareTo(literal[i]) == 0); } else { assertFalse(normalized[i].compareTo(literal[i]) == 0); } } } @DataProvider private Object[][] setProvider() { return new Object[][]{ {new HashSet(), "HashSet"}, {new TreeSet(), "TreeSet"} }; } @Test(dataProvider = "setProvider") public void testSetBehaves(Set set, String name) { Collections.addAll(set, normalized); // 1 assertEquals(set.size(), 1); //hashcode & equals are the same for all elements, so we'll end up with just one. for (NormalizedString element : normalized) { assertTrue(set.contains(element)); } int count = 0; for (NormalizedString element : literal) { if (set.contains(element)) { count++; } } assertEquals(count, 1); // only one literal element will match because the original strings are compared and if they match then the NormalizedString always matches. Collections.addAll(set, literal); assertEquals(set.size(), literal.length); //all literal elements should be in the set for (NormalizedString element : normalized) { assertTrue(set.contains(element)); //normalized Strings should all be found here } for (NormalizedString element : literal) { assertTrue(set.contains(element)); //literal Strings should all be found here } } @Test public void testComparisonForOrdering() { TreeSet set = new TreeSet(); Collections.addAll(set, literal); TreeSet stringSet = new TreeSet(); for (NormalizedString element : literal) { stringSet.add(element.toString()); } assertEquals(stringSet.toString(), set.toString()); set.add(NormalizedString.valueOf("0")); set.add(NormalizedString.literalValueOf(" 0")); set.add(NormalizedString.literalValueOf("Z")); set.add(NormalizedString.valueOf("z")); stringSet.add("0"); stringSet.add(" 0"); stringSet.add("Z"); stringSet.add("z"); assertEquals(set.toString(), stringSet.toString()); } @Test public void testHashMap() { Map map = new HashMap(); putString(map, "A"); putString(map, "'a'"); //literal assertEquals(map.get(NormalizedString.valueOf("A")), "A"); assertEquals(map.get(NormalizedString.literalValueOf("A")), "A"); assertEquals(map.get(NormalizedString.valueOf("'a'")), "'a'"); assertEquals(map.get(NormalizedString.literalValueOf("a")), "'a'"); // Unspecified behavior here as A and 'a' clash (A should be a literal in this test, but it isn't). // A normalized, non literal 'a' can match either the literal 'a' or the normalized A, depending on the // HashMap implementation. These entries have the same hashcode and the equals method will // compare this: // Search entry: // NormalizedString.valueOf("a") - not a literal, normalized value = a // Keys in map // NormalizedString.valueOf("A") - not literal, normalized value = a (will match) // NormalizedString.valueOf("'a'") - literal, original value = a (will also match) // The entry picked up first by the map implementation will be returned. // On JDK 6 this is the expected output: // assertEquals(map.get(NormalizedString.valueOf("a")), "'a'"); // On JDK 8 the expected output is: // assertEquals(map.get(NormalizedString.valueOf("a")), "A"); } @Test public void testTreeMap() { Map map = new TreeMap(); putString(map, "A"); putString(map, "'a'"); //literal assertEquals(map.get(NormalizedString.valueOf("A")), "A"); assertEquals(map.get(NormalizedString.literalValueOf("A")), "A"); assertEquals(map.get(NormalizedString.valueOf("'a'")), "'a'"); assertEquals(map.get(NormalizedString.literalValueOf("a")), "'a'"); assertEquals(map.get(NormalizedString.valueOf("a")), "A"); //compareTo implementation will run a compareTo against normalized values (normalized against normalized) } private void putString(Map map, String str) { map.put(NormalizedString.valueOf(str), str); } @Test public void identifyLiterals(){ NormalizedString[] s = NormalizedString.toArray("a", "A", " a ", " A ", "a ", "A ", "B"); Set set = new HashSet(); Collections.addAll(set, s); assertEquals(set.size(), 2); NormalizedString.identifyLiterals(s); assertTrue(s[0].isLiteral()); assertTrue(s[1].isLiteral()); assertTrue(s[2].isLiteral()); assertTrue(s[3].isLiteral()); assertTrue(s[4].isLiteral()); assertTrue(s[5].isLiteral()); assertFalse(s[6].isLiteral()); set = new HashSet(); Collections.addAll(set, s); assertEquals(set.size(), 7); } }univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/000077500000000000000000000000001400120543400267355ustar00rootroot00000000000000ExcludeFieldEnumSelectorTest.java000066400000000000000000000023401400120543400352430ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import org.testng.annotations.*; import static org.testng.Assert.*; public class ExcludeFieldEnumSelectorTest { @Test public void getFieldsToExtract() { ExcludeFieldEnumSelector selector = new ExcludeFieldEnumSelector(); selector.add(TestEnum.D, TestEnum.A); int[] indexes = selector.getFieldIndexes(new String[]{"A", "B", "C", "D", "E", "F"}); int[] expected = new int[]{1, 2, 4, 5}; assertEquals(indexes, expected); } } ExcludeFieldNameSelectorTest.java000066400000000000000000000023221400120543400352170ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import org.testng.annotations.*; import static org.testng.Assert.*; public class ExcludeFieldNameSelectorTest { @Test public void getFieldsToExtract() { ExcludeFieldNameSelector selector = new ExcludeFieldNameSelector(); selector.add("D", "A"); int[] indexes = selector.getFieldIndexes(new String[]{"A", "B", "C", "D", "E", "F"}); int[] expected = new int[]{1, 2, 4, 5}; assertEquals(indexes, expected); } } ExcludeIndexSelectorTest.java000066400000000000000000000023221400120543400344420ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import org.testng.annotations.*; import static org.testng.Assert.*; public class ExcludeIndexSelectorTest { @Test public void getFieldsToExtract() { ExcludeFieldIndexSelector selector = new ExcludeFieldIndexSelector(); selector.add(3, 0); int[] indexes = selector.getFieldIndexes(new String[]{null, null, null, null, null, null}); int[] expected = new int[]{1, 2, 4, 5}; assertEquals(indexes, expected); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/FieldEnumSelectorTest.java000066400000000000000000000023051400120543400340110ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import org.testng.annotations.*; import static org.testng.Assert.*; public class FieldEnumSelectorTest { @Test public void getFieldsToExtract() { FieldEnumSelector selector = new FieldEnumSelector(); selector.add(TestEnum.D, TestEnum.A); int[] indexes = selector.getFieldIndexes(new String[]{"A", "B", "C", "D", "E", "F"}); int[] expected = new int[]{3, 0}; assertEquals(indexes, expected); } } FieldIndexSelectorTest.java000066400000000000000000000022741400120543400341020ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import org.testng.annotations.*; import static org.testng.Assert.*; public class FieldIndexSelectorTest { @Test public void getFieldsToExtract() { FieldIndexSelector selector = new FieldIndexSelector(); selector.add(3, 0); int[] indexes = selector.getFieldIndexes(new String[]{null, null, null, null, null, null}); int[] expected = new int[]{3, 0}; assertEquals(indexes, expected); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/FieldNameSelectorTest.java000066400000000000000000000045741400120543400337770ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; import org.testng.annotations.*; import java.util.*; import static org.testng.Assert.*; public class FieldNameSelectorTest { @Test public void getFieldsToExtract() { FieldNameSelector selector = new FieldNameSelector(); selector.add("D", "A"); int[] indexes = selector.getFieldIndexes(new String[]{"A", "B", "C", "D", "E", "F"}); int[] expected = new int[]{3, 0}; assertEquals(indexes, expected); } @Test public void getFieldsToExtract2() { String[] fields = new String[]{"SellerID", "Brand", "MPN", "SoldPrice", "Shipping", "TotalAmount", "Currency", "SoldPriceUSD", "ShippingUSD", "TotalAmountUSD", "SoldQuantity", "Condition", "Format", "SoldDate", "ProductRating", "UPC", "EAN", "ItemLocation", "Title", "PictureURL", "ListingURL"}; String[] selection = new String[]{"Format", "PictureURL", "SellerID", "SoldDate", "ListingURL", "Currency", "Condition", "Title", "BidPrice", "SoldPrice", "SoldPriceUSD", "SystemCurrency", "SoldPriceDiscounted", "Shipping", "ShippingUSD", "TotalAmount", "TotalAmountUSD", "SoldQuantity", "ProductRating", "Brand", "EAN", "UPC", "MPN", "ItemLocation", "SellerID", "SellerFeedback", "StockQuantity", "eBayItemNumber", "PicURL1", "PicURL2", "PicURL3", "PicURL4", "PicURL5", "PicURL6", "PicURL7", "PicURL8", "PicURL9", "PicURL10"}; FieldNameSelector selector = new FieldNameSelector(); selector.add(selection); int[] indexes = selector.getFieldIndexes(fields); assertEquals(Arrays.toString(indexes), "[12, 19, 0, 13, 20, 6, 11, 18, -1, 3, 7, -1, -1, 4, 8, 5, 9, 10, 14, 1, 16, 15, 2, 17, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]"); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/fields/TestEnum.java000066400000000000000000000015221400120543400313440ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.fields; public enum TestEnum { A, B, C, D, E, F } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/input/000077500000000000000000000000001400120543400266265ustar00rootroot00000000000000DefaultCharInputReaderTest.java000066400000000000000000000054311400120543400346020ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/input/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import org.testng.annotations.*; import java.io.*; import static org.testng.Assert.*; public class DefaultCharInputReaderTest { private void assertEOF(DefaultCharInputReader reader) { try { reader.nextChar(); fail("Expected EOFException"); } catch (EOFException ex) { //pass } } @Test public void testInputReading() { DefaultCharInputReader reader = new DefaultCharInputReader("\n\r".toCharArray(), '\n', 2, -1, true); reader.start(new StringReader("a")); assertEquals('a', reader.nextChar()); assertEquals(reader.lastIndexOf('a'), 0); assertEquals(reader.lastIndexOf('b'), -1); assertEOF(reader); reader.start(new StringReader("ab")); assertEquals('a', reader.nextChar()); assertEquals('b', reader.nextChar()); assertEquals(reader.lastIndexOf('a'), 0); assertEquals(reader.lastIndexOf('b'), 1); assertEOF(reader); reader.start(new StringReader("a\n\r")); assertEquals('a', reader.nextChar()); assertEquals('\n', reader.nextChar()); assertEOF(reader); assertEquals(reader.lastIndexOf('a'), 0); assertEquals(reader.lastIndexOf('\n'), 1); reader.start(new StringReader("a\r\n")); assertEquals('a', reader.nextChar()); assertEquals(reader.lastIndexOf('a'), 0); assertEquals('\r', reader.nextChar()); assertEquals(reader.lastIndexOf('\r'), 1); assertEquals('\n', reader.nextChar()); assertEquals(reader.lastIndexOf('\n'), 2); assertEOF(reader); assertEquals(reader.lastIndexOf('a'), 0); assertEquals(reader.lastIndexOf('\r'), 1); assertEquals(reader.lastIndexOf('\n'), 2); reader.start(new StringReader("\n\ra")); assertEquals('\n', reader.nextChar()); assertEquals('a', reader.nextChar()); assertEOF(reader); assertEquals(reader.lastIndexOf('\n'), 0); assertEquals(reader.lastIndexOf('\r'), 1); assertEquals(reader.lastIndexOf('a'), 2); reader.start(new StringReader("\n\r")); assertEquals('\n', reader.nextChar()); assertEquals(reader.lastIndexOf('\n'), 0); assertEquals(reader.lastIndexOf('\r'), 1); assertEOF(reader); } } ExpandingCharAppenderTest.java000066400000000000000000000137051400120543400344520ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/input/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import org.testng.annotations.*; import static org.testng.Assert.*; public class ExpandingCharAppenderTest { @Test public void testAppendIgnoringWhitespace() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(2, null, -1); assertEquals(a.chars.length, 2); a.append('a'); a.append('b'); a.appendIgnoringWhitespace(' '); assertEquals(a.toString(), "ab"); assertEquals(a.chars.length, 6); a.appendIgnoringWhitespace('c'); a.appendIgnoringWhitespace(' '); assertEquals(a.toString(), "ab c"); assertEquals(a.chars.length, 6); } @Test public void testAppendIgnoringPadding() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(1, null, -1); assertEquals(a.chars.length, 1); a.append('_'); a.append('b'); a.appendIgnoringPadding('_', '_'); assertEquals(a.toString(), "_b"); assertEquals(a.chars.length, 4); a.appendIgnoringPadding('a', '_'); assertEquals(a.toString(), "_b_a"); assertEquals(a.chars.length, 4); } @Test public void testAppendIgnoringWhitespaceAndPadding() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(1, null, -1); assertEquals(a.chars.length, 1); a.append('_'); a.append('b'); a.appendIgnoringWhitespaceAndPadding(' ', '_'); assertEquals(a.toString(), "_b"); assertEquals(a.chars.length, 4); a.appendIgnoringWhitespaceAndPadding('_', '_'); assertEquals(a.toString(), "_b"); assertEquals(a.chars.length, 4); a.appendIgnoringWhitespaceAndPadding('c', '_'); assertEquals(a.toString(), "_b _c"); assertEquals(a.chars.length, 10); } @Test public void testAppend() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(1, null, -1); a.append('a'); assertEquals(a.toString(), "a"); assertEquals(a.chars.length, 1); a.append('b'); assertEquals(a.toString(), "ab"); assertEquals(a.chars.length, 4); a.append('c'); a.append('d'); a.append('e'); assertEquals(a.toString(), "abcde"); assertEquals(a.chars.length, 10); } @Test public void testFill() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(2, null, -1); a.fill('*', 2); assertEquals(a.toString(), "**"); assertEquals(a.chars.length, 2); a.fill('*', 4); assertEquals(a.toString(), "******"); assertEquals(a.chars.length, 6); a.fill('*', 1); assertEquals(a.toString(), "*******"); assertEquals(a.chars.length, 14); } @Test public void testPrepend() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(2, null, -1); a.prepend('a'); assertEquals(a.toString(), "a"); assertEquals(a.chars.length, 2); a.prepend('b'); assertEquals(a.toString(), "ba"); assertEquals(a.chars.length, 2); a.prepend('c'); assertEquals(a.toString(), "cba"); assertEquals(a.chars.length, 4); a.prepend("12345678890".toCharArray()); assertEquals(a.toString(), "12345678890cba"); } @Test public void testAppend1() throws Exception { ExpandingCharAppender a = new ExpandingCharAppender(2, null, -1); ExpandingCharAppender b = new ExpandingCharAppender(2, null, -1); a.append(b); assertEquals(a.toString(), null); assertEquals(a.chars.length, 2); b.append('a'); b.append('b'); a.append(b); //whitespaceRangeStart gets reset here assertEquals(a.toString(), "ab"); assertEquals(a.chars.length, 2); a.append(b); //should make no difference assertEquals(a.toString(), "ab"); assertEquals(a.chars.length, 2); assertEquals(b.toString(), null); assertEquals(b.chars.length, 2); b.append('c'); b.append('d'); a.append(b); assertEquals(a.toString(), "abcd"); assertEquals(a.chars.length, 6); assertEquals(b.toString(), null); assertEquals(b.chars.length, 2); } @Test public void testAppendArray() { ExpandingCharAppender a = new ExpandingCharAppender(2, null, -1); a.append("abc".toCharArray(), 0, 3); assertEquals(a.toString(), "abc"); assertEquals(a.chars.length, 5); a.append("defghi".toCharArray(), 0, 3); assertEquals(a.toString(), "abcdef"); a.append("defghi".toCharArray(), 4, 2); assertEquals(a.toString(), "abcdefhi"); a.append("012345678901234567890123456789012345678901234567890123456789".toCharArray(), 0, 60); assertEquals(a.toString(), "abcdefhi012345678901234567890123456789012345678901234567890123456789"); } @Test public void testRemove(){ ExpandingCharAppender a = new ExpandingCharAppender(10, null, -1); a.append("0123456789"); a.remove(0, 0); assertEquals(a.toString(), "0123456789"); a.remove(10, 0); assertEquals(a.toString(), "0123456789"); a.remove(0, 1); assertEquals(a.toString(), "123456789"); a.remove(8, 1); assertEquals(a.toString(), "12345678"); a.remove(2, 3); assertEquals(a.toString(), "12678"); a.remove(1, 2); assertEquals(a.toString(), "178"); a.remove(1, 1); assertEquals(a.toString(), "18"); a.remove(1, 1); assertEquals(a.toString(), "1"); a.remove(0, 1); assertEquals(a.toString(), null); a.append("0123456789"); a.remove(1, 1); assertEquals(a.toString(), "023456789"); a.remove(1, 4); assertEquals(a.toString(), "06789"); a.reset(); a.append("0123456789"); a.remove(1, 7); assertEquals(a.toString(), "089"); a.remove(0, 3); assertEquals(a.toString(), null); } }LookaheadCharInputReaderTest.java000066400000000000000000000046761400120543400351170ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/input/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.input; import org.testng.annotations.*; import java.io.*; import static org.testng.Assert.*; public class LookaheadCharInputReaderTest { @Test public void testLookahead() { LookaheadCharInputReader reader = new LookaheadCharInputReader(new DefaultCharInputReader("\n\r".toCharArray(), '\n', 2, -1, true), '\n', -1); reader.start(new StringReader("abcdefgh")); assertEquals(reader.nextChar(), 'a'); reader.lookahead(1); reader.lookahead(1); assertTrue(reader.matches(new char[]{'b', 'c'}, '?')); assertTrue(reader.matches(new char[]{'b'}, '?')); assertFalse(reader.matches(new char[]{'c'}, '?')); assertFalse(reader.matches(new char[]{'a', 'b'}, '?')); assertFalse(reader.matches(new char[]{'c', 'd'}, '?')); assertEquals(reader.nextChar(), 'b'); assertFalse(reader.matches(new char[]{'b'}, '?')); assertTrue(reader.matches(new char[]{'c'}, '?')); assertEquals(reader.nextChar(), 'c'); assertFalse(reader.matches(new char[]{'c'}, '?')); assertFalse(reader.matches(new char[]{'d'}, '?')); assertEquals(reader.nextChar(), 'd'); assertEquals(reader.nextChar(), 'e'); reader.lookahead(5); assertTrue(reader.matches(new char[]{'f', 'g', 'h'}, '?')); assertTrue(reader.matches(new char[]{'f', 'g'}, '?')); assertTrue(reader.matches(new char[]{'f'}, '?')); assertEquals(reader.nextChar(), 'f'); assertEquals(reader.nextChar(), 'g'); assertTrue(reader.matches(new char[]{'h'}, '?')); assertEquals(reader.nextChar(), 'h'); assertFalse(reader.matches(new char[]{'f'}, '?')); try { char ch = reader.nextChar(); fail("Expected EOFException after end of the input. Got char: " + ch); } catch (EOFException ex) { //pass } } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/input/WriterCharAppenderTest.java000066400000000000000000000036221400120543400340650ustar00rootroot00000000000000/* * Copyright 2016 Univocity Software Pty Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.input; import com.univocity.parsers.csv.CsvWriter; import com.univocity.parsers.csv.CsvWriterSettings; import java.io.StringWriter; import static org.testng.Assert.assertEquals; import org.testng.annotations.Test; public class WriterCharAppenderTest { @Test public void testAppendAndExpandWhenAppendingChar() { CsvWriterSettings settings = new CsvWriterSettings(); settings.getFormat().setLineSeparator("\n"); settings.getFormat().setDelimiter('\t'); settings.setMaxCharsPerColumn(16); // note default max length before expanding settings.getFormat().setQuote('"'); settings.getFormat().setQuoteEscape('"'); settings.setIgnoreLeadingWhitespaces(false); settings.setIgnoreTrailingWhitespaces(false); settings.setQuoteAllFields(true); settings.setEmptyValue(""); StringWriter stringWriter = new StringWriter(); CsvWriter writer = new CsvWriter(stringWriter, settings); // test data's first column length is specific to repro bug occuring due to // appending quote character occuring at writer buffer boundary String[] testCase = {"abcdefghijklmno", "pqrst", "uvwxyz"}; String expectedString = "\"abcdefghijklmno\"\t\"pqrst\"\t\"uvwxyz\"\n"; writer.writeRow(testCase); writer.close(); assertEquals(stringWriter.toString(), expectedString); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/000077500000000000000000000000001400120543400275065ustar00rootroot00000000000000AnnotatedBeanProcessorTest.java000077500000000000000000000127231400120543400355450ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.annotations.*; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.lang.annotation.*; import java.math.*; import java.util.*; import static org.testng.Assert.*; public class AnnotatedBeanProcessorTest { String input = "date,amount,quantity,pending,comments,active\n" + "10-oct-2001,555.999,1,yEs,?,n\n" + "2001-10-10,,?,N,\" \"\" something \"\" \",true"; @Trim @UpperCase @BooleanString(falseStrings = "N", trueStrings = {"TRUE", "Y"}) @Parsed @Retention(RetentionPolicy.RUNTIME) @Inherited @Target(value = {ElementType.FIELD}) public @interface MetaBoolean { } public static class TestBean { @Parsed(defaultNullRead = "0") Integer quantity; @Trim(length = 8) @LowerCase @Parsed(index = 4) String commts; @Parsed(field = "amount") BigDecimal amnt; @Trim @LowerCase @BooleanString(falseStrings = {"no", "n", "null"}, trueStrings = {"yes", "y"}) @Parsed Boolean pending; @MetaBoolean Boolean active; @BooleanString(falseStrings = "0", trueStrings = "1") @Parsed Boolean other; } protected CsvParserSettings newCsvInputSettings() { return new CsvParserSettings(); } @Test public void testAnnotatedBeanProcessor() { BeanListProcessor processor = new BeanListProcessor(TestBean.class); processor.convertAll(Conversions.toNull("", "?")); CsvParserSettings settings = newCsvInputSettings(); settings.getFormat().setLineSeparator("\n"); settings.excludeIndexes(0); StringReader reader = new StringReader(input); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); CsvParser parser = new CsvParser(settings); parser.parse(reader); List beans = processor.getBeans(); assertEquals(beans.size(), 2); TestBean bean; bean = beans.get(0); assertEquals(bean.amnt, new BigDecimal("555.999")); assertEquals(bean.quantity, (Object) 1); assertTrue(bean.pending); assertNull(bean.commts); bean = beans.get(1); assertEquals(bean.amnt, null); assertEquals(bean.quantity, (Object) 0); assertFalse(bean.pending); assertEquals(bean.commts, "\" someth"); // trimmed to 8 characters } @Test public void testAnnotatedBeanWithLessColumns() { BeanListProcessor processor = new BeanListProcessor(TestBean.class); CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); settings.setProcessor(processor); StringReader reader = new StringReader("active,other\n,1\n,,\ny,0"); CsvParser parser = new CsvParser(settings); parser.parse(reader); List beans = processor.getBeans(); assertTrue(beans.get(0).other); assertNull(beans.get(1).other); assertFalse(beans.get(2).other); } @Test public void testAnnotatedBeanProcessorWithOneFieldOnly() { BeanListProcessor processor = new BeanListProcessor(TestBean.class); processor.convertAll(Conversions.toNull("", "?")); CsvParserSettings settings = newCsvInputSettings(); settings.getFormat().setLineSeparator("\n"); settings.setColumnReorderingEnabled(true); settings.selectIndexes(1); StringReader reader = new StringReader(input); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); CsvParser parser = new CsvParser(settings); parser.parse(reader); List beans = processor.getBeans(); assertEquals(beans.size(), 2); TestBean bean; bean = beans.get(0); assertEquals(bean.amnt, new BigDecimal("555.999")); assertEquals(bean.quantity, Integer.valueOf(0)); assertNull(bean.pending); assertNull(bean.commts); bean = beans.get(1); assertEquals(bean.amnt, null); assertEquals(bean.quantity, Integer.valueOf(0)); assertNull(bean.pending); assertNull(bean.commts); } public static class Data { @Parsed(index = 0) public String value; @Parsed(index = 1) public String foo; @Nested MetaData metaData; } public static class MetaData { @Parsed(index = 1) public String title; } @Test public void testRepeatedIndexInAnnotation() { BeanListProcessor rowProcessor = new BeanListProcessor(Data.class); CsvParserSettings settings = new CsvParserSettings(); settings.setProcessor(rowProcessor); settings.setLineSeparatorDetectionEnabled(true); CsvParser parser = new CsvParser(settings); parser.parseAll(new StringReader("a1,b1,c1\na2,b2,c2\na3,b3,c3")); List beans = rowProcessor.getBeans(); for (int i = 1; i <= beans.size(); i++) { Data bean = beans.get(i - 1); assertEquals(bean.value, "a" + i); assertEquals(bean.foo, "b" + i); assertEquals(bean.metaData.title, "b" + i); } } } BatchedColumnProcessorTest.java000066400000000000000000000051021400120543400355400ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.util.*; import static org.testng.Assert.*; public class BatchedColumnProcessorTest { private static final String INPUT = "" + "A,B,C" + "\n1A,1B,1C" + "\n2A,2B" + "\n3A,3B,3C" + "\n4A,4B,4C,4D"; @Test public void testColumnValues() { final String[][] expectedValueOnFirstBatch = new String[][]{ {"1A", "2A"}, {"1B", "2B"}, {"1C", null}, }; final String[][] expectedValueOnSecondBatch = new String[][]{ {"3A", "4A"}, {"3B", "4B"}, {"3C", "4C"}, {null, "4D"} }; BatchedColumnProcessor processor = new BatchedColumnProcessor(2) { @Override public void batchProcessed(int rowsInThisBatch) { List> columnValues = getColumnValuesAsList(); Map> columnsByIndex = getColumnValuesAsMapOfIndexes(); String[][] expectedValues = getBatchesProcessed() == 0 ? expectedValueOnFirstBatch : expectedValueOnSecondBatch; assertEquals(columnValues.size(), expectedValues.length); for (int i = 0; i < expectedValues.length; i++) { assertEquals(columnValues.get(i).size(), rowsInThisBatch); assertEquals(columnValues.get(i).toArray(), expectedValues[i]); assertEquals(columnsByIndex.get(i).toArray(), expectedValues[i]); } if (expectedValues.length == 4) { try { getColumnValuesAsMapOfNames(); fail("Expected exception. No name defined for 4th column"); } catch (Exception e) { //OK } } assertEquals(getHeaders(), new String[]{"A", "B", "C"}); } }; CsvParserSettings settings = new CsvParserSettings(); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); new CsvParser(settings).parse(new StringReader(INPUT)); } } BatchedObjectColumnProcessorTest.java000066400000000000000000000056151400120543400367000ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.math.*; import java.util.*; import static org.testng.Assert.*; public class BatchedObjectColumnProcessorTest { private static final String INPUT = "" + "A,B,C" + "\n1,true,C" + "\n2,false" + "\n3,,C" + "\n4,false,C,55.4"; @Test public void testColumnValues() { final Object[][] expectedValueOnFirstBatch = new Object[][]{ {1, 2}, {true, false}, {'C', null}, }; final Object[][] expectedValueOnSecondBatch = new Object[][]{ {3, 4}, {null, false}, {'C', 'C'}, {null, new BigDecimal("55.4")} }; BatchedObjectColumnProcessor processor = new BatchedObjectColumnProcessor(2) { @Override public void batchProcessed(int rowsInThisBatch) { List> columnValues = getColumnValuesAsList(); Map> columnsByIndex = getColumnValuesAsMapOfIndexes(); Object[][] expectedValues = getBatchesProcessed() == 0 ? expectedValueOnFirstBatch : expectedValueOnSecondBatch; assertEquals(columnValues.size(), expectedValues.length); for (int i = 0; i < expectedValues.length; i++) { assertEquals(columnValues.get(i).size(), rowsInThisBatch); assertEquals(columnValues.get(i).toArray(), expectedValues[i]); assertEquals(columnsByIndex.get(i).toArray(), expectedValues[i]); } if (expectedValues.length == 4) { try { getColumnValuesAsMapOfNames(); fail("Expected exception. No name defined for 4th column"); } catch (Exception e) { //OK } } assertEquals(getHeaders(), new String[]{"A", "B", "C"}); } }; processor.convertFields(Conversions.toInteger()).add("A"); processor.convertFields(Conversions.toBoolean()).add("B"); processor.convertFields(Conversions.toChar()).add("C"); processor.convertIndexes(Conversions.toBigDecimal()).add(3); CsvParserSettings settings = new CsvParserSettings(); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); new CsvParser(settings).parse(new StringReader(INPUT)); } } BeanWriterProcessorTest.java000066400000000000000000000060211400120543400350730ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.annotations.*; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.math.*; import static org.testng.Assert.*; public class BeanWriterProcessorTest { static class TestBean { @Parsed(defaultNullRead = "0") Integer quantity; @Trim @LowerCase @Parsed(index = 4) String commts; @Parsed(field = "amount") BigDecimal amnt; @Trim @LowerCase @BooleanString(falseStrings = {"no", "n", "null"}, trueStrings = {"yes", "y"}) @Parsed Boolean pending; } private final NormalizedString[] headers = NormalizedString.toArray("date,amount,quantity,pending,comments".split(",")); @Test public void testAnnotatedBeanProcessor() { BeanWriterProcessor processor = new BeanWriterProcessor(TestBean.class); processor.convertAll(Conversions.toNull("?")); processor.initialize(); Object[] row; TestBean bean1 = new TestBean(); bean1.amnt = new BigDecimal("555.999"); bean1.commts = null; bean1.pending = true; bean1.quantity = 1; row = processor.write(bean1, headers, null); assertEquals(row[0], "?"); // date not mapped in bean assertEquals(row[1], "555.999"); assertEquals(row[2], "1"); assertEquals(row[3], "yes"); assertEquals(row[4], "?"); TestBean bean2 = new TestBean(); bean2.amnt = null; bean2.quantity = 0; bean2.pending = false; bean2.commts = " something "; row = processor.write(bean2, headers, null); assertEquals(row[0], "?"); // date not mapped in bean assertEquals(row[1], "?"); assertEquals(row[2], "0"); assertEquals(row[3], "no"); assertEquals(row[4], "something"); // trimmed } @Test public void testRepeatedIndexInAnnotation() { BeanWriterProcessor rowProcessor = new BeanWriterProcessor(AnnotatedBeanProcessorTest.Data.class); CsvWriterSettings settings = new CsvWriterSettings(); settings.setRowWriterProcessor(rowProcessor); try { new CsvWriter(settings); fail("Expecting validation error on duplicate field"); } catch(Exception e){ assertTrue(e.getMessage().startsWith("Duplicate field index '1' found in attribute")); } } } ColumnProcessorTest.java000077500000000000000000000043631400120543400343000ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.util.*; import static org.testng.Assert.*; public class ColumnProcessorTest { private static final String INPUT = "" + "A,B,C" + "\n1A,1B,1C" + "\n2A,2B" + "\n3A,3B,3C" + "\n4A,4B,4C,4D"; @Test public void testColumnValues() { ColumnProcessor processor = new ColumnProcessor(); CsvParserSettings settings = new CsvParserSettings(); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); settings.getFormat().setLineSeparator("\n"); new CsvParser(settings).parse(new StringReader(INPUT)); String[][] expectedValues = new String[][]{ {"1A", "2A", "3A", "4A"}, {"1B", "2B", "3B", "4B"}, {"1C", null, "3C", "4C"}, {null, null, null, "4D"} }; List> columnValues = processor.getColumnValuesAsList(); Map> columnsByIndex = processor.getColumnValuesAsMapOfIndexes(); assertEquals(columnValues.size(), expectedValues.length); int i = 0; for (List column : columnValues) { assertEquals(column.toArray(new String[0]), expectedValues[i]); assertEquals(columnsByIndex.get(i).toArray(new String[0]), expectedValues[i]); i++; } try { processor.getColumnValuesAsMapOfNames(); fail("Expected exception. No name defined for 4th column"); } catch (Exception e) { //OK } assertEquals(processor.getHeaders(), new String[]{"A", "B", "C"}); } } ConcurrentRowProcessorTest.java000066400000000000000000000071021400120543400356440ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.util.*; import static org.testng.Assert.*; public class ConcurrentRowProcessorTest { private final int LINES = 5000; private String input; @BeforeClass public void init() throws Exception { StringBuilder bigInput = new StringBuilder("A,B,C,D,E,F,G\n"); for (int i = 0; i < LINES; i++) { bigInput.append("A").append(i); bigInput.append(",B").append(i); bigInput.append(",C").append(i); bigInput.append(",D").append(i); bigInput.append(",E").append(i); bigInput.append(",F").append(i); bigInput.append(",G").append(i); bigInput.append("\n"); } input = bigInput.toString(); } @DataProvider private Object[][] getLimits() { return new Object[][]{ {-1}, {0}, {1}, {2}, {5}, {10}, {100} }; } @Test(dataProvider = "getLimits") public void concurrentRowProcessorTest(int limit) throws Exception { ColumnProcessor processor = new ColumnProcessor(); CsvParserSettings settings = new CsvParserSettings(); settings.setLineSeparatorDetectionEnabled(true); settings.setColumnReorderingEnabled(true); Reader reader = new StringReader(input); settings.setHeaderExtractionEnabled(true); ConcurrentRowProcessor concurrentRowProcessor = new ConcurrentRowProcessor(processor, limit); settings.setProcessor(concurrentRowProcessor); CsvParser parser = new CsvParser(settings); //long start = System.currentTimeMillis(); parser.parse(reader); List> columnValues = processor.getColumnValuesAsList(); //System.out.println("Concurrently processed " + LINES + " lines in " + (System.currentTimeMillis() - start) + "ms with limit of " + limit); assertEquals(columnValues.size(), 7); for (int i = 0; i < 7; i++) { assertEquals(columnValues.get(i).size(), LINES); } } @Test public void ensureContextIsPreserved() throws Exception { CsvParserSettings settings = new CsvParserSettings(); settings.setLineSeparatorDetectionEnabled(true); settings.setColumnReorderingEnabled(true); Reader reader = new StringReader(input); settings.setHeaderExtractionEnabled(true); final StringBuilder out = new StringBuilder("A,B,C,D,E,F,G\n"); RowProcessor myProcessor = new AbstractRowProcessor(){ @Override public void rowProcessed(String[] row, ParsingContext context) { out.append(context.currentParsedContent()); } @Override public void processEnded(ParsingContext context) { assertEquals(out.toString(), input); } }; ConcurrentRowProcessor concurrent = new ConcurrentRowProcessor(myProcessor); concurrent.setContextCopyingEnabled(true); settings.setProcessor(concurrent); CsvParser parser = new CsvParser(settings); parser.parse(reader); } } MasterDetailProcessorTest.java000077500000000000000000000061731400120543400354220ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.math.*; import java.util.*; import static org.testng.Assert.*; public class MasterDetailProcessorTest { private String totalsOnTop = "type,amount\n" + "T,100\n" + "50\n" + "40\n" + "10\n" + "T,200\n" + "170\n" + "30"; private String totalsAtBottom = "type,amount\n" + "50\n" + "40\n" + "10\n" + "T,100\n" + "170\n" + "30\n" + "T,200"; @DataProvider(name = "inputsAndProcessors") private Object[][] getInputsAndProcessors() { return new Object[][]{ {totalsOnTop, getProcessor(true)}, {totalsAtBottom, getProcessor(false)}, }; } @Test(dataProvider = "inputsAndProcessors") public void testMasterDetail(String input, MasterDetailListProcessor processor) { StringReader reader = new StringReader(input); CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); CsvParser parser = new CsvParser(settings); parser.parse(reader); List records = processor.getRecords(); assertEquals(records.size(), 2); MasterDetailRecord record; record = records.get(0); assertEquals(sumItems(record), getTotal(record)); record = records.get(1); assertEquals(sumItems(record), getTotal(record)); } private Integer getTotal(MasterDetailRecord record) { Object[] masterRow = record.getMasterRow(); return ((BigInteger) masterRow[1]).intValue(); } private Integer sumItems(MasterDetailRecord record) { List rows = record.getDetailRows(); Integer out = 0; for (Object[] row : rows) { out += (Integer) row[0]; } return out; } private MasterDetailListProcessor getProcessor(boolean totalsOnTop) { final ObjectRowListProcessor items = new ObjectRowListProcessor(); MasterDetailListProcessor totals = new MasterDetailListProcessor(totalsOnTop ? RowPlacement.TOP : RowPlacement.BOTTOM, items) { @Override protected boolean isMasterRecord(String[] row, ParsingContext context) { return "T".equals(row[0]); } }; totals.convertIndexes(Conversions.toBigInteger()).set(1); items.convertIndexes(Conversions.toInteger()).set(0); return totals; } } MultiBeanListProcessorTest.java000077500000000000000000000102541400120543400355530ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/* * Copyright (c) 2015. Univocity Software Pty Ltd *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.univocity.parsers.common.processor; import com.univocity.parsers.annotations.*; import com.univocity.parsers.common.*; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.math.*; import java.util.*; import static org.testng.Assert.*; public class MultiBeanListProcessorTest extends AnnotatedBeanProcessorTest{ public static class AmountBean { @Trim @UpperCase @Parsed(index = 4) String commts; @Parsed(field = "amount") BigDecimal amnt; } public static class QuantityBean { @Parsed(defaultNullRead = "-1") Integer quantity; @Trim @LowerCase @BooleanString(falseStrings = {"no", "n", "null"}, trueStrings = {"yes", "y"}) @Parsed Boolean pending; } public static class BrokenBean { @Parsed(index = 4) String commts; @Parsed int quantity; public int getQuantity() { return this.quantity; } public void setQuantity(int quantity) { if(quantity == 0) { throw new NullPointerException("throwing error on purpose"); } this.quantity = quantity; } } @Test public void testMultiBeanProcessor() { MultiBeanListProcessor processor = new MultiBeanListProcessor(TestBean.class, AmountBean.class, QuantityBean.class, BrokenBean.class); processor.convertAll(Conversions.toNull("", "?")); CsvParserSettings settings = newCsvInputSettings(); settings.setRowProcessorErrorHandler(new RowProcessorErrorHandler() { @Override public void handleError(DataProcessingException error, Object[] inputRow, ParsingContext context) { assertEquals(context.currentRecord(), 2L); } }); settings.excludeIndexes(0); StringReader reader = new StringReader(input); settings.setHeaderExtractionEnabled(true); settings.getFormat().setLineSeparator("\n"); settings.setRowProcessor(processor); CsvParser parser = new CsvParser(settings); parser.parse(reader); List testBeans = processor.getBeans(TestBean.class); List amountBeans = processor.getBeans(AmountBean.class); List quantityBeans = processor.getBeans(QuantityBean.class); List brokenBeans = processor.getBeans(BrokenBean.class); assertEquals(testBeans.size(), 2); assertEquals(amountBeans.size(), 2); assertEquals(quantityBeans.size(), 2); assertEquals(brokenBeans.size(), 2); TestBean testBean; AmountBean amountBean; QuantityBean quantityBean; BrokenBean brokenBean; testBean = testBeans.get(0); amountBean = amountBeans.get(0); quantityBean = quantityBeans.get(0); brokenBean = brokenBeans.get(0); assertEquals(testBean.amnt, new BigDecimal("555.999")); assertNull(testBean.commts); assertEquals(testBean.quantity, (Object) 1); assertTrue(testBean.pending); assertEquals(brokenBean.quantity, 1); assertNull(brokenBean.commts); assertEquals(amountBean.amnt, new BigDecimal("555.999")); assertNull(amountBean.commts); assertEquals(quantityBean.quantity, (Object) 1); assertTrue(quantityBean.pending); testBean = testBeans.get(1); amountBean = amountBeans.get(1); quantityBean = quantityBeans.get(1); assertNull(brokenBeans.get(1)); //Second row generated a NullPointerException and no bean is generated here assertEquals(testBean.amnt, null); assertEquals(testBean.quantity, (Object) 0); assertFalse(testBean.pending); assertEquals(testBean.commts, "\" someth"); assertEquals(amountBean.amnt, null); assertEquals(amountBean.commts, "\" SOMETHING \""); //upper cased assertEquals(quantityBean.quantity, Integer.valueOf(-1)); assertFalse(quantityBean.pending); } } ObjectColumnProcessorTest.java000077500000000000000000000050431400120543400354230ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.math.*; import java.util.*; import static org.testng.Assert.*; public class ObjectColumnProcessorTest { private static final String INPUT = "" + "A,B,C" + "\n1,true,C" + "\n2,false" + "\n3,,C" + "\n4,false,C,55.4"; @Test public void testColumnValues() { ObjectColumnProcessor processor = new ObjectColumnProcessor(); processor.convertFields(Conversions.toInteger()).add("A"); processor.convertFields(Conversions.toBoolean()).add("B"); processor.convertFields(Conversions.toChar()).add("C"); processor.convertIndexes(Conversions.toBigDecimal()).add(3); CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); new CsvParser(settings).parse(new StringReader(INPUT)); Object[][] expectedValues = new Object[][]{ {1, 2, 3, 4}, {true, false, null, false}, {'C', null, 'C', 'C'}, {null, null, null, new BigDecimal("55.4")} }; List> columnValues = processor.getColumnValuesAsList(); Map> columnsByIndex = processor.getColumnValuesAsMapOfIndexes(); assertEquals(columnValues.size(), expectedValues.length); int i = 0; for (List column : columnValues) { assertEquals(column.toArray(), expectedValues[i]); assertEquals(columnsByIndex.get(i).toArray(), expectedValues[i]); i++; } try { processor.getColumnValuesAsMapOfNames(); fail("Expected exception. No name defined for 4th column"); } catch (Exception e) { //OK } assertEquals(processor.getHeaders(), new String[]{"A", "B", "C"}); } } ObjectRowListProcessorTest.java000077500000000000000000000127311400120543400355730ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import org.testng.annotations.*; import java.io.*; import java.math.*; import java.util.*; import static com.univocity.parsers.conversions.Conversions.*; import static org.testng.Assert.*; public class ObjectRowListProcessorTest { protected CsvParserSettings newCsvInputSettings() { CsvParserSettings out = new CsvParserSettings(); out.getFormat().setLineSeparator("\n"); return out; } private String[] valuesForTrue = new String[]{"yes", "y"}; private String[] valuesForFalse = new String[]{"no", "n", null}; private String input = "date,amount,quantity,pending,comments\n" + "10-oct-2001,555.999,1,yEs,?\n" + "2001-10-10,,?,N,\" \"\" something \"\" \""; private List process(String input, ObjectRowListProcessor processor, CsvParserSettings settings) { StringReader reader = new StringReader(input); settings.setHeaderExtractionEnabled(true); settings.setRowProcessor(processor); CsvParser parser = new CsvParser(settings); parser.parse(reader); List rows = processor.getRows(); return rows; } private ObjectRowListProcessor newProcessorWithFieldNames() { ObjectRowListProcessor processor = new ObjectRowListProcessor(); Conversion toNull = toNull("", "?"); processor.convertFields(toNull).set("quantity", "amount"); processor.convertFields(toCalendar(Locale.ENGLISH,"dd-MMM-yyyy", "yyyy-MM-dd")).set("date"); processor.convertFields(toBigDecimal()).set("amount"); processor.convertFields(toInteger()).set("quantity"); processor.convertFields(toLowerCase(), toBoolean(valuesForTrue, valuesForFalse)).set("pending"); processor.convertFields(trim(), toNull).set("comments"); return processor; } private ObjectRowListProcessor newProcessorWithFieldIndexes() { ObjectRowListProcessor processor = new ObjectRowListProcessor(); Conversion toNull = toNull("", "?"); processor.convertIndexes(toNull).set(1, 2); processor.convertIndexes(toCalendar(Locale.ENGLISH, "dd-MMM-yyyy", "yyyy-MM-dd")).set(0); processor.convertIndexes(toBigDecimal()).set(1); processor.convertIndexes(toInteger()).set(2); processor.convertIndexes(toLowerCase(), toBoolean(valuesForTrue, valuesForFalse)).set(3); processor.convertIndexes(trim(), toNull).set(4); return processor; } @DataProvider(name = "processors") Object[][] getProcessors() { return new Object[][]{ {newProcessorWithFieldNames()}, {newProcessorWithFieldIndexes()} }; } @Test(dataProvider = "processors") public void conversionTest(ObjectRowListProcessor processor) { process(input, processor, newCsvInputSettings()); List rows = processor.getRows(); assertEquals(rows.size(), 2); Calendar date = new GregorianCalendar(2001, Calendar.OCTOBER, 10); Object[] row = rows.get(0); assertEquals(row[0], date); assertEquals(row[1], new BigDecimal("555.999")); assertEquals(row[2], 1); assertEquals(row[3], true); assertNull(row[4]); row = rows.get(1); assertEquals(row[0], date); assertEquals(row[1], null); assertEquals(row[2], null); assertEquals(row[3], false); assertEquals(row[4], "\" something \""); // trimmed } @Test(dataProvider = "processors") public void conversionTestOnSelectedColumnsWithReordering(ObjectRowListProcessor processor) { CsvParserSettings settings = newCsvInputSettings(); settings.setColumnReorderingEnabled(true); settings.selectIndexes(1, 0, 3); settings.getFormat().setLineSeparator("\n"); process(input, processor, settings); List rows = processor.getRows(); assertEquals(rows.size(), 2); Calendar date = new GregorianCalendar(2001, Calendar.OCTOBER, 10); Object[] row = rows.get(0); assertEquals(row[0], new BigDecimal("555.999")); assertEquals(row[1], date); assertEquals(row[2], true); row = rows.get(1); assertEquals(row[0], null); assertEquals(row[1], date); assertEquals(row[2], false); } @Test(dataProvider = "processors") public void conversionTestOnSelectedColumnsWithoutColumnReordering(ObjectRowListProcessor processor) { CsvParserSettings settings = newCsvInputSettings(); settings.selectIndexes(1, 0, 3); settings.setColumnReorderingEnabled(false); process(input, processor, settings); List rows = processor.getRows(); assertEquals(rows.size(), 2); Calendar date = new GregorianCalendar(2001, Calendar.OCTOBER, 10); Object[] row = rows.get(0); assertEquals(row[0], date); assertEquals(row[1], new BigDecimal("555.999")); assertNull(row[2]); assertEquals(row[3], true); assertNull(row[4]); row = rows.get(1); assertEquals(row[0], date); assertEquals(row[1], null); assertNull(row[2]); assertEquals(row[3], false); assertNull(row[2]); } } ObjectRowWriterProcessorTest.java000066400000000000000000000102661400120543400361320ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/processor/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.processor; import com.univocity.parsers.conversions.*; import org.testng.annotations.*; import java.math.*; import java.text.*; import java.util.*; import static com.univocity.parsers.conversions.Conversions.*; import static org.testng.Assert.*; public class ObjectRowWriterProcessorTest { private final SimpleDateFormat format = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); private final String[] headers = "date,amount,quantity,pending,comments".split(","); private final Object[][] values; { try { values = new Object[][]{ {format.parse("10-oct-2001"), new BigDecimal("555.999"), 1, true, null}, {format.parse("11-oct-2001"), null, null, false, " something "} }; } catch (ParseException e) { throw new IllegalStateException(e); } } private ObjectRowWriterProcessor newProcessorWithFieldNames() { ObjectRowWriterProcessor processor = new ObjectRowWriterProcessor(); Conversion toNull = toNull("?"); processor.convertFields(toNull).set("quantity", "amount"); processor.convertFields(toDate("dd-MM-yyyy")).set("date"); processor.convertFields(toBigDecimal()).set("amount"); processor.convertFields(toInteger()).set("quantity"); processor.convertFields(toBoolean("y", "n")).set("pending"); processor.convertFields(trim(), toNull).set("comments"); return processor; } private ObjectRowWriterProcessor newProcessorWithFieldIndexes() { ObjectRowWriterProcessor processor = new ObjectRowWriterProcessor(); Conversion toNull = toNull("?"); processor.convertIndexes(toNull).set(1, 2); processor.convertIndexes(toDate("dd-MM-yyyy")).set(0); processor.convertIndexes(toBigDecimal()).set(1); processor.convertIndexes(toInteger()).set(2); processor.convertIndexes(toBoolean("y", "n")).set(3); processor.convertIndexes(trim(), toNull).set(4); return processor; } @DataProvider(name = "processors") Object[][] getProcessors() { return new Object[][]{ {newProcessorWithFieldNames()}, {newProcessorWithFieldIndexes()} }; } @Test(dataProvider = "processors") public void conversionTest(ObjectRowWriterProcessor processor) { Object[] row; row = processor.write(values[0], headers, null); assertEquals(row[0], "10-10-2001"); assertEquals(row[1], "555.999"); assertEquals(row[2], "1"); assertEquals(row[3], "y"); assertEquals(row[4], "?"); row = processor.write(values[1], headers, null); assertEquals(row[0], "11-10-2001"); assertEquals(row[1], "?"); assertEquals(row[2], "?"); assertEquals(row[3], "n"); assertEquals(row[4], "something"); // trimmed } @Test public void testTypeConversion() { ObjectRowWriterProcessor processor = new ObjectRowWriterProcessor(); processor.convertType(Boolean.class, Conversions.string(), Conversions.toUpperCase()); processor.convertType(String.class, Conversions.toUpperCase(), Conversions.trim()); processor.convertType(Date.class, Conversions.toDate(Locale.ENGLISH,"yyyy-MMM-dd"), Conversions.toUpperCase()); Object[] row; row = processor.write(values[0], headers, null); assertEquals(row[0], "2001-OCT-10"); assertEquals(row[1], new BigDecimal("555.999")); assertEquals(row[2], 1); assertEquals(row[3], "TRUE"); assertEquals(row[4], null); row = processor.write(values[1], headers, null); assertEquals(row[0], "2001-OCT-11"); assertEquals(row[1], null); assertEquals(row[2], null); assertEquals(row[3], "FALSE"); assertEquals(row[4], "SOMETHING"); // trimmed } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/record/000077500000000000000000000000001400120543400267455ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/record/RecordImplTest.java000066400000000000000000000207151400120543400325150ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.record; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import com.univocity.parsers.tsv.*; import org.testng.annotations.*; import java.io.*; import java.math.*; import java.util.*; import static org.testng.Assert.*; public class RecordImplTest { private Record record; @BeforeClass public void setup() { TsvParserSettings settings = new TsvParserSettings(); settings.setHeaders("boolean,byte,short,int,long,bigint,float,double,bigdec,char,string,date,calendar".split(",")); TsvParser parser = new TsvParser(settings); record = parser.parseRecord("Y 1 2 4 5 6.6 7.7 $8.888 B blah 10/10/10 11/11/11"); assertNotNull(record); RecordMetaData md = record.getMetaData(); assertNotNull(md); md.setTypeOfColumns(BigInteger.class, E.bigint); md.convertFields(E.class, new DateConversion("dd/MM/yy")).add(E.date); md.setTypeOfColumns(Short.class, 2); md.setTypeOfColumns(float.class, 6); md.convertFields(new CalendarConversion("dd/MM/yy")).add("calendar"); } enum E { bigint, bigdec, date, calendar } @Test public void fillEnumMap() { EnumMap map = new EnumMap(E.class); record.fillEnumMap(map, E.date, E.bigint); assertEquals(map.size(), 2); assertEquals(map.get(E.date), "10/10/10"); assertEquals(map.get(E.bigint), "5"); } @Test public void fillEnumObjectMap() { EnumMap map = new EnumMap(E.class); record.fillEnumObjectMap(map, E.date, E.bigint); assertEquals(map.size(), 2); assertEquals((Date) map.get(E.date), new GregorianCalendar(2010, Calendar.OCTOBER, 10).getTime()); assertEquals((BigInteger) map.get(E.bigint), new BigInteger("5")); } @Test public void fillFieldMap() { Map map = new TreeMap(); record.fillFieldMap(map, "short", "float"); assertEquals(map.size(), 2); assertEquals(map.get("short"), "2"); assertEquals(map.get("float"), "6.6"); } @Test public void fillFieldObjectMap() { Map map = new TreeMap(); record.fillFieldObjectMap(map, "short", "float"); assertEquals(map.size(), 2); assertEquals((Short) map.get("short"), Short.valueOf((short) 2)); assertEquals((Float) map.get("float"), (float) 6.6); } @Test public void fillIndexMap() { Map map = new TreeMap(); record.fillIndexMap(map, 2, 6); assertEquals(map.size(), 2); assertEquals(map.get(2), "2"); assertEquals(map.get(6), "6.6"); } @Test public void fillIndexObjectMap() { Map map = new TreeMap(); record.fillIndexObjectMap(map, 2, 6); assertEquals(map.size(), 2); assertEquals((Short) map.get(2), Short.valueOf((short) 2)); assertEquals((Float) map.get(6), (float) 6.6); } @Test public void getBigDecimalStringStringString() { BigDecimal dec = record.getBigDecimal("bigdec", "$#0.00", "decimalSeparator=."); assertEquals(dec, new BigDecimal("8.888")); } @Test public void getBigDecimalEnumStringString() { BigDecimal dec = record.getBigDecimal(E.bigdec, "$#0.00", "decimalSeparator=."); assertEquals(dec, new BigDecimal("8.888")); } @Test public void getBigDecimalintStringString() { BigDecimal dec = record.getBigDecimal(8, "$#0.00", "decimalSeparator=."); assertEquals(dec, new BigDecimal("8.888")); } @Test public void getBigIntegerStringStringString() { BigInteger i = record.getBigInteger("bigint"); assertEquals(i, new BigInteger("5")); } @Test public void getBigIntegerEnumStringString() { BigInteger i = record.getBigInteger(E.bigint); assertEquals(i, new BigInteger("5")); } @Test public void getBigIntegerintStringString() { BigInteger i = record.getBigInteger(5); assertEquals(i, new BigInteger("5")); } @Test public void getBoolean() { assertTrue(record.getBoolean("boolean", "Y", "N")); assertFalse(record.getBoolean("boolean", "N", "Y")); } @Test public void getByte() { assertEquals((byte) record.getByte("byte"), (byte) 1); } @Test public void getCalendar() { Calendar cal = record.getCalendar(12, "hh/mm/ss");//ha! assertEquals(cal.get(Calendar.HOUR), 11); assertEquals(cal.get(Calendar.MINUTE), 11); assertEquals(cal.get(Calendar.SECOND), 11); cal = record.getCalendar("calendar");//format defined in metaData's column type - see @BeforeClass method. assertEquals(cal, new GregorianCalendar(2011, Calendar.NOVEMBER, 11)); } @Test public void getChar() { assertEquals(record.getChar("char"), Character.valueOf('B')); } @Test public void getDate() { Date dt = record.getDate("date", "hh/mm/ss");//ha! Calendar cal = Calendar.getInstance(); cal.setTime(dt); assertEquals(cal.get(Calendar.HOUR), 10); assertEquals(cal.get(Calendar.MINUTE), 10); assertEquals(cal.get(Calendar.SECOND), 10); dt = record.getDate(11);//format defined in metaData's column type - see @BeforeClass method. cal.setTime(dt); assertEquals(cal, new GregorianCalendar(2010, Calendar.OCTOBER, 10)); } @Test public void getDouble() { assertEquals(record.getDouble("double"), Double.valueOf(7.7)); } @Test public void getFloat() { assertEquals(record.getFloat("float"), Float.valueOf(6.6f)); } @Test public void getInt() { assertEquals(record.getInt("int"), null); } @Test public void getLong() { assertEquals(record.getLong("long"), Long.valueOf(4L)); } @Test public void getString() { assertEquals(record.getString("string"), "blah"); } @Test public void getValue() { assertEquals(record.getValue(E.bigdec, String.class), "$8.888"); assertEquals(record.getValue("int", Integer.class), null); assertEquals(record.getValue("int", Long.class), null); assertEquals(record.getValue("int", Long.valueOf(0L)), Long.valueOf(0L)); assertEquals(record.getValue("int", Integer.valueOf(100)), Integer.valueOf(100)); } @Test public void getValues() { assertEquals(record.getValues(E.bigdec, E.date), new String[]{"$8.888", "10/10/10"}); assertEquals(record.getValues("bigdec", "date"), new String[]{"$8.888", "10/10/10"}); assertEquals(record.getValues(8, 11), new String[]{"$8.888", "10/10/10"}); } @Test public void testUnivocityNull() { String data = "name,value\n'a',1\n'b',null\n"; CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setQuote('\''); settings.setHeaderExtractionEnabled(true); CsvParser parser = new CsvParser(settings); parser.beginParsing(new StringReader(data)); parser.getRecordMetadata().convertFields(Conversions.toNull("null")).set("value"); List records = parser.parseAllRecords(); assertEquals(records.get(0).getString("name"), "a"); assertEquals(records.get(0).getInt("value"), new Integer(1)); assertEquals(records.get(1).getString("name"), "b"); assertNull(records.get(1).getString("value")); } @Test public void testReuseAndHeaders() { String data1 = "name1,value1\n'a',1\n'b',null\n"; String data2 = "name2,value2\n'c',2\n'd',e\n"; CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setQuote('\''); settings.setHeaderExtractionEnabled(true); CsvParser parser = new CsvParser(settings); Record r1 = parseAndReturnRecord(parser, data1); assertEquals(Arrays.toString(r1.getMetaData().headers()), "[name1, value1]"); Record r2 = parseAndReturnRecord(parser, data2); assertEquals(Arrays.toString(r2.getMetaData().headers()), "[name2, value2]"); assertEquals(r1.getMetaData().indexOf("value1"), 1); assertEquals(Arrays.toString(r1.getMetaData().headers()), "[name1, value1]"); } private Record parseAndReturnRecord(CsvParser parser, String data) { parser.beginParsing(new StringReader(data)); parser.getRecordMetadata().convertFields(Conversions.toNull("null")).set("value"); List records = parser.parseAllRecords(); return records.get(0); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/routine/000077500000000000000000000000001400120543400271545ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/common/routine/AbstractRoutinesTest.java000066400000000000000000000214241400120543400341560ustar00rootroot00000000000000/******************************************************************************* * Copyright 2016 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.common.routine; import com.univocity.parsers.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.conversions.*; import com.univocity.parsers.csv.*; import com.univocity.parsers.examples.*; import com.univocity.parsers.fixed.*; import com.univocity.parsers.tsv.*; import org.testng.annotations.*; import java.io.*; import java.sql.*; import java.util.*; import static org.testng.Assert.*; public class AbstractRoutinesTest { private CsvParserSettings getParserSettings() { CsvParserSettings out = new CsvParserSettings(); out.getFormat().setLineSeparator("\n"); return out; } private CsvWriterSettings getWriterSettings() { CsvWriterSettings out = new CsvWriterSettings(); out.getFormat().setLineSeparator("\n"); return out; } static class ResultSetTest { AbstractRoutines routineImpl; String result; ResultSetTest(AbstractRoutines routineImpl) { this.routineImpl = routineImpl; } void run(ResultSet rs) throws Exception { StringWriter output = new StringWriter(); routineImpl.setKeepResourcesOpen(true); routineImpl.write(rs, output); output.write("A random line"); assertFalse(rs.isClosed()); output.close(); result = output.toString(); rs.close(); } } @Test public void testWriteResultSet() throws Exception { ObjectRowWriterProcessor processor = new ObjectRowWriterProcessor(); processor.convertType(java.sql.Timestamp.class, Conversions.toDate("dd/MM/yyyy HH:mm")); CsvRoutines csvRoutine = new CsvRoutines(); csvRoutine.setWriterSettings(getWriterSettings()); csvRoutine.getWriterSettings().setRowWriterProcessor(processor); ResultSetTest csvTest = new ResultSetTest(csvRoutine); TsvRoutines tsvRoutine = new TsvRoutines(); tsvRoutine.setWriterSettings(new TsvWriterSettings()); tsvRoutine.getWriterSettings().getFormat().setLineSeparator("\n"); tsvRoutine.getWriterSettings().setRowWriterProcessor(processor); ResultSetTest tsvTest = new ResultSetTest(tsvRoutine); FixedWidthRoutines fixedWidthRoutine = new FixedWidthRoutines(); fixedWidthRoutine.setWriterSettings(new FixedWidthWriterSettings()); fixedWidthRoutine.getWriterSettings().getFormat().setLineSeparator("\n"); fixedWidthRoutine.getWriterSettings().getFormat().setPadding('.'); fixedWidthRoutine.getWriterSettings().setRowWriterProcessor(processor); ResultSetTest fixedWidthTest = new ResultSetTest(fixedWidthRoutine); testWriteResultSet(csvTest, tsvTest, fixedWidthTest); String randomLine = "A random line"; String expectedCsv = "" + "1234,Description 1,02/12/2015 10:35\n" + "2345,Description 2,25/11/2016 11:05\n" + "39,Description 3,31/05/2017 09:24\n"; String expectedTsv = "" + "1234\tDescription 1\t02/12/2015 10:35\n" + "2345\tDescription 2\t25/11/2016 11:05\n" + "39\tDescription 3\t31/05/2017 09:24\n"; String expectedFixedWidth = "" + "1234Description 1...................02/12/2015 10:35..................\n" + "2345Description 2...................25/11/2016 11:05..................\n" + "39..Description 3...................31/05/2017 09:24..................\n"; assertEquals(csvTest.result, expectedCsv + randomLine); assertEquals(tsvTest.result, expectedTsv + randomLine); assertEquals(fixedWidthTest.result, expectedFixedWidth + randomLine); } private void testWriteResultSet(ResultSetTest... tests) throws Exception { String createTable = "CREATE TABLE test(" + " id char(4) primary key," + " desc varchar(32) not null," + " some_date datetime not null" + ")"; Class.forName("org.hsqldb.jdbcDriver"); Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:run"); try { Statement statement = connection.createStatement(); try { try { statement.execute(createTable); statement.executeUpdate("INSERT INTO test (id, desc, some_date) VALUES ('1234', 'Description 1', '2015-12-02 10:35:12')"); statement.executeUpdate("INSERT INTO test (id, desc, some_date) VALUES ('2345', 'Description 2', '2016-11-25 11:05:32')"); statement.executeUpdate("INSERT INTO test (id, desc, some_date) VALUES ('39' , 'Description 3', '2017-05-31 09:24:45')"); } catch(Exception ex){ //ignore. Table exists already. } for (ResultSetTest test : tests) { ResultSet rs = statement.executeQuery("SELECT id, desc, some_date FROM test ORDER BY id"); try { test.run(rs); } finally { rs.close(); } } } finally { statement.close(); } } finally { connection.close(); } } @Test public void testParseAndWrite() throws Exception { CsvRoutines csvRoutines = new CsvRoutines(); CsvParserSettings parser = getParserSettings(); parser.setNumberOfRowsToSkip(2); parser.setHeaderExtractionEnabled(true); parser.selectFields("Description"); csvRoutines.setParserSettings(parser); CsvWriterSettings writer = new CsvWriterSettings(); writer.getFormat().setDelimiter('|'); writer.getFormat().setLineSeparator("\r\n"); writer.getFormat().setQuoteEscape('$'); writer.getFormat().setQuote('$'); writer.setSkipEmptyLines(true); csvRoutines.setWriterSettings(writer); StringWriter output = new StringWriter(); csvRoutines.parseAndWrite(ParserTestCase.newReader("/csv/essential.csv"), output); String expected = "ac, abs, moon\r\n" + "$MUST SELL!\r\n" + "air, moon roof, loaded$\r\n" + "ac, abs, moon\r\n" + "ac, abs, moon\r\n" + "ac, abs, moon\r\n" + "ac, abs, moon\r\n" + "\" ac, abs, moon \"\r\n" + "\" ac, abs, moon \"\r\n"; assertEquals(output.toString(), expected); } @Test public void testParseAllJavaBeans() throws Exception { List beans = new CsvRoutines(getParserSettings()).parseAll(TestBean.class, CsvParserTest.newReader("/examples/bean_test.csv")); assertNotNull(beans); assertFalse(beans.isEmpty()); } @Test public void testWriteAllJavaBeans() throws Exception { List beans = new CsvRoutines(getParserSettings()).parseAll(TestBean.class, CsvParserTest.newReader("/examples/bean_test.csv")); StringWriter output = new StringWriter(); CsvWriterSettings settings = getWriterSettings(); new CsvRoutines(settings).writeAll(beans, TestBean.class, output); assertEquals(output.toString(), "1,555.999,yes,,?\n0,,no,,\"\"\" something \"\"\"\n"); output = new StringWriter(); new CsvRoutines(settings).writeAll(beans, TestBean.class, output, "pending", "amount"); assertEquals(output.toString(), "pending,amount\nyes,555.999\nno,\n"); } @Test public void testIterateJavaBeans() throws Exception { List beans = new ArrayList(); for (TestBean bean : new CsvRoutines(getParserSettings()).iterate(TestBean.class, CsvParserTest.newReader("/examples/bean_test.csv"))) { beans.add(bean); } assertEquals(beans.size(), 2); assertEquals(beans.get(0).getQuantity(), Integer.valueOf(1)); assertEquals(beans.get(1).getComments(), "\" something \""); } @Test public void testResourcesOpenFlagWithOutputStream() throws Exception { final File tmp = File.createTempFile("test", ".csv"); testWriteResultSet(new ResultSetTest(null) { public void run(ResultSet rs) { CsvWriterSettings settings = new CsvWriterSettings(); CsvRoutines csvRoutines = new CsvRoutines(settings); csvRoutines.setKeepResourcesOpen(true); FileOutputStream fout = null; try { try { fout = new FileOutputStream(tmp); csvRoutines.write(rs, fout); } finally { fout.close(); } } catch (Exception e) { throw new IllegalStateException(e); } } }); List rows = new CsvParser(new CsvParserSettings()).parseAll(tmp); assertEquals(rows.size(), 3); } @Test public void testInputDimensionRoutine() { CsvParserSettings csvParserSettings = new CsvParserSettings(); csvParserSettings.getFormat().setLineSeparator("\n"); csvParserSettings.setHeaderExtractionEnabled(true); CsvRoutines csvRoutines = new CsvRoutines(csvParserSettings); InputDimension d = csvRoutines.getInputDimension(new StringReader("a\nb\nc\n")); assertEquals(d.rowCount(), 3L); assertEquals(d.columnCount(), 1); } }univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/csv/000077500000000000000000000000001400120543400247725ustar00rootroot00000000000000univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/csv/CsvFormatDetectorTest.java000077500000000000000000000123221400120543400320760ustar00rootroot00000000000000/******************************************************************************* * Copyright 2015 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import org.testng.annotations.*; import java.io.*; import java.util.*; import static org.testng.Assert.*; public class CsvFormatDetectorTest { @DataProvider public Object[][] getInputsAndOutputs() { return new Object[][]{ {"A,B,C\n1,2,3\n1,2,3\n1,2,3", Arrays.asList(new String[]{"A", "B", "C"}, new String[]{"1", "2", "3"}, new String[]{"1", "2", "3"}, new String[]{"1", "2", "3"})}, {"\"A\";'B';\"C\"\n\"1\\\" and \\\"2\";\"3\\\"A\";'B';\"C\"\n\"A\";'B';\"C\"\n\"A\";'B';\"C\"\n", Arrays.asList(new String[]{"A", "'B'", "C"}, new String[]{"1\" and \"2", "3\"A", "'B'", "C"}, new String[]{"A", "'B'", "C"}, new String[]{"A", "'B'", "C"})}, {"\"A\";'B';\"C\"\n\"1\\\" and \\\"2\";\"3' and '4\";\"5\\\" and \\\"6\"\n\"A\";'B';\"C\"\n\"A\";'B';\"C\"\n", Arrays.asList(new String[]{"A", "'B'", "C"}, new String[]{"1\" and \"2", "3' and '4", "5\" and \"6"}, new String[]{"A", "'B'", "C"}, new String[]{"A", "'B'", "C"})}, {"1,2;2,3;3,4;a\n1,2;2,3;3,4;b\n1,2;2,3;3,4;c\n1,2;2,3;3,4;d\n", Arrays.asList(new String[]{"1,2", "2,3", "3,4", "a"}, new String[]{"1,2", "2,3", "3,4", "b"}, new String[]{"1,2", "2,3", "3,4", "c"}, new String[]{"1,2", "2,3", "3,4", "d"})}, {"A;B;C;D;E\n$1.2;$2.3;$3.4\n$1.2;$2.3;$3.4\n$1.2;$2.3;$3.4\n$1.2;$2.3;$3.4\n", Arrays.asList(new String[]{"A", "B", "C", "D", "E"}, new String[]{"$1.2", "$2.3", "$3.4"}, new String[]{"$1.2", "$2.3", "$3.4"}, new String[]{"$1.2", "$2.3", "$3.4"}, new String[]{"$1.2", "$2.3", "$3.4"})}, {"\"A'A\",\"BB\",\"CC\"\n\"11\",\"22\",\"33\"\n\"11\",\"22\",\"33\"\n\"11\",\"22\",\"33\"\n", Arrays.asList(new String[]{"A'A", "BB", "CC"}, new String[]{"11", "22", "33"}, new String[]{"11", "22", "33"}, new String[]{"11", "22", "33"})} }; } private CsvParserSettings newSettings() { CsvParserSettings settings = new CsvParserSettings(); settings.setDelimiterDetectionEnabled(true); settings.setQuoteDetectionEnabled(true); settings.setParseUnescapedQuotes(false); settings.getFormat().setLineSeparator("\n"); settings.getFormat().setDelimiter('x'); settings.getFormat().setQuote('x'); settings.getFormat().setQuoteEscape('x'); return settings; } @Test(dataProvider = "getInputsAndOutputs") public void testDelimiterDiscovery(String input, List expectedOutput) { CsvParserSettings settings = newSettings(); CsvParser parser = new CsvParser(settings); List rows = parser.parseAll(new StringReader(input)); assertEquals(rows.size(), expectedOutput.size()); for (int i = 0; i < rows.size(); i++) { assertEquals(expectedOutput.get(i), rows.get(i)); } } @Test public void testAutodetection() throws Exception { CsvParserSettings settings = new CsvParserSettings(); settings.detectFormatAutomatically(); CsvParser parser = new CsvParser(settings); String s = "" + "1;2001-01-01;First row;1.1\n" + "2;2002-02-02;Second row;2.2\n" + "3;2003-03-03;Third row;3.3\n" + "4;2004-04-04;Fourth row;4.4"; List rows = parser.parseAll(new StringReader(s)); CsvFormat format = parser.getDetectedFormat(); assertEquals(format.getDelimiter(), ';'); assertEquals(rows.size(), 4); s = "" + "1;2001-01-01;First row;1.1\n" + "2;2002-02-02;Second row;2\n" + "3;2003-03-03;Third row;3.3\n" + "4;2004-04-04;Fourth row;4.4"; rows = parser.parseAll(new StringReader(s)); format = parser.getDetectedFormat(); assertEquals(format.getDelimiter(), ';'); assertEquals(rows.size(), 4); } @Test public static void testDelimitersDetectedUsingOrderOfPreference() { String input = "HEADER1, HEADER2, HEADER3\n" + "11, 12, 13\n" + "21, 22, 23\n" + "31, 32, 33\n"; CsvParserSettings settings = new CsvParserSettings(); settings.setDelimiterDetectionEnabled(true, ',', ' '); CsvParser parser = new CsvParser(settings); parser.parseAll(new StringReader(input)); CsvFormat format = parser.getDetectedFormat(); assertEquals(format.getDelimiter(), ','); } @Test public static void testDelimitersDetectedUsingOrderOfPreference1() { String input = "HEADER 1,HEADER 2,HEADER 3\n" + "SOME TEXT 1,SOME TEXT 2,SOME TEXT 3,"; CsvParserSettings settings = new CsvParserSettings(); settings.setDelimiterDetectionEnabled(true, ',', ' '); settings.setFormatDetectorRowSampleCount(2); CsvParser parser = new CsvParser(settings); parser.parseAll(new StringReader(input)); CsvFormat format = parser.getDetectedFormat(); assertEquals(format.getDelimiter(), ','); } } univocity-parsers-2.9.1/src/test/java/com/univocity/parsers/csv/CsvParserTest.java000077500000000000000000001001431400120543400304070ustar00rootroot00000000000000/******************************************************************************* * Copyright 2014 Univocity Software Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.univocity.parsers.csv; import com.univocity.parsers.*; import com.univocity.parsers.common.*; import com.univocity.parsers.common.processor.*; import com.univocity.parsers.common.record.*; import org.testng.annotations.*; import java.io.*; import java.util.*; import java.util.concurrent.*; import static org.testng.Assert.*; public class CsvParserTest extends ParserTestCase { @DataProvider(name = "testProvider") public Object[][] testProvider() { return new Object[][]{ {"/csv/test.csv", new char[]{'\n'}}, {"/csv/test.csv", null} }; } @DataProvider(name = "csvProvider") public Object[][] csvProvider() { return new Object[][]{ {"/csv/essential.csv", new char[]{'\n'}}, {"/csv/essential-dos.csv", new char[]{'\r', '\n'}}, {"/csv/essential-mac.csv", new char[]{'\r'}}, {"/csv/essential.csv", null}, {"/csv/essential-dos.csv", null}, {"/csv/essential-mac.csv", null} }; } @Test(enabled = true, dataProvider = "csvProvider") public void parseIgnoringWhitespaces(String csvFile, char[] lineSeparator) throws Exception { CsvParserSettings settings = newCsvInputSettings(lineSeparator); settings.setCommentCollectionEnabled(true); settings.setRowProcessor(processor); settings.setHeaderExtractionEnabled(true); settings.setIgnoreLeadingWhitespaces(true); settings.setIgnoreTrailingWhitespaces(true); CsvParser parser = new CsvParser(settings); parser.parse(newReader(csvFile)); String[] expectedHeaders = new String[]{"Year", "Make", "Model", "Description", "Price"}; String[][] expectedResult = new String[][]{ {"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}, {"1999", "Chevy", "Venture \"Extended Edition\"", null, "4900.00"}, {"1996", "Jeep", "Grand Cherokee", "MUST SELL!\nair, moon roof, loaded", "4799.00"}, {"1999", "Chevy", "Venture \"Extended Edition, Very Large\"", null, "5000.00"}, {null, null, "Venture \"Extended Edition\"", null, "4900.00"}, {null, null, null, null, null}, {null, null, null, null, null}, {null, null, "5", null, null}, {"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}, {"1997", "Ford", "E350", " ac, abs, moon ", "3000.00"}, {"1997", "Ford", "E350", " ac, abs, moon ", "3000.00"}, {"19 97", "Fo rd", "E350", " ac, abs, moon ", "3000.00"}, {null, " ", null, " ", "30 00.00"}, {"1997", "Ford", "E350", " \" ac, abs, moon \" ", "3000.00"}, {"1997", "Ford", "E350", "\" ac, abs, moon \" ", "3000.00"}, }; assertHeadersAndValuesMatch(expectedHeaders, expectedResult); Map comments = parser.getContext().comments(); assertEquals(comments.size(), 1); assertEquals(comments.keySet().iterator().next().longValue(), 6L); assertEquals(comments.values().iterator().next(), parser.getContext().lastComment()); assertEquals(parser.getContext().lastComment(), "this is a comment and should be ignored"); } protected CsvParserSettings newCsvInputSettings(char[] lineSeparator) { CsvParserSettings out = new CsvParserSettings(); if (lineSeparator == null) { out.setLineSeparatorDetectionEnabled(true); } else { out.getFormat().setLineSeparator(lineSeparator); } out.getFormat().setNormalizedNewline('\n'); return out; } @Test(enabled = true, dataProvider = "csvProvider") public void parseUsingWhitespaces(String csvFile, char[] lineSeparator) throws Exception { CsvParserSettings settings = newCsvInputSettings(lineSeparator); settings.setRowProcessor(processor); settings.setHeaderExtractionEnabled(true); settings.setNullValue("?????"); settings.setEmptyValue("XXXXX"); settings.setIgnoreLeadingWhitespaces(false); settings.setIgnoreTrailingWhitespaces(false); CsvParser parser = new CsvParser(settings); parser.parse(newReader(csvFile)); String[] expectedHeaders = new String[]{"Year", "Make", "Model", "Description", "Price"}; String[][] expectedResult = new String[][]{ {"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}, {"1999", "Chevy", "Venture \"Extended Edition\"", "XXXXX", "4900.00"}, {"1996", "Jeep", "Grand Cherokee", "MUST SELL!\nair, moon roof, loaded", "4799.00"}, {"1999", "Chevy", "Venture \"Extended Edition, Very Large\"", "?????", "5000.00"}, {"?????", "?????", "Venture \"Extended Edition\"", "XXXXX", "4900.00"}, {"?????", "?????", "?????", "?????", "?????"}, {" ", " ", " ", " ", " "}, {"?????", "?????", " 5 ", "?????", "?????"}, {" "}, {"1997 ", " Ford ", "E350", "ac, abs, moon", " \"3000.00\" \t"}, {"1997", " Ford ", "E350", " ac, abs, moon ", "3000.00 \t"}, {" 1997", " Ford ", "E350", " ac, abs, moon ", "3000.00"}, {" 19 97 ", " Fo rd ", "E350", " ac, abs, moon ", "3000.00"}, {"\t\t", " ", " ", " \" \"\t", "30 00.00\t"}, {"1997", "Ford", "E350", " \" ac, abs, moon \" ", "3000.00"}, {"1997", "Ford", "E350", "\" ac, abs, moon \" ", "3000.00"}, }; assertHeadersAndValuesMatch(expectedHeaders, expectedResult); } @Test(enabled = true, dataProvider = "csvProvider") public void parseColumns(String csvFile, char[] lineSeparator) throws Exception { CsvParserSettings settings = newCsvInputSettings(lineSeparator); settings.setRowProcessor(processor); settings.setHeaderExtractionEnabled(true); settings.setIgnoreLeadingWhitespaces(true); settings.setIgnoreTrailingWhitespaces(true); settings.selectFields("Year"); settings.setColumnReorderingEnabled(false); CsvParser parser = new CsvParser(settings); parser.parse(newReader(csvFile)); String[] expectedHeaders = new String[]{"Year", "Make", "Model", "Description", "Price"}; String[][] expectedResult = new String[][]{ {"1997", null, null, null, null}, {"1999", null, null, null, null}, {"1996", null, null, null, null}, {"1999", null, null, null, null}, {null, null, null, null, null}, {null, null, null, null, null}, {null, null, null, null, null}, {null, null, null, null, null}, {"1997", null, null, null, null}, {"1997", null, null, null, null}, {"1997", null, null, null, null}, {"19 97", null, null, null, null}, {null, null, null, null, null}, {"1997", null, null, null, null}, {"1997", null, null, null, null}, }; assertHeadersAndValuesMatch(expectedHeaders, expectedResult); } private String[] process(String input, Integer[] indexesToExclude, Integer[] indexesToSelect, String[] fieldsToExclude, String[] fieldsToSelect) { CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); settings.setHeaderExtractionEnabled(fieldsToExclude != null || fieldsToSelect != null); if (indexesToExclude != null) { settings.excludeIndexes(indexesToExclude); } else if (fieldsToExclude != null) { settings.excludeFields(fieldsToExclude); } else if (indexesToSelect != null) { settings.selectIndexes(indexesToSelect); } else if (fieldsToSelect != null) { settings.selectFields(fieldsToSelect); } CsvParser parser = new CsvParser(settings); return parser.parseLine(input); } @Test public void columnSelectionTest() { String[] result; String input = "a,b,c,d,e"; Integer[] indexesToExclude = new Integer[]{0, 4}; result = process(input, indexesToExclude, null, null, null); assertEquals(result, new String[]{"b", "c", "d"}); Integer[] indexesToSelect = new Integer[]{0, 4}; result = process(input, null, indexesToSelect, null, null); assertEquals(result, new String[]{"a", "e"}); input = "ha,hb,hc,hd,he\na,b,c,d,e"; String[] fieldsToExclude = new String[]{"hb", "hd"}; result = process(input, null, null, fieldsToExclude, null); assertEquals(result, new String[]{"a", "c", "e"}); String[] fieldsToSelect = new String[]{"hb", "hd"}; result = process(input, null, null, null, fieldsToSelect); assertEquals(result, new String[]{"b", "d"}); } @Override protected RowListProcessor newRowListProcessor() { return new RowListProcessor() { @Override public void processStarted(ParsingContext context) { super.processStarted(context); context.skipLines(2); } @Override public void rowProcessed(String[] row, ParsingContext context) { super.rowProcessed(row, context); // for (int i = 0; i < row.length; i++) { // row[i] = ">>" + row[i] + "<<"; // } // System.out.println(context.currentLine() + " => " + Arrays.toString(row)); if (context.currentLine() == 9) { context.skipLines(1); } } }; } @Test(enabled = true, dataProvider = "csvProvider") public void parseOneByOne(String csvFile, char[] lineSeparator) throws Exception { CsvParserSettings settings = newCsvInputSettings(lineSeparator); settings.setRowProcessor(processor); settings.setHeaderExtractionEnabled(true); settings.setIgnoreLeadingWhitespaces(true); settings.setIgnoreTrailingWhitespaces(true); settings.setHeaders("YR", "MK", "MDL", "DSC", "PRC"); List results = new ArrayList(); CsvParser parser = new CsvParser(settings); try { parser.beginParsing(newReader(csvFile)); Object[] row; while ((row = parser.parseNext()) != null) { if (row.length == 5) { results.add(row); } } } finally { parser.stopParsing(); } String[] expectedHeaders = new String[]{"YR", "MK", "MDL", "DSC", "PRC"}; String[][] expectedResult = new String[][]{ {"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}, {"1999", "Chevy", "Venture \"Extended Edition\"", null, "4900.00"}, {"1996", "Jeep", "Grand Cherokee", "MUST SELL!\nair, moon roof, loaded", "4799.00"}, {"1999", "Chevy", "Venture \"Extended Edition, Very Large\"", null, "5000.00"}, {null, null, "Venture \"Extended Edition\"", null, "4900.00"}, {null, null, null, null, null}, {null, null, null, null, null}, {null, null, "5", null, null}, {"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}, {"1997", "Ford", "E350", " ac, abs, moon ", "3000.00"}, {"1997", "Ford", "E350", " ac, abs, moon ", "3000.00"}, {"19 97", "Fo rd", "E350", " ac, abs, moon ", "3000.00"}, {null, " ", null, " ", "30 00.00"}, {"1997", "Ford", "E350", " \" ac, abs, moon \" ", "3000.00"}, {"1997", "Ford", "E350", "\" ac, abs, moon \" ", "3000.00"}, }; Object[] headers = processor.getHeaders(); TestUtils.assertEquals(headers, expectedHeaders); assertEquals(results.size(), expectedResult.length); for (int i = 0; i < expectedResult.length; i++) { Object[] result = results.get(i); String[] expectedRow = expectedResult[i]; assertEquals(result, expectedRow); } } @Test(enabled = true, dataProvider = "csvProvider") public void parse3Records(String csvFile, char[] lineSeparator) throws Exception { CsvParserSettings settings = newCsvInputSettings(lineSeparator); settings.setRowProcessor(processor); settings.setHeaderExtractionEnabled(true); settings.setIgnoreLeadingWhitespaces(true); settings.setIgnoreTrailingWhitespaces(true); settings.setNumberOfRecordsToRead(3); CsvParser parser = new CsvParser(settings); parser.parse(newReader(csvFile)); String[] expectedHeaders = new String[]{"Year", "Make", "Model", "Description", "Price"}; String[][] expectedResult = new String[][]{ {"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}, {"1999", "Chevy", "Venture \"Extended Edition\"", null, "4900.00"}, {"1996", "Jeep", "Grand Cherokee", "MUST SELL!\nair, moon roof, loaded", "4799.00"}, }; assertHeadersAndValuesMatch(expectedHeaders, expectedResult); } @Test public void parseBrokenQuoteEscape() { CsvParserSettings settings = newCsvInputSettings(new char[]{'\n'}); settings.setParseUnescapedQuotesUntilDelimiter(false); settings.setHeaderExtractionEnabled(false); CsvParser parser = new CsvParser(settings); parser.beginParsing(new StringReader("" + "something,\"a quoted value \"with unescaped quotes\" can be parsed\", something\n" + "1997 , Ford ,E350,\"s, m\"\"\" , \"3000.00\"\n" + "1997 , Ford ,E350,\"ac, abs, moon\" , \"3000.00\" \n" + "something,\"a \"quoted\" \"\"value\"\" \"\"with unescaped quotes\"\" can be parsed\" , something\n")); String[] row = parser.parseNext(); assertEquals(row[0], "something"); assertEquals(row[2], "something"); assertEquals(row[1], "a quoted value \"with unescaped quotes\" can be parsed"); row = parser.parseNext(); assertEquals(row, new String[]{"1997", "Ford", "E350", "s, m\"", "3000.00"}); row = parser.parseNext(); assertEquals(row, new String[]{"1997", "Ford", "E350", "ac, abs, moon", "3000.00"}); row = parser.parseNext(); assertEquals(row[0], "something"); assertEquals(row[2], "something"); assertEquals(row[1], "a \"quoted\" \"value\" \"with unescaped quotes\" can be parsed"); } @Test public void testReadEmptyValue() { CsvParserSettings settings = newCsvInputSettings(new char[]{'\n'}); settings.setEmptyValue(""); settings.setHeaderExtractionEnabled(false); CsvParser parser = new CsvParser(settings); parser.beginParsing(new StringReader("a,b,,c,\"\",\r\n")); String[] row = parser.parseNext(); assertEquals(row[0], "a"); assertEquals(row[1], "b"); assertEquals(row[2], null); assertEquals(row[3], "c"); assertEquals(row[4], ""); assertEquals(row[5], null); } @DataProvider public Object[][] escapeHandlingProvider() { return new Object[][]{ //parsing a line with the following content: ||,|| |"," |" B |" "," |" ||" {false, false, new String[]{"||", "|| |\"", " \" B \" ", " \" |"}}, // process escapes on quoted values only: || , || |" , " B " , " | {false, true, new String[]{"|", "| \"", " \" B \" ", " \" |"}}, // process escapes quoted and unquoted: | , | " , " B " , " | {true, false, new String[]{"||", "|| |\"", " |\" B |\" ", " |\" ||"}}, // keep escape on quoted values only: || , || |" , " |" B |" " , |" ||" {true, true, new String[]{"||", "|| |\"", " |\" B |\" ", " |\" ||"}} // keep escape on everything: || , || |" , " |" B |" " , |" ||" }; } @Test(dataProvider = "escapeHandlingProvider") public void testHandlingOfEscapeSequences(boolean keepEscape, boolean escapeUnquoted, String[] expected) throws Exception { CsvParserSettings settings = new CsvParserSettings(); settings.setKeepEscapeSequences(keepEscape); settings.setEscapeUnquotedValues(escapeUnquoted); settings.getFormat().setCharToEscapeQuoteEscaping('|'); settings.getFormat().setQuoteEscape('|'); String line = "||,|| |\",\" |\" B |\" \",\" |\" ||\""; CsvParser parser = new CsvParser(settings); String[] result = parser.parseLine(line); // ||, || |", " |" B |" ", " |" ||" assertEquals(result, expected); } @Test public void testEscapedLineEndingsAreNotModified() { CsvParserSettings settings = new CsvParserSettings(); settings.setNormalizeLineEndingsWithinQuotes(false); settings.getFormat().setLineSeparator("\r\n"); CsvParser parser = new CsvParser(settings); String input = "1,\" Line1 \r\n Line2 \r Line3 \n Line4 \n\r \"\r\n" + "2,\" Line10 \r\n Line11 \""; List result = parser.parseAll(new StringReader(input)); // ||, || |", " |" B |" ", " |" ||" assertEquals(result.size(), 2); assertEquals(result.get(0).length, 2); assertEquals(result.get(1).length, 2); assertEquals(result.get(0), new String[]{"1", " Line1 \r\n Line2 \r Line3 \n Line4 \n\r "}); assertEquals(result.get(1), new String[]{"2", " Line10 \r\n Line11 "}); } public char[] getLineSeparator() { return new char[]{'\n'}; } @Test public void shouldNotAllowParseUnescapedQuotes() throws UnsupportedEncodingException { CsvParserSettings settings = newCsvInputSettings(getLineSeparator()); settings.setRowProcessor(new RowListProcessor()); //Default used by CsvParserTest skip 2 lines settings.setParseUnescapedQuotes(false); //To force exception CsvParser parser = new CsvParser(settings); try { parser.parse(new StringReader("1997,\"TV 29\"LED\"\n")); fail("Expected exception to be thrown here"); } catch (TextParsingException ex) { assertTrue(ex.getMessage().contains("Unescaped quote character")); } } @Test public void parseQuotedStringFollowedByBlankSpace() throws UnsupportedEncodingException { RowListProcessor processor = new RowListProcessor(); CsvParserSettings settings = newCsvInputSettings(getLineSeparator()); settings.setRowProcessor(processor); //Default used by CsvParserTest skip 2 lines settings.setParseUnescapedQuotes(true); settings.setParseUnescapedQuotesUntilDelimiter(false); CsvParser parser = new CsvParser(settings); parser.parse(new StringReader("1997,\"TV 29\" LED\"\n")); List rows = processor.getRows(); assertEquals(rows.size(), 1); String[] firstRow = rows.get(0); assertEquals(firstRow[0], "1997"); assertEquals(firstRow[1], "TV 29\" LED"); } @Test(dataProvider = "testProvider") public void shouldNotAllowUnexpectedCharacterAfterQuotedValue(String csvFile, char[] lineSeparator) throws UnsupportedEncodingException { CsvParserSettings settings = newCsvInputSettings(lineSeparator); settings.setParseUnescapedQuotes(false); CsvParser parser = new CsvParser(settings); try { parser.parseLine("1997,\"value\"x"); fail("Expected exception to be thrown here"); } catch (TextParsingException ex) { assertTrue(ex.getMessage().contains("Unescaped quote character '\"' inside quoted value of CSV field")); } } @Test public void parseValueProcessingEscapeNotIgnoringWhitespace() { RowListProcessor processor = new RowListProcessor(); CsvParserSettings settings = newCsvInputSettings(getLineSeparator()); settings.setRowProcessor(processor); //Default used by CsvParserTest skip 2 lines settings.setKeepEscapeSequences(true); settings.setIgnoreTrailingWhitespaces(false); settings.setEscapeUnquotedValues(true); CsvFormat format = new CsvFormat(); format.setQuoteEscape('\''); format.setCharToEscapeQuoteEscaping('\\'); settings.setFormat(format); CsvParser parser = new CsvParser(settings); parser.parse(new StringReader("'\\\"a\n")); //goes into the else statement of CsvParser.parseValueProcessingEscape() method. List rows = processor.getRows(); assertEquals(rows.size(), 1); String[] firstRow = rows.get(0); assertEquals(firstRow[0], "\\\"a"); } @Test public void parseValueProcessingEscapeNotIgnoringWhitespacePrevQuoteEscape2() { RowListProcessor processor = new RowListProcessor(); CsvParserSettings settings = newCsvInputSettings(getLineSeparator()); settings.setRowProcessor(processor); //Default used by CsvParserTest skip 2 lines settings.setKeepEscapeSequences(true); settings.setIgnoreTrailingWhitespaces(false); settings.setEscapeUnquotedValues(true); CsvFormat format = new CsvFormat(); format.setQuoteEscape('\''); format.setCharToEscapeQuoteEscaping('\\'); settings.setFormat(format); CsvParser parser = new CsvParser(settings); parser.parse(new StringReader("\\\'\n")); List rows = processor.getRows(); assertEquals(rows.size(), 1); String[] firstRow = rows.get(0); assertEquals(firstRow[0], "\\\\'"); } @Test public void parseValueProcessingEscapeNotIgnoringWhitespacePrevQuoteEscape() { RowListProcessor processor = new RowListProcessor(); CsvParserSettings settings = newCsvInputSettings(getLineSeparator()); settings.setRowProcessor(processor); //Default used by CsvParserTest skip 2 lines settings.setKeepEscapeSequences(true); settings.setIgnoreTrailingWhitespaces(false); settings.setEscapeUnquotedValues(true); CsvFormat format = new CsvFormat(); format.setQuoteEscape('\''); format.setCharToEscapeQuoteEscaping('\\'); settings.setFormat(format); CsvParser parser = new CsvParser(settings); parser.parse(new StringReader("'\"a\n")); List rows = processor.getRows(); assertEquals(rows.size(), 1); String[] firstRow = rows.get(0); assertEquals(firstRow[0], "'\"a"); } @DataProvider public Object[][] skipLinesProvider() { return new Object[][]{ {0, "1234"}, {1, "234"}, {2, "34"}, {3, "4"}, {4, null}, {5, "BOOM"}, }; } @Test(dataProvider = "skipLinesProvider") public void testSkipLines(int rowsToSkip, String expectedResult) { CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); settings.setNumberOfRowsToSkip(rowsToSkip); CsvParser parser = new CsvParser(settings); String input = "1\n2\n3\n4\n"; List result = parser.parseAll(new StringReader(input)); StringBuilder out = null; for (String row[] : result) { if (out == null) { out = new StringBuilder(); } assertEquals(row.length, 1); out.append(row[0]); } if("BOOM".equals(expectedResult)){ expectedResult = null; } assertEquals(out == null ? null : out.toString(), expectedResult); } @Test public void testParseUnescapedQuotesWithStop() { CsvParserSettings settings = new CsvParserSettings(); settings.setParseUnescapedQuotesUntilDelimiter(true); settings.getFormat().setLineSeparator("\n"); CsvParser parser = new CsvParser(settings); String input = "field1,\"inner quote\" field2,\"12,34\",\",5\","; String[] values = parser.parseLine(input); assertEquals(values[0], "field1"); assertEquals(values[1], "\"inner quote\" field2"); assertEquals(values[2], "12,34"); assertEquals(values[3], ",5"); assertEquals(values[4], null); } @Test public void parseIgnoreTrailingWhitespace() { CsvParserSettings settings = new CsvParserSettings(); settings.getFormat().setLineSeparator("\n"); settings.setIgnoreTrailingWhitespaces(true); CsvParser parser = new CsvParser(settings); String[] value = parser.parseLine("b "); assertEquals(value[0], "b"); } @Test public void parseWithAutoExpansion() { CsvParserSettings settings = new CsvParserSettings(); settings.setMaxCharsPerColumn(-1); StringBuilder in = new StringBuilder(100000); for (int i = 0; i < 100000; i++) { in.append(i % 10); if (i % 10000 == 0) { in.append(','); } } String[] result = new CsvParser(settings).parseLine(in.toString()); StringBuilder out = new StringBuilder(); for (String value : result) { if (out.length() > 0) { out.append(','); } out.append(value); } assertEquals(out.toString(), in.toString()); } @Test public void testErrorMessageRestrictions() { CsvParserSettings settings = new CsvParserSettings(); settings.setMaxCharsPerColumn(3); settings.setErrorContentLength(0); try { new CsvParser(settings).parseLine("abcde"); fail("Expecting an exception here"); } catch (TextParsingException ex) { assertFalse(ex.getMessage().contains("abc")); assertNull(ex.getParsedContent()); } settings.setErrorContentLength(2); try { new CsvParser(settings).parseLine("abcde"); fail("Expecting an exception here"); } catch (TextParsingException ex) { assertTrue(ex.getMessage().contains("...bc")); assertEquals(ex.getParsedContent(), "abc"); } } @Test public void testKeepQuotes() { CsvParserSettings settings = new CsvParserSettings(); settings.setKeepQuotes(true); settings.getFormat().setQuote('\''); settings.getFormat().setQuoteEscape('\''); CsvParser parser = new CsvParser(settings); String[] result = parser.parseLine("a,'b', '', '' c '', '' ' '', ''''"); assertEquals(result[0], "a"); assertEquals(result[1], "'b'"); assertEquals(result[2], "''"); assertEquals(result[3], "'' c ''"); assertEquals(result[4], "'' ' ''"); assertEquals(result[5], "'''"); } @Test public void testNullValue() { CsvParserSettings settings = new CsvParserSettings(); settings.setIgnoreLeadingWhitespaces(false); settings.setIgnoreTrailingWhitespaces(true); settings.setNullValue("NULL"); CsvParser parser = new CsvParser(settings); String[] result = parser.parseLine(", ,"); assertEquals(result.length, 3); assertEquals(result[0], "NULL"); assertEquals(result[1], "NULL"); assertEquals(result[2], "NULL"); } @Test public void testColumnReorderingWithUserProvidedHeaders() throws Exception { CsvParserSettings settings = new CsvParserSettings(); settings.setHeaders("a", "b", "c"); settings.setColumnReorderingEnabled(false); settings.selectFields("a", "c"); String[] values = new CsvParser(settings).parseLine("1,2,3"); assertEquals(values, new String[]{"1", null, "3"}); } @Test public void testEscapeCharacter() { CsvParserSettings parserSettings = new CsvParserSettings(); parserSettings.getFormat().setQuoteEscape('/'); CsvParser parser = new CsvParser(parserSettings); String[] line; line = parser.parseLine("\"a ,/,b/,\",c"); assertEquals(line.length, 2); assertEquals(line[0], "a ,/,b/,"); assertEquals(line[1], "c"); line = parser.parseLine("\"a ,//,b//,\",c"); assertEquals(line.length, 2); assertEquals(line[0], "a ,/,b/,"); assertEquals(line[1], "c"); } @Test public void testBitsAreNotDiscardedWhenParsing() { CsvParserSettings parserSettings = new CsvParserSettings(); parserSettings.setSkipBitsAsWhitespace(false); CsvParser parser = new CsvParser(parserSettings); String[] line; line = parser.parseLine("\0 a, b"); assertEquals(line.length, 2); assertEquals(line[0], "\0 a"); assertEquals(line[1], "b"); line = parser.parseLine("\1 a, b \0"); assertEquals(line.length, 2); assertEquals(line[0], "\1 a"); assertEquals(line[1], "b \0"); line = parser.parseLine("\2 a, \"b, \1\""); assertEquals(line.length, 2); assertEquals(line[0], "a"); assertEquals(line[1], "b, \1"); } @Test public void testParserIteratorOnFile() throws Exception { CsvParserSettings parserSettings = new CsvParserSettings(); parserSettings.setLineSeparatorDetectionEnabled(true); CsvParser parser = new CsvParser(parserSettings); String[][] correctRows = { {"a", "b", "c"}, {"d", "e", "f"}, {"g", "h", "i"}, {"j", null}, {"k", "l"}, {"m", "n", "o", "p", "q", "r"} }; Reader input = newReader("/csv/iterating_test.csv"); int i = 0; for (String[] row : parser.iterate(input)) { assertEquals(row, correctRows[i++]); } input = newReader("/csv/iterating_test.csv"); i = 0; for (Record row : parser.iterateRecords(input)) { assertEquals(row.getValues(), correctRows[i++]); } for (Record row : parser.iterateRecords(new StringReader(""))) { fail("Empty input, should not get here"); } } private static void append4000Symbols(StringBuilder sb) { final long startTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(200); for (int i = 0; i < 200; i++) { sb.append(startTime + TimeUnit.SECONDS.toMillis(i)).append(",10000\n"); } } @Test public void testCollectCommentOnBufferUpdate() { final StringBuilder commentLine = new StringBuilder("#"); for (int i = 0; i < 100; i++) { commentLine.append(' ').append(i); } final StringBuilder csv = new StringBuilder("time,value\n"); append4000Symbols(csv); // now the comment processing will meet buffer update operation (if buffer size is 4096) csv.append(commentLine); final CsvParserSettings csvParserSettings = new CsvParserSettings(); csvParserSettings.setCommentCollectionEnabled(true); csvParserSettings.setReadInputOnSeparateThread(false); final CsvParser csvParser = new CsvParser(csvParserSettings); csvParser.parseAll(new StringReader(csv.toString())); final Map comments = csvParser.getContext().comments(); assertEquals(1, comments.size()); assertEquals(commentLine.substring(2), comments.values().iterator().next()); } @Test public void shouldPrintUserDefinedHeaders() { final String[] userDefinedHeader = {"timestamp", "memory_used"}; final CsvParserSettings settings = new CsvParserSettings(); settings.setHeaderExtractionEnabled(false); settings.setHeaders(userDefinedHeader); final String[][] headersFromContext = new String[][]{null}; settings.setProcessor(new AbstractRowProcessor() { @Override public void processStarted(ParsingContext context) { headersFromContext[0] = context.headers(); System.out.println("headers: " + Arrays.toString(context.headers())); } }); settings.setReadInputOnSeparateThread(false); final CsvParser csvParser = new CsvParser(settings); final String csv = "2018-11-22T17:53:19.446Z,1493984088\n" + "2018-11-22T17:53:34.447Z,865556632\n" + "2018-11-22T17:53:49.447Z,600667192"; csvParser.parse(new StringReader(csv)); assertEquals(headersFromContext[0], userDefinedHeader); } @Test public void shouldResolveSpaceAsColumnSeparator() { final Reader csv = new StringReader("