From 897dea0b30ca7ae77e0f08537cbb0789bc07d7be Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 5 Dec 2020 17:13:05 +0800 Subject: [PATCH] fix date read for sax bug --- CHANGELOG.md | 1 + .../poi/excel/sax/Excel03SaxReader.java | 2 +- .../cn/hutool/poi/excel/sax/ExcelSaxUtil.java | 76 ++++++++++-------- .../hutool/poi/excel/BigExcelWriteTest.java | 11 +-- .../cn/hutool/poi/excel/CellUtilTest.java | 2 +- .../cn/hutool/poi/excel/ExcelReadTest.java | 4 +- .../cn/hutool/poi/excel/ExcelSaxReadTest.java | 29 +++++-- .../cn/hutool/poi/excel/ExcelUtilTest.java | 3 +- .../cn/hutool/poi/excel/ExcelWriteTest.java | 16 ++-- .../java/cn/hutool/poi/excel/OrderExcel.java | 2 +- .../java/cn/hutool/poi/excel/TestBean.java | 2 +- .../cn/hutool/poi/word/WordWriterTest.java | 2 +- .../src/test/resources/data_for_sax_test.xls | Bin 31232 -> 31744 bytes .../src/test/resources/data_for_sax_test.xlsx | Bin 10000 -> 10176 bytes 14 files changed, 83 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 976b6982c..fdd19a094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Bug修复 * 【cache 】 修复Cache中get重复misCount计数问题(issue#1281@Github) +* 【poi 】 修复sax读取自定义格式单元格无法识别日期类型的问题(issue#1283@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java index 67d31b67f..dbd97dd04 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel03SaxReader.java @@ -268,7 +268,7 @@ public class Excel03SaxReader implements HSSFListener, ExcelSaxReader createSaxReader(boolean isXlsx, RowHandler rowHandler) { + return isXlsx + ? new Excel07SaxReader(rowHandler) + : new Excel03SaxReader(rowHandler); + } + /** * 根据数据类型获取数据 * @@ -175,12 +190,13 @@ public class ExcelSaxUtil { /** * 判断数字Record中是否为日期格式 - * @param cell 单元格记录 + * + * @param cell 单元格记录 * @param formatListener {@link FormatTrackingHSSFListener} * @return 是否为日期格式 * @since 5.4.8 */ - public static boolean isDateFormat(CellValueRecordInterface cell, FormatTrackingHSSFListener formatListener){ + public static boolean isDateFormat(CellValueRecordInterface cell, FormatTrackingHSSFListener formatListener) { final int formatIndex = formatListener.getFormatIndex(cell); final String formatString = formatListener.getFormatString(cell); return isDateFormat(formatIndex, formatString); @@ -189,15 +205,15 @@ public class ExcelSaxUtil { /** * 判断日期格式 * - * @param formatIndex 格式索引,一般用于内建格式 + * @param formatIndex 格式索引,一般用于内建格式 * @param formatString 格式字符串 * @return 是否为日期格式 * @since 5.5.3 */ - public static boolean isDateFormat(int formatIndex, String formatString){ + public static boolean isDateFormat(int formatIndex, String formatString) { // https://blog.csdn.net/u014342130/article/details/50619503 // issue#1283@Github - if(formatIndex == 28 || formatIndex == 31){ + if (formatIndex == 28 || formatIndex == 31) { // 28 -> m月d日 // 31 -> yyyy年m月d日 return true; @@ -227,43 +243,22 @@ public class ExcelSaxUtil { return DateUtil.date(org.apache.poi.ss.usermodel.DateUtil.getJavaDate(value, false)); } - /** - * 创建 {@link ExcelSaxReader} - * - * @param isXlsx 是否为xlsx格式(07格式) - * @param rowHandler 行处理器 - * @return {@link ExcelSaxReader} - * @since 5.4.4 - */ - public static ExcelSaxReader createSaxReader(boolean isXlsx, RowHandler rowHandler) { - return isXlsx - ? new Excel07SaxReader(rowHandler) - : new Excel03SaxReader(rowHandler); - } - /** * 在Excel03 sax读取中获取日期或数字类型的结果值 - * @param cell 记录单元格 - * @param value 值 + * + * @param cell 记录单元格 + * @param value 值 * @param formatListener {@link FormatTrackingHSSFListener} * @return 值,可能为Date或Double或Long * @since 5.5.0 */ - public static Object getNumberOrDateValue(CellValueRecordInterface cell, double value, FormatTrackingHSSFListener formatListener){ + public static Object getNumberOrDateValue(CellValueRecordInterface cell, double value, FormatTrackingHSSFListener formatListener) { Object result; - if(ExcelSaxUtil.isDateFormat(cell, formatListener)){ + if (isDateFormat(cell, formatListener)) { // 可能为日期格式 - result = ExcelSaxUtil.getDateValue(value); - } else { - final long longPart = (long) value; - // 对于无小数部分的数字类型,转为Long,否则保留原数字 - if (((double) longPart) == value) { - result = longPart; - } else { - result = value; - } + return getDateValue(value); } - return result; + return getNumberValue(value, formatListener.getFormatString(cell)); } /** @@ -278,9 +273,20 @@ public class ExcelSaxUtil { if (StrUtil.isBlank(value)) { return null; } - double numValue = Double.parseDouble(value); + return getNumberValue(Double.parseDouble(value), numFmtString); + } + + /** + * 获取数字类型值,除非格式中明确数字保留小数,否则无小数情况下按照long返回 + * + * @param numValue 值 + * @param numFmtString 格式 + * @return 数字,可以是Double、Long + * @since 5.5.3 + */ + private static Number getNumberValue(double numValue, String numFmtString) { // 普通数字 - if (null != numFmtString && numFmtString.indexOf(StrUtil.C_DOT) < 0) { + if (null != numFmtString && false == StrUtil.contains(numFmtString, CharUtil.DOT)) { final long longPart = (long) numValue; //noinspection RedundantIfStatement if (longPart == numValue) { diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/BigExcelWriteTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/BigExcelWriteTest.java index 6cf85898d..1b5cf41ed 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/BigExcelWriteTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/BigExcelWriteTest.java @@ -1,13 +1,10 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.poi.excel.BigExcelWriter; -import cn.hutool.poi.excel.ExcelUtil; -import cn.hutool.poi.excel.ExcelWriter; import cn.hutool.poi.excel.style.StyleUtil; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.FillPatternType; @@ -163,21 +160,21 @@ public class BigExcelWriteTest { @Test @Ignore public void writeBeanTest() { - TestBean bean1 = new TestBean(); + cn.hutool.poi.excel.TestBean bean1 = new cn.hutool.poi.excel.TestBean(); bean1.setName("张三"); bean1.setAge(22); bean1.setPass(true); bean1.setScore(66.30); bean1.setExamDate(DateUtil.date()); - TestBean bean2 = new TestBean(); + cn.hutool.poi.excel.TestBean bean2 = new cn.hutool.poi.excel.TestBean(); bean2.setName("李四"); bean2.setAge(28); bean2.setPass(false); bean2.setScore(38.50); bean2.setExamDate(DateUtil.date()); - List rows = CollUtil.newArrayList(bean1, bean2); + List rows = CollUtil.newArrayList(bean1, bean2); // 通过工具类创建writer String file = "e:/bigWriteBeanTest.xlsx"; FileUtil.del(file); diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/CellUtilTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/CellUtilTest.java index 63a4cac69..11f11f896 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/CellUtilTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/CellUtilTest.java @@ -1,4 +1,4 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import org.apache.poi.ss.usermodel.BuiltinFormats; import org.junit.Ignore; diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java index 1ed8526e1..ea6ba57a3 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelReadTest.java @@ -1,11 +1,9 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.lang.Console; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.poi.excel.ExcelReader; -import cn.hutool.poi.excel.ExcelUtil; import lombok.Data; import org.junit.Assert; import org.junit.Ignore; diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java index ffce24266..3407be109 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java @@ -1,4 +1,4 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; @@ -6,7 +6,6 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Console; import cn.hutool.core.util.StrUtil; -import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.cell.FormulaCellValue; import cn.hutool.poi.excel.sax.Excel03SaxReader; import cn.hutool.poi.excel.sax.handler.RowHandler; @@ -138,15 +137,33 @@ public class ExcelSaxReadTest { } @Test - public void dateReadTest() { + public void dateReadXlsTest() { List rows = new ArrayList<>(); - ExcelUtil.readBySax("data_for_sax_test.xls", 0, (i, i1, list) -> - rows.add(StrUtil.toString(list.get(0)))); + ExcelUtil.readBySax("data_for_sax_test.xls", 0, + (i, i1, list) ->{ + rows.add(StrUtil.toString(list.get(0))); + } + ); Assert.assertEquals("2020-10-09 00:00:00", rows.get(1)); // 非日期格式不做转换 Assert.assertEquals("112233", rows.get(2)); - Assert.assertEquals("1000", rows.get(3)); + Assert.assertEquals("1000.0", rows.get(3)); + Assert.assertEquals("2012-12-21 00:00:00", rows.get(4)); + } + + @Test + public void dateReadXlsxTest() { + List rows = new ArrayList<>(); + ExcelUtil.readBySax("data_for_sax_test.xlsx", 0, + (i, i1, list) -> rows.add(StrUtil.toString(list.get(0))) + ); + + Assert.assertEquals("2020-10-09 00:00:00", rows.get(1)); + // 非日期格式不做转换 + Assert.assertEquals("112233", rows.get(2)); + Assert.assertEquals("1000.0", rows.get(3)); + Assert.assertEquals("2012-12-21 00:00:00", rows.get(4)); } @Test diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelUtilTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelUtilTest.java index 8d723f9bf..0a0a7d1ca 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelUtilTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelUtilTest.java @@ -1,6 +1,5 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; -import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.cell.CellLocation; import org.junit.Assert; import org.junit.Test; diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java index 17bc26ba7..4d16c964d 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelWriteTest.java @@ -1,4 +1,4 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; @@ -6,8 +6,6 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.poi.excel.ExcelUtil; -import cn.hutool.poi.excel.ExcelWriter; import cn.hutool.poi.excel.style.StyleUtil; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.FillPatternType; @@ -340,21 +338,21 @@ public class ExcelWriteTest { @Test @Ignore public void writeBeanTest() { - TestBean bean1 = new TestBean(); + cn.hutool.poi.excel.TestBean bean1 = new cn.hutool.poi.excel.TestBean(); bean1.setName("张三"); bean1.setAge(22); bean1.setPass(true); bean1.setScore(66.30); bean1.setExamDate(DateUtil.date()); - TestBean bean2 = new TestBean(); + cn.hutool.poi.excel.TestBean bean2 = new cn.hutool.poi.excel.TestBean(); bean2.setName("李四"); bean2.setAge(28); bean2.setPass(false); bean2.setScore(38.50); bean2.setExamDate(DateUtil.date()); - List rows = CollUtil.newArrayList(bean1, bean2); + List rows = CollUtil.newArrayList(bean1, bean2); // 通过工具类创建writer String file = "e:/writeBeanTest.xlsx"; FileUtil.del(file); @@ -376,17 +374,17 @@ public class ExcelWriteTest { @Test @Ignore public void writeBeanTest2() { - OrderExcel order1 = new OrderExcel(); + cn.hutool.poi.excel.OrderExcel order1 = new cn.hutool.poi.excel.OrderExcel(); order1.setId("1"); order1.setNum("123"); order1.setBody("body1"); - OrderExcel order2 = new OrderExcel(); + cn.hutool.poi.excel.OrderExcel order2 = new cn.hutool.poi.excel.OrderExcel(); order1.setId("2"); order1.setNum("456"); order1.setBody("body2"); - List rows = CollUtil.newArrayList(order1, order2); + List rows = CollUtil.newArrayList(order1, order2); // 通过工具类创建writer String file = "f:/test/writeBeanTest2.xlsx"; FileUtil.del(file); diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/OrderExcel.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/OrderExcel.java index 279221787..10bf20af9 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/OrderExcel.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/OrderExcel.java @@ -1,4 +1,4 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import lombok.Data; diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/TestBean.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/TestBean.java index 62c8405ba..a1ee1644a 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/TestBean.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/TestBean.java @@ -1,4 +1,4 @@ -package cn.hutool.poi.excel.test; +package cn.hutool.poi.excel; import lombok.Data; diff --git a/hutool-poi/src/test/java/cn/hutool/poi/word/WordWriterTest.java b/hutool-poi/src/test/java/cn/hutool/poi/word/WordWriterTest.java index b05131edd..22effb9a8 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/word/WordWriterTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/word/WordWriterTest.java @@ -1,4 +1,4 @@ -package cn.hutool.poi.word.test; +package cn.hutool.poi.word; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; diff --git a/hutool-poi/src/test/resources/data_for_sax_test.xls b/hutool-poi/src/test/resources/data_for_sax_test.xls index ebebc51bc0b1676a907911e2862d27cc5934857c..a5f18d6340d40a3e94a6954e02be71c8bcf4ea50 100644 GIT binary patch delta 3239 zcmbVOUuaWT82`@AO?p$>G_AHbN!wV{vQgLCrq&9zX{z9ZqYsWJ}6iODT;~KhY99E<~D*<6n&V=#-T0|={f9AASXA! z^Sj^g`+et}@7~OBDDww8)r*Es$D8%F0C4@&Yba-l)fPdFN;xmck7} zwXIW#{J=4XS($Bv{RAI?^6XVlyT#nABSdCzYsGsjF`t482cqakWBCB}QXGLH#J6bD zRG?p&aB;sB*T9qt08}gRemS0kA?XByq*NGSSjq?Rm+{=7H=}u@`bxNgeZC&g0Nlir zzBSeXn1en*yPyqX@WPJ43UbvBUF6#iy+HA`&RCh<4&DF2h!IANxp&05j_MO|3kEqc zi&I-k6#tE;npad#l0sE7FRK4Q)|7tU+d=2IIOOK5e)Y!aH3EVgr711nR^%@$;yV7O zw#By|@*BoY{HnIYaMQ%`pozN!4Lq<@1g1qGF8~oQQP!ZABb9Ud7@(dK(proX3w4<1j(>qZ!bc{o LnpIS#^j`NjY>e)( delta 2883 zcmbVOT}V_x6h3oz)w^o@r|sQ8*G;X^3R!8U3aiXqI+_mVT=%t^R?T?^9>Y>1g2nwVJ3Gz0x+ufa+oq6dluIxA8eCK>;<{WNn z0i}MS3u{rw#aPJR4gk}=_ff_~5roEZ#AdMHfpHD>9DjN;2DLClsAlp4BLCn_j#*rd z!VtkXKwUhgssa6%bq_8jxTK|5i03`V;LqtPh7T48Q6)bDNilwuxTPl`=W&Mz|vfDV1ZMD+8vfDV1BPtf>@rjDXdDNCF zB61$Ls#u)Ib8=Q|8|U$zip6>K`J{QZws9T@R4mTpq>9CPoKvwlk1dAK#D@*$vX2_g z{2kT*-FdzOhTtvFGUFV}=a;gCY7Y3Bd}uRG_$AFmN4)v+wOMGUccM6LIm&wRlVSM+ zP3yVFtdQ7YEs{RXJ@`m~NsG?9bbu*SHqgAA)3u_{&(9Zfq_Jp5$z;;BbZIORb1)d8 zm=6D_9 z8d>!*zh5nC^n{z_MrBoGO<5VSm}vBQSW!hYd!>-V{~u8Z57#yDt&$)509+G1suP;P zVsXRbkBY?&i_H;*7B?(LRV;2;WK}F~Sa|9cCuEx0k|L(l>5{9+V%7;!j9$g_^}<#B zR?l2T+H=8Z5y%{fUUO?7+OZy+)IP7t+>X7kuB4@o^a9lE$c%Qa_IVndu3US8<|HfR zDDbnLdvL7(MB_)OhF1jXgfKYCr%Us~Lt0(vreE~IU;4vN8+q@c?>;v2K`2wTYZ8s^ ze(Qkcp_}oCACN8M*|O4=9=hSy7fsV#MYr7<@#lWD(I43nzk{y)A%Bb0y)|eoaY#?y hUGbog?8ZkEr?s;?@b5$dwcz37$?8_JhrVag=Pwy3crgF~ diff --git a/hutool-poi/src/test/resources/data_for_sax_test.xlsx b/hutool-poi/src/test/resources/data_for_sax_test.xlsx index 8fb64fd8b5f1031ac5f20b8f28ac3d6994cd6e07..ecbac56a91bb6f493728d905ac3d37c2e603c416 100644 GIT binary patch delta 2823 zcmY*bX*kpm7afLS#x_ZeC5@QT46Fxc0dH?VIbe`wl=X|;MoO{yEI?P{EAutiw5|KK35J&+++=B1`6#qOa zcw}M2AG(8r7;i0l?6ZgNz^@l`3*r1b(~}u1 ze6ijRFUK2@Nl=%l_Ti8+m4=vh#%ko(+G;#D8HS|d3H5#&8J44Wo5ah`2hchD6DTtm z_T#pC)xU*20qgJ^fz~kclPG#FTJ)UbkgkBl}oE4AHKsGA@$*Y?H=FqF_p=jBxaDznJk+AQ-W(`N=O&{Ri**=?Poj|@fXuzMT zb8y?I4Uqd(QeL?Fb^4H#SpDO=Jr4$W*6nh_?4e6=xLw{-zfu9*B?QIi0WQ-8ScJ=D z*a8UN-0yvaZO1N8PKv;~px)WOx6BO&rCF`B9wz5359l>iCm^oMG9Anr`LtHZ9U<^t zgU1$y1hSXLpu5CEQ>@G6)@tY&wu>q^7^dyoGuG3g5JT~joLB%#Ck<{svMytZGWnf< z&Kxi*zxN}0`|gRpvzJMVt)U~(0mg=2HwsjJSq%2wHko#dI`}NpXTi;T!b?*V-7=3j zEtq>eEty{tcMo=WO-Q((wQyq9CB^Vb@W}zTd-h~ZKXu4oSEA)~k*4&|-F&g2O>oT; zj#+1p@$UH*v+DO{l{Y|XolBIk2WC;Mu#nKTO>(ueHXvUKN7+x&^hf}O(XM(x}TlkT5vy*jTZxIi2uCTingfSsE9T{V=na)^kx+wChThPz?Ji zh6acxT*+><_cvFYlK^0;9Lfi+(ihf0n=*cZ?>LtG57SVuhvL_q#kWk!oLw3@llC@lm`qE(?T;AZUIxpu%-C5a*99zN^vV@(AT)F-n2qBQ7-ldN+_h1I zRjg!0pjnG#$}DaIwJV=s&65VQ5`&~8x@D(ucai-zl|e4PmRlRlKGF3b?QcUr>x!ebvHzF?Mm_F=hNGInr(K z_vPo}&mIXpjGfnrr`DAH)}#i*KG3`)kP6JGjy&rFE9AN zeRFu6c9R^x24Ps2PwknS>Z^6cAnuoxtcK@@<_1MSfqrXkTd1HLPWeT2ZH^bGi{q;T z+}XjCdz-;>_mEGs1u||f=;LbSX5y}@PA}eJuXig8gm$-@w_H~9?B_HS1OK?u^l7^0 zs@zfaBWdK8IEMF{m|@G%NTfa`ngHuRAW3X(K0p1viO&F34GUw&SvIjmZ#yJ>fn3v4 zv^Um7mB#sW!SW{Q<0svVNl~4ng}>AQf`ImwLK>oxEGnhgxhb~l`t+}9unzeudWhoWNZky)l%?6!vK7_u=1ct*-etj_2*!B z!|`o<^tUz}b~`^_1rz17Ge%0^L4$Rdr}UhRC#F3sp6AB$D9${@xeW|0ILmYcy(cVo zb)@_3&)c0j`UBw@wcWxwPIl2LCcmLykemAZ#Uu~R$-2iaIr~qm847INmj0^6wF=2R z#*%`Ll34@c+&Y~*BeA`kj;aw0_H2!a)GuraOcr~cyF#A?{V;;80>`wU^t0NudN0oK zGYQd;(_%*X%|A@$nd#L&?saaE%unG_xFyKZ(wi239<0u~;CiF4H=v8*vk5Jrab$E9 zW*D=i&akv8yL9dkvpWF(M^6#F9~l_4!#zEW>#qr-=>P}gE-6IZCgUINzKFbrL#OyA zaArt-^rFs>iz1s%E_wGvW_KugP4*-+_-$f7QS{3j5eLENGW!g!EGStneFjG4E6%ui z&$W#YRb*{)o{wMo- zcY(~A03hSk6n^Z(SFd*SBZwd0kx+I^Ml?;Cl4>ntG6RmsV5#a@0>NLO+{S zg-JWE$~T6|8kUX`gl@o%Jr;UK!fc{i{31gnb%RDWe6|VjXb!zYKF`nYx9_}lc&aJ{ zg}S~ix4*4jA#F2)c=ue6D!eb R^ua>JHS~Fip2&ZD{{fpEDQ^G( delta 2651 zcmY*bc{tPy8=Yas60(g9BZRSUpQ0&^LN3F_7%`}rLU*!co3dsy%94`&kjq$V>`P)4 zCHqdul944T+$>{^ean3J-aqd5-S@BaJnwnlf8O()bDC_EY+lXt9C>JPPs4``1PX-$ zt2~nAtF{xmu)8_c9KtSI=@xK70XKsCsO~E+E$Eu0%m_dTboTqizA-u2o>CIRzKgwe zF~0F-P{cre=k|urRm+X8#}Y1{=`v7wKs!l$M6dO{pLV_YqcWO$Axbt_7M@JYo;SayZv4p8M_4h->8S zZ>jN|;nE$bMkG|RId)L~I&Jtt(!m>h=@$;0S!vb;oaQ=!x=q)4gAG(Peqv zBwBt@_^|F+Oe-3M0pW>iRtuA^c4U0+c$c8kg}$++Y(I0!e-25LcSAGTQ7_Q zrEHV^^<-iPnp)b_hO6(i8x#xOh-v+yQ{YFMx@?g&sz63_)W(clha0`ah!a7r)x`!i zl!|5Vy-YQiY5vqDhnRZE!%sJUd*fvttPl*1Q;<^5UAn~-knH6;K+d8bOX4`BBQ>5{zM~FwZ+Rj2DRhkpQ+6gIpqBSvJ*u@Z+I`hhI3gMrZTeLSG`j zM5jqiS(pzj)%f-3kMS}v3;bf$rNlq#)!;!d0PbK>criN|1)D8s&5px6gmwa{#@?I_o)^Xb@-W&s`&I6{JMajY}_e-R;?)Gbx2c%8Ri^M6uRAg>@S>}k^4y>r4p~2ksp2TwZ4x{T zT9TEl>3HI`+}N{XUK$xTA5e#k6dB?1y{(C#jL&rm)Cll`TmuF={__L8EIhq-3 zdVQT_(vCt1QddKZD2Ia|Ov4ZAh_zhxcO6?uLT+6;liDeF@}4o%GsG>IVP&Y6*Sz9Y zB+5d1bSG)~&T%7Yu|kA7(OuwcaHiI_&0hYA7{pE1h#agliQ027%)Ffb3(6+ygF&K-)2O zvPUmw5T%Bd)eN+VTo-DT8tMid{WmV$ZcX_$AVi9A(et^b@@cih747XCaQ&FBWPjw% zhuhW7wp9b?t)%IHY}~ObHU^!@g!w%#mTZ`YT~@aUD)v&pR)BL6;Wn*jXS&$q4DJA5 zBViDGHxcDZQ>VCZG9tzsrB|5}mLqMi$ok(uEE{S*x>yhRgUaN-!pJY`p{r=6CN@KukG+z`Rm z(sDbhSjS*Fdt&lu(`Bg2)P--Um(F)h+JMgxeDI!_b;U4w8O`VIsxZ8?R2Ni!(pgD# zq&#GQLdTEHp zQS~nM%J3AirkUP+#wL4)3goGG!ew8GRl>B8>SmVAjc%6(u^wNMN!gr}p8SQWQ9Jq* zK1@cDbR?q(-qoY2CiPhrQ~T++vB6+@DkU(|+iGop5mHzFOFX3ASGajTg=r#xmof9! zI8CJA0EgwT!umR0Gt(kiWQqAg1v?I@+fTpqLpIroc9-u$qQwsy$R zWy1Lf!#ZoP4e@)~Z@uEy?cGYxiz*j&_gmJ!E9@%Ymh9-!**A4fuf6jy$fYXlUS_@9 zrcgMxAliam$@wsfBcom$5xukZ{jHlNnWwDSwx@eL*FRayhSCXV)|K(6w&vM^bM3&= zoLA~Vv})~P=mzN^HkKAXpR3x^{bFYI{STwD>;vXt+PA@`LDntNT=cxrlY(Y^06b*Z zK_m=N`#qGt;IKRw052~9=B%lmiYsgtBZnxtm{$8wG3*w&uJIWl+A-S1T_XuYy~VnIjqI`7I^i z+k1Ke7Tj|zLd1M~FpXAuGo$GD&HqG1Qrv1X3md$gN^UvIV6%_dyamr!cb|5_`>y6{ zfYsZ@pChifmrd-2vMfbvm^!R{)0MY(AA^r7tTz9^elL6fVPz#jzXw3Yn|@=wkMsLM z{pi6SMRUGKN|I1Xg(4g=415|EU;-eK9wz6w*I;fDi3b8eB|nUQ|HQA{|KHNam8`)5 zfGSc8@$dBf)5rdq*ML9L4D1HHK`P7q&&xm{p??^EffWg02YH6ePYO7xe1R)U8n8kj zfedAJu4n{cEu#jo{({E=go*;<@4tdUpqqb&wdlW&`~o