From d9009484a6d8aba5ddb4afc5737bf09097e8155f Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Mon, 3 Feb 2014 20:56:43 +0000 Subject: [PATCH] Bug 55923: Fix compare/sorting of nodes in exported XML git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1564050 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xssf/extractor/XSSFExportToXml.java | 12 +- .../xssf/extractor/TestXSSFExportToXML.java | 211 +++++++++++++++++- test-data/spreadsheet/55923.xlsx | Bin 0 -> 10322 bytes 3 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 test-data/spreadsheet/55923.xlsx diff --git a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java index 543b243c5e..e0ee1ef882 100644 --- a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java +++ b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java @@ -409,11 +409,8 @@ public class XSSFExportToXml implements Comparator{ */ @Override public int compare(String leftXpath, String rightXpath) { - - int result = 0; Node xmlSchema = map.getSchema(); - String[] leftTokens = leftXpath.split("/"); String[] rightTokens = rightXpath.split("/"); @@ -421,15 +418,12 @@ public class XSSFExportToXml implements Comparator{ Node localComplexTypeRootNode = xmlSchema; - for(int i =1;i { int rightIndex = indexOfElementInComplexType(rightElementName,localComplexTypeRootNode); if (leftIndex!=-1 && rightIndex!=-1) { if ( leftIndex < rightIndex) { - result = -1; + return -1; }if ( leftIndex > rightIndex) { - result = 1; + return 1; } } else { // NOTE: the xpath doesn't match correctly in the schema @@ -447,7 +441,7 @@ public class XSSFExportToXml implements Comparator{ } } - return result; + return 0; } private int indexOfElementInComplexType(String elementName,Node complexType) { diff --git a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java index 0bdef92863..4e112d268f 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java @@ -20,6 +20,7 @@ package org.apache.poi.xssf.extractor; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,8 +31,12 @@ import javax.xml.parsers.ParserConfigurationException; import junit.framework.TestCase; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.model.MapInfo; +import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFMap; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; @@ -47,6 +52,7 @@ public final class TestXSSFExportToXML extends TestCase { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings.xlsx"); + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (!(p instanceof MapInfo)) { @@ -82,7 +88,10 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals("aa", crediti); parseXML(xml); - } + + found = true; + } + assertTrue(found); } public void testExportToXMLInverseOrder() throws Exception { @@ -92,6 +101,7 @@ public final class TestXSSFExportToXML extends TestCase { MapInfo mapInfo = null; + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (!(p instanceof MapInfo)) { @@ -127,7 +137,10 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals("ro", crediti); parseXML(xml); - } + + found = true; + } + assertTrue(found); } public void testXPathOrdering() { @@ -137,6 +150,7 @@ public final class TestXSSFExportToXML extends TestCase { MapInfo mapInfo = null; + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (p instanceof MapInfo) { @@ -148,7 +162,10 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals(1, exporter.compare("/CORSO/DOCENTE", "/CORSO/NOME")); assertEquals(-1, exporter.compare("/CORSO/NOME", "/CORSO/DOCENTE")); } - } + + found = true; + } + assertTrue(found); } public void testMultiTable() throws Exception { @@ -156,6 +173,7 @@ public final class TestXSSFExportToXML extends TestCase { XSSFWorkbook wb = XSSFTestDataSamples .openSampleWorkbook("CustomXMLMappings-complex-type.xlsx"); + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (p instanceof MapInfo) { @@ -187,7 +205,10 @@ public final class TestXSSFExportToXML extends TestCase { assertTrue(matcher.find()); } } - } + + found = true; + } + assertTrue(found); } public void test55850ComplexXmlExport() throws Exception { @@ -195,6 +216,7 @@ public final class TestXSSFExportToXML extends TestCase { XSSFWorkbook wb = XSSFTestDataSamples .openSampleWorkbook("55850.xlsx"); + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (!(p instanceof MapInfo)) { @@ -227,12 +249,16 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals("19", chf); parseXML(xmlData); + + found = true; } + assertTrue(found); } public void testFormulaCells_Bugzilla_55927() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55927.xlsx"); + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (!(p instanceof MapInfo)) { @@ -256,12 +282,16 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals("2012-01-13", date); parseXML(xmlData); + + found = true; } + assertTrue(found); } public void testFormulaCells_Bugzilla_55926() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55926.xlsx"); + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (!(p instanceof MapInfo)) { @@ -289,7 +319,10 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals("5.1", doubleValue); parseXML(xmlData); + + found = true; } + assertTrue(found); } @Test @@ -297,6 +330,7 @@ public final class TestXSSFExportToXML extends TestCase { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55924.xlsx"); + boolean found = false; for (POIXMLDocumentPart p : wb.getRelations()) { if (!(p instanceof MapInfo)) { @@ -321,9 +355,88 @@ public final class TestXSSFExportToXML extends TestCase { assertEquals("1",euro); parseXML(xmlData); + + found = true; } + assertTrue(found); } + public void testXmlExportCompare_Bug_55923() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); + + boolean found = false; + for (POIXMLDocumentPart p : wb.getRelations()) { + + if (!(p instanceof MapInfo)) { + continue; + } + MapInfo mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(4); + + assertNotNull("XSSFMap is null", map); + + XSSFExportToXml exporter = new XSSFExportToXml(map); + assertEquals(0, exporter.compare("", "")); + assertEquals(0, exporter.compare("/", "/")); + assertEquals(0, exporter.compare("//", "//")); + assertEquals(0, exporter.compare("/a/", "/b/")); + + assertEquals(-1, exporter.compare("/ns1:Entry/ns1:A/ns1:B/ns1:C/ns1:E/ns1:EUR", + "/ns1:Entry/ns1:A/ns1:B/ns1:C/ns1:E/ns1:CHF")); + + found = true; + } + assertTrue(found); + } + + public void testXmlExportSchemaOrderingBug_Bugzilla_55923() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); + + boolean found = false; + for (POIXMLDocumentPart p : wb.getRelations()) { + + if (!(p instanceof MapInfo)) { + continue; + } + MapInfo mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(4); + + assertNotNull("XSSFMap is null", map); + + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, true); + String xmlData = os.toString("UTF-8"); + + assertNotNull(xmlData); + assertFalse(xmlData.equals("")); + + String a = xmlData.split("")[1].split("")[0].trim(); + String a_b = a.split("")[1].split("")[0].trim(); + String a_b_c = a_b.split("")[1].split("")[0].trim(); + String a_b_c_e = a_b_c.split("")[1].split("")[0].trim(); + String a_b_c_e_euro = a_b_c_e.split("")[1].split("")[0].trim(); + String a_b_c_e_chf = a_b_c_e.split("")[1].split("")[0].trim(); + + assertEquals("1",a_b_c_e_euro); + assertEquals("2",a_b_c_e_chf); + + String a_b_d = a_b.split("")[1].split("")[0].trim(); + String a_b_d_e = a_b_d.split("")[1].split("")[0].trim(); + + String a_b_d_e_euro = a_b_d_e.split("")[1].split("")[0].trim(); + String a_b_d_e_chf = a_b_d_e.split("")[1].split("")[0].trim(); + + assertEquals("3",a_b_d_e_euro); + assertEquals("4",a_b_d_e_chf); + + found = true; + } + assertTrue(found); + } + private void parseXML(String xmlData) throws IOException, SAXException, ParserConfigurationException { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(true); @@ -342,4 +455,94 @@ public final class TestXSSFExportToXML extends TestCase { return null; } } + + public void testExportDataTypes() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); + + Sheet sheet = wb.getSheetAt(0); + Row row = sheet.getRow(0); + + Cell cString = row.createCell(0); + cString.setCellValue("somestring"); + cString.setCellType(XSSFCell.CELL_TYPE_STRING); + + Cell cBoolean = row.createCell(1); + cBoolean.setCellValue(true); + cBoolean.setCellType(XSSFCell.CELL_TYPE_BOOLEAN); + + Cell cError = row.createCell(2); + cError.setCellType(XSSFCell.CELL_TYPE_ERROR); + + Cell cFormulaString = row.createCell(3); + cFormulaString.setCellFormula("A1"); + cFormulaString.setCellType(XSSFCell.CELL_TYPE_FORMULA); + + Cell cFormulaNumeric = row.createCell(4); + cFormulaNumeric.setCellFormula("F1"); + cFormulaNumeric.setCellType(XSSFCell.CELL_TYPE_FORMULA); + + Cell cNumeric = row.createCell(5); + cNumeric.setCellValue(1.2); + cNumeric.setCellType(XSSFCell.CELL_TYPE_NUMERIC); + + Cell cDate = row.createCell(6); + cDate.setCellValue(new Date()); + cDate.setCellType(XSSFCell.CELL_TYPE_NUMERIC); + + boolean found = false; + for (POIXMLDocumentPart p : wb.getRelations()) { + + if (!(p instanceof MapInfo)) { + continue; + } + MapInfo mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(4); + + assertNotNull("XSSFMap is null", map); + + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, true); + String xmlData = os.toString("UTF-8"); + + assertNotNull(xmlData); + assertFalse(xmlData.equals("")); + + parseXML(xmlData); + + found = true; + } + assertTrue(found); + } + + public void testValidateFalse() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55923.xlsx"); + + boolean found = false; + for (POIXMLDocumentPart p : wb.getRelations()) { + + if (!(p instanceof MapInfo)) { + continue; + } + MapInfo mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(4); + + assertNotNull("XSSFMap is null", map); + + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, false); + String xmlData = os.toString("UTF-8"); + + assertNotNull(xmlData); + assertFalse(xmlData.equals("")); + + parseXML(xmlData); + + found = true; + } + assertTrue(found); + } } diff --git a/test-data/spreadsheet/55923.xlsx b/test-data/spreadsheet/55923.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..fbcb7bdcfca1557ed0cbd82e1b0622a1a2ac9ae1 GIT binary patch literal 10322 zcmeHtWn5Hi*Z$Dm-6AO=Lnt6fql5#DAPrI@-Q8V+8L-K(r0Du4{ z06+%7M%9zGw{wBmxjfbIbbvS;-1e}wxsi*G%9#y7MLz%E?LSxpy&53XJaTrD01P${9Hc))07&P%njRQUE8g1hFfRH;XS$>czZ>! zyL5y%E6}6gh6)U)H>qY0d%rXyE+C$w{WOUiM}Rf1b z{4k(s>C21Sbau;Krmv0MySZ}4*2>TuzNCLO-I7ngH*1NSvRrmUK}T9>nW)Hgtqoh3HM+nD+D#m&9QdtO&^ zo!d<7*hg1rvHgb+NsVLh1MYSO*l42i`7GOidMIS1ZCNjLL3@PlOw82QYwE$$IRr#4 z+dm(>((my-mo2fV?wq~H8hhw)rcld1rcoFZ@Bs@$jLy0`n1SW{;(Ca;ANmV4Dz&!` zhij+7d`P3ax$$?Dm2P2AvWw3uygVo9w{_1YcPIb-C?SEk;f|a?IZ*(NXc}1@MN`LQg3!=#ZrTb z-h@x3^*yI_rJ#xTxRpm)mL^fQ$xU-h@=n?qdSk7bS(~RGFld^|IHIU3DvI_!^qj>+ zh(VPcZ-2`vKOP9~Hntc{q$z@YfiB+l8N=WEz3T`EAF2zdL`4N7))`PK1|YuxM_-($v&m8dURDQ;&@XzazJ7JP<-yW zv()Bh;O#7n;?t%+@rA;WuOPEiV|Y7}#?#+=$-Jc69yKz*fzbf~S^zeRhYjB!j^b|b zWNl(^Z~Y^~{g2b2Ak!T3+5haV5!z$b3M7{Q7J3<+;>$a2IsEvhxjL?ni~0}|@xiII zjiSX!_*L^q0w+}XOo|P z6QuCZ1$q|~fbdn2?klyJNYMZv#Q={Z9Oi8rA6Agpe$eX_TNX}86Q~(Q>3jpBBL)6(qNEw z^;e6EP5`TZ1QK`7fzAm*bNPBS=-70TU>3d(xscWOv)Z!bw{2JFr@g09O^)D&4VxF= zHfLJ&%43q)v*sJC*z@-}bI~8!&)irS-Yf{?vS3nzZ5j~^L ztcKA?fMd4&hkf5-E7fX9j*w0Gi-@94D)84Y4?&TX65>h}QUxwS`L1Fbg@4=lI( zItNp#wyKs&Sx9jQ*DB!0eJ)2)9OCG0&4JD^9w^(eWKHXv~bSB}zVY8i^ zyhYOuJ@8D(9&MczEb^rx&f3!%8JwhO7AAhP&cc7;W}i{a->gA3UJqEY%K>gC&*WgBaCgz2x*mu<*M$_b<%T)Mek>Q()IJP;=NwooL8Eu{Ow zV}Fi^iv`3M!uNB}|HI3-bX4u9?~=C=O}O81vA5-@rNeT4K2e!g2BK@T3um-UeW|UU zD>bC(OE(ChM}}bg4nPS45ix)3Atzw|m%yG962Ee`iP1kEfS3mR3?;}h$mwuqtDnr3Z1A$v)u6Z z7RZs1mIVz>cSn4qZtPInK*@>>WCx{FV2Wjq3CZ+9l5t!jbf(cHO#OsdiJ;-sFLBwY z3`fr!yT?XxXCG1V*O=HE8_b;#xvmH8yL{dwhjW_iZjS3|xjCI-ei#ubAK!HKu1U{C zF`B~ymGQ{GI=*~RmN>{0u(c?#ZC3*>YdA%{IA%-L%TOsCRDx8>5qy#uOWva!edg=^ z8BQ11F8p%uxX-O_+GP*ZZ2vQx&y`ZCj{80)xd5$d^1xKig`d*>@JAxkyrK30pS)Ke zsW>CT#NP`=;lz%XXh-e&-aZ2iNQF_nh=0Xl7UUbTa69HfkjZC&J*j?5rEIf4?NDRf z5e7(O8UcjIk>Gi~$@IV4_^y7m@jY=dyg!EN(&c<@aKDv_m)HF3?U>+&kI8d>C#&O3 zd6JoahX?yq%N{yyS7)spE$8Q+qQn9g%!5&US?4EPT~Ti&E-o56+4gJ$AIhJ`J&?=Q zTf%18!*&r2b7bRSacrh&B~~}bAPjWFSeBrJowaps zV1ctrXS@XXCSuNTTZzCy_K-{4LnJELX~jgdjlsxAyQ3k>w6ms32la^{o>J$@Oq__x zjvzfZeOwABZ1ofxN4mWO-k5NWJquEn0IxC1GdxlT3BXu5;wYosEYbZ^lh^Uf-E6Ae zsa!?4hrPn--zz9!_bP=jk*=id-^;v3pA=8O5J5rUM^;+#=o4K3R9;_|YtKcB`e74) ze3i{-Ta+`(bU!rNCN5V5W7@;pk2&x0=6RAi8_MnMSqis-ABQmvi13(5#;Ie2qfg#( z1@Afv%N~iHqI7c4_$HHf&J7qwurbd!JHRSjbIR{S%0r{U`D!o@PW}&?@ zn0!XQQMcS^ELyak)%O>A(1 zzaZ9rhtuiznYK?XCBCw@;TXI^xfmMd5|6*10+Sf>;zJZYONX|1C*0H}qQgJi@_0Uq z`$hNF7M+7wRO0ik4eklZtT!1SgrsqgR81^3hUYdrN7NvA+WO8IE#raX&iV&d^jDMljS0 z#^u*fFx)-vT=M^|dHx^9Mex7l^8bHaq)kTV2ax`fiuP~)*wj%s^n~WaCSqy(D!)BTUz6)V-*ai*@L4# z@yqFf>JwJuD7wUqkl%#6{|LjKf8)Gwld12iKQfkaoQ)vUT zvF2)kcjvjO)ejLtL#!Ho!S8urb+1@j`-`e@?{)8ORKWodUHwvTzjC)=6-$gI zUoxxL3;x{uUT7DnQdWo2X08$YYpkP%StZ}`eit>n@TCm&kr9&%0{|fSBWgOkc-lam zf4C$}W6nMeNbHyO_6mOBZDORDEGJbXBwsS}Br!qJ+w}u+>1CkJa3}~d=Zz^tdTg9= z613a5y4#HKVQ;>#6lj=Isb7j7J`7+fa58Xs!gh+doyG&KiYW=gEz=Itd$wi4|5*Ss3kP}}-ZVWl)&$x`+kY7t{A&1cn#j6+PrgD)1P%EA~TxjhN1 zTf9ANI;2uci;ua@!R2(zQrrP26q@}?R~o5v2XG1Q7u$SCG8l3)kA_6XW$2cqXl{(j zlPA6IKQ$4dkSK;FMAZ^KkdX3$8&_8Jhf?=EY$in?qBe8rEyAZ4nrNsEyN7G{I(+@$ zGQogmDs1q40kDo+uy2@#%D(jF^cJ)uD2XtF){xKwt``8uo0JFMtlOfhWa?JGU2d~OY21UyS2v1 z+spI1h+iDuvOAuYoQB|e#S?UFB7kUHNnG}6O6@gw-~7z3hY5E0wz`md*~zl)$L=uv z)dqqg4iOX*ds~0Mlho(y(NO~&YYM1b$R5Gq@3hYS+azI+^$}Yc8H_BErbvP;xS83T zsyW#^IP;m>J3)S!BC!wnH|BVJd_ z?FkrLq?TpN|3ZJllgl_C4;`P07dig)KqxIC~-A!W{Txzrfhp1iQz&PVmSA<3K- zF_!QWUD+5nqrpaz`&}Ngp0@L|BLvhOaLh~??=mRnZr>ep(;ngMloyS%-i`)z?dg)Q zNQI+Ysz@D3JE^n-O@lFiA5>O?bVG3f=~Jb4O2yu zTpBvur#dsLFqql~n`)edYHjIiESXwYIZi!2zYS;+J*B5VO$zEMlU7o2?JC@=lR9_D}k#x={lmL0TX!G98ls z5eQA~?d%|?E|&IoKcnGDyfm^djx2F1e1X5Z@5qVG427kY=*3y+Ko%#4S*^rl#0lTl znbYEc<@g4cnR@ImtD~0TL>pu*-vX8D`<>Jj?$&2ck=CXi&j@m=gr(D<8sj`P)7GpL z4keJYNYpoF!(SkM5AC2Tn{-AROG?*srFxF4BEOqS0m`$NVlf>)>${zuQ~~a4@>)IiJn!Lhm&#m8 z^h%btL%2~4YQ|b2yp>J2%c|cjZXl~=|5PA<`X22qqyj%770C350$q$vkeP(<=Rw`l z&fEqf3$d|r{z)s|CV;GwU52pT;0vrg#0Csu1ihCJ(yQ&E&I>hYlCwswO`qJcxVnIn zcc9*fOv`^7+Z^}eQ8mZ1ZsRyp6MGyNLrSa=%56P$Yr<|!>)Q>P5e=(FYzlh*4?GQx zDVUnnJ~iwQO@oZ+aB)kh(;}fcMFSf^CjwHf*^Ih-2SdAN7UCNM8WDrEqACeumad?< zV+MHxF6!Vt+?DYgxDP|*m)My+C5`V)*9uD_R8oT`)GsPKIc(o4Cr+zD+)vC$G0rB-|O6p%v4 z4uqZ?UsyxO7VXteDQF;~_>eV?msbKw(}a*aKhFJf@iUaJT0^&fo3HJIXlnA1S~f*$ znfR}P(%8Y_zi|?2c7JZ^|6;9Ygkd``(qWuoQkq~GgO_$Cz;RlTd$>|fM}T!q;p`~U z2Ig41Q$^R6b$T2LZTsAOM<-Uo!W{2z)-sQ5seb-;xsmE}Y|kza9(pb6A`j(OK;X=$ zD0`DdgYZnDFdp3rwTiGJJx4~3VBQcNsI7+8s$O==8H2lP8O5yV@oX+z^QS}W7;hOp zW7kPv+Nwdv(c!CCnksC~d-{y;v3cazu^$!dLEp_6%6)Om7{ukAHI%SgHgLED`Q{z^ zV6T|-<}u}H!?=qAMM@VL>fg@Rgs8k1p+gM;?(bptmZ6N}%CZf8zEQ8D#pOO}?gT z@0HF56!JwRt1ko^FEKdQM%qg(BUl+=?JfE7E?%X^YVo^7xi!{e6`au9q%q41?EiSo%hNBb^FmD%tveE>gT+!@fsg;gsOLx*HrqQ+ zrSq$7R`XmRf014c$@NF0ElSi6oEK!Y>Nfp1=e$b|$32q}Ak-BQRzLWMxSY?0Y;d0}_gAtqihPwGA2| zp(p7C4Rb=&11-i;Y`Mn_bERH!j3?>N`?648)G>r)=K7EB*B?fJg31f{cPoqjbmo5@ zezVRp|+ ezxnf1{t~>^lrfMV^rPcV1Xw~kp*HJ}tN#a_`)+jr literal 0 HcmV?d00001 -- 2.39.5