close resources in tests fix gradle warnings git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1892683 13f79535-47bb-0310-9956-ffa450edef68tags/REL_5_1_0
// make XML test-results available for Jenkins CI | // make XML test-results available for Jenkins CI | ||||
useJUnitPlatform() | useJUnitPlatform() | ||||
reports { | reports { | ||||
junitXml.enabled = true | |||||
junitXml.required = true | |||||
} | } | ||||
// Exclude some tests that are not actually tests or do not run cleanly on purpose | // Exclude some tests that are not actually tests or do not run cleanly on purpose | ||||
"-Dversion.id=${project.version}", | "-Dversion.id=${project.version}", | ||||
'-ea', | '-ea', | ||||
'-Djunit.jupiter.execution.parallel.config.strategy=fixed', | '-Djunit.jupiter.execution.parallel.config.strategy=fixed', | ||||
'-Djunit.jupiter.execution.parallel.config.fixed.parallelism=3' | |||||
'-Djunit.jupiter.execution.parallel.config.fixed.parallelism=2' | |||||
// -Xjit:verbose={compileStart|compileEnd},vlog=build/jit.log${no.jit.sherlock} ... if ${isIBMVM} | // -Xjit:verbose={compileStart|compileEnd},vlog=build/jit.log${no.jit.sherlock} ... if ${isIBMVM} | ||||
] | ] | ||||
jacocoTestReport { | jacocoTestReport { | ||||
reports { | reports { | ||||
xml.enabled true | |||||
xml.required = true | |||||
} | } | ||||
} | } | ||||
spotbugs { | spotbugs { | ||||
ignoreFailures = true | ignoreFailures = true | ||||
showStackTraces = false | |||||
} | } | ||||
} | } | ||||
task zipJavadocs(type: Zip, dependsOn: allJavaDoc) { | task zipJavadocs(type: Zip, dependsOn: allJavaDoc) { | ||||
from('build/docs/javadoc/') | from('build/docs/javadoc/') | ||||
destinationDir = file('build/dist') | |||||
destinationDirectory = file('build/dist') | |||||
archiveBaseName = 'poi' | archiveBaseName = 'poi' | ||||
archiveVersion = subprojects[0].version | archiveVersion = subprojects[0].version | ||||
archiveAppendix = 'javadoc' | archiveAppendix = 'javadoc' |
} | } | ||||
jar { | jar { | ||||
dependsOn cacheJava9 | |||||
destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") | destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") | ||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) { | if (JavaVersion.current() == JavaVersion.VERSION_1_8) { |
} | } | ||||
jar { | jar { | ||||
dependsOn cacheJava9 | |||||
destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") | destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}") | ||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) { | if (JavaVersion.current() == JavaVersion.VERSION_1_8) { | ||||
// Create a separate jar for test-code to depend on it in other projects | // Create a separate jar for test-code to depend on it in other projects | ||||
// See http://stackoverflow.com/questions/5144325/gradle-test-dependency | // See http://stackoverflow.com/questions/5144325/gradle-test-dependency | ||||
task testJar(type: Jar, dependsOn: testClasses) { | |||||
task testJar(type: Jar, dependsOn: [ testClasses, cacheTest9 ] ) { | |||||
destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") | destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") | ||||
classifier 'tests' | classifier 'tests' |
// Create a separate jar for test-code to depend on it in other projects | // Create a separate jar for test-code to depend on it in other projects | ||||
// See http://stackoverflow.com/questions/5144325/gradle-test-dependency | // See http://stackoverflow.com/questions/5144325/gradle-test-dependency | ||||
task testJar(type: Jar, dependsOn: testClasses) { | |||||
task testJar(type: Jar, dependsOn: [ testClasses, cacheTest9 ] ) { | |||||
destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") | destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") | ||||
classifier 'tests' | classifier 'tests' |
* types of files/exceptions, e.g. old file formats. | * types of files/exceptions, e.g. old file formats. | ||||
* | * | ||||
*/ | */ | ||||
@SuppressWarnings({"java:S2187", "unused"}) | |||||
public class BaseIntegrationTest { | public class BaseIntegrationTest { | ||||
private final File rootDir; | private final File rootDir; | ||||
private final String file; | private final String file; |
package org.apache.poi.stress; | package org.apache.poi.stress; | ||||
import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM; | import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM; | ||||
import static org.apache.poi.xssf.XSSFTestDataSamples.getSampleFile; | |||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | |||||
import static org.junit.jupiter.api.Assertions.assertFalse; | import static org.junit.jupiter.api.Assertions.assertFalse; | ||||
import static org.junit.jupiter.api.Assertions.assertNotNull; | import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||
import static org.junit.jupiter.api.Assertions.assertTrue; | import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
} | } | ||||
@Test | @Test | ||||
void testExtracting() throws Exception { | |||||
handleExtracting(new File("test-data/spreadsheet/ref-56737.xlsx")); | |||||
void testExtracting() { | |||||
File testFile = getSampleFile("ref-56737.xlsx"); | |||||
assertDoesNotThrow(() -> handleExtracting(testFile)); | |||||
} | } | ||||
@Test | @Test | ||||
void testAdditional() throws Exception { | |||||
handleAdditional(new File("test-data/spreadsheet/poc-xmlbomb.xlsx")); | |||||
void testAdditional() { | |||||
File testFile = getSampleFile("poc-xmlbomb.xlsx"); | |||||
assertDoesNotThrow(() -> handleAdditional(testFile)); | |||||
} | } | ||||
} | } |
} | } | ||||
} | } | ||||
spotbugsTest.enabled = false | |||||
spotbugsMain.enabled = false |
} | } | ||||
public byte[] sign() throws IOException, GeneralSecurityException { | public byte[] sign() throws IOException, GeneralSecurityException { | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
bos.write(getHashMagic()); | |||||
bos.write(md.digest()); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
bos.write(getHashMagic()); | |||||
bos.write(md.digest()); | |||||
final Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa | |||||
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding"); | |||||
return cipher.doFinal(bos.toByteArray()); | |||||
final Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa | |||||
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding"); | |||||
return cipher.doFinal(bos.toByteArray()); | |||||
} | |||||
} | } | ||||
static boolean isMSCapi(final PrivateKey key) { | static boolean isMSCapi(final PrivateKey key) { | ||||
// in an earlier release the hashMagic (aka DigestAlgorithmIdentifier) contained only | // in an earlier release the hashMagic (aka DigestAlgorithmIdentifier) contained only | ||||
// an object identifier, but to conform with the header generated by the | // an object identifier, but to conform with the header generated by the | ||||
// javax-signature API, the empty <associated parameters> are also included | // javax-signature API, the empty <associated parameters> are also included | ||||
try { | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
final byte[] oidBytes = new Oid(algo.rsaOid).getDER(); | final byte[] oidBytes = new Oid(algo.rsaOid).getDER(); | ||||
final UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
bos.write(0x30); | bos.write(0x30); | ||||
bos.write(algo.hashSize+oidBytes.length+6); | bos.write(algo.hashSize+oidBytes.length+6); | ||||
bos.write(0x30); | bos.write(0x30); |
// although xmlsec has an option to ignore line breaks, currently this | // although xmlsec has an option to ignore line breaks, currently this | ||||
// only affects .rels files, so we only modify these | // only affects .rels files, so we only modify these | ||||
// http://stackoverflow.com/questions/4728300 | // http://stackoverflow.com/questions/4728300 | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
for (int ch; (ch = dataStream.read()) != -1; ) { | |||||
if (ch == 10 || ch == 13) continue; | |||||
bos.write(ch); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
for (int ch; (ch = dataStream.read()) != -1; ) { | |||||
if (ch == 10 || ch == 13) continue; | |||||
bos.write(ch); | |||||
} | |||||
dataStream = bos.toInputStream(); | |||||
} | } | ||||
dataStream = bos.toInputStream(); | |||||
} | } | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new URIReferenceException("I/O error: " + e.getMessage(), e); | throw new URIReferenceException("I/O error: " + e.getMessage(), e); |
} | } | ||||
public static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) { | public static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) { | ||||
UnsynchronizedByteArrayOutputStream c14nValue = new UnsynchronizedByteArrayOutputStream(); | |||||
try { | |||||
try (UnsynchronizedByteArrayOutputStream c14nValue = new UnsynchronizedByteArrayOutputStream()) { | |||||
for (Node node : nodeList) { | for (Node node : nodeList) { | ||||
/* | /* | ||||
* Re-initialize the c14n else the namespaces will get cached | * Re-initialize the c14n else the namespaces will get cached | ||||
Canonicalizer c14n = Canonicalizer.getInstance(c14nAlgoId); | Canonicalizer c14n = Canonicalizer.getInstance(c14nAlgoId); | ||||
c14n.canonicalizeSubtree(node, c14nValue); | c14n.canonicalizeSubtree(node, c14nValue); | ||||
} | } | ||||
return c14nValue.toByteArray(); | |||||
} catch (RuntimeException e) { | } catch (RuntimeException e) { | ||||
throw e; | throw e; | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
throw new RuntimeException("c14n error: " + e.getMessage(), e); | throw new RuntimeException("c14n error: " + e.getMessage(), e); | ||||
} | } | ||||
return c14nValue.toByteArray(); | |||||
} | } | ||||
private BigInteger getCrlNumber(X509CRL crl) { | private BigInteger getCrlNumber(X509CRL crl) { |
* Colour Scale change thresholds | * Colour Scale change thresholds | ||||
*/ | */ | ||||
public class XSSFConditionalFormattingThreshold implements org.apache.poi.ss.usermodel.ConditionalFormattingThreshold { | public class XSSFConditionalFormattingThreshold implements org.apache.poi.ss.usermodel.ConditionalFormattingThreshold { | ||||
private CTCfvo cfvo; | |||||
private final CTCfvo cfvo; | |||||
protected XSSFConditionalFormattingThreshold(CTCfvo cfvo) { | protected XSSFConditionalFormattingThreshold(CTCfvo cfvo) { | ||||
this.cfvo = cfvo; | this.cfvo = cfvo; | ||||
} | } | ||||
protected CTCfvo getCTCfvo() { | protected CTCfvo getCTCfvo() { | ||||
return cfvo; | return cfvo; | ||||
} | } | ||||
@Override | |||||
public RangeType getRangeType() { | public RangeType getRangeType() { | ||||
return RangeType.byName(cfvo.getType().toString()); | return RangeType.byName(cfvo.getType().toString()); | ||||
} | } | ||||
@Override | |||||
public void setRangeType(RangeType type) { | public void setRangeType(RangeType type) { | ||||
STCfvoType.Enum xtype = STCfvoType.Enum.forString(type.name); | STCfvoType.Enum xtype = STCfvoType.Enum.forString(type.name); | ||||
cfvo.setType(xtype); | cfvo.setType(xtype); | ||||
} | } | ||||
@Override | |||||
public String getFormula() { | public String getFormula() { | ||||
if (cfvo.getType() == STCfvoType.FORMULA) { | if (cfvo.getType() == STCfvoType.FORMULA) { | ||||
return cfvo.getVal(); | return cfvo.getVal(); | ||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
@Override | |||||
public void setFormula(String formula) { | public void setFormula(String formula) { | ||||
cfvo.setVal(formula); | cfvo.setVal(formula); | ||||
} | } | ||||
@Override | |||||
public Double getValue() { | public Double getValue() { | ||||
if (cfvo.getType() == STCfvoType.FORMULA || | if (cfvo.getType() == STCfvoType.FORMULA || | ||||
cfvo.getType() == STCfvoType.MIN || | cfvo.getType() == STCfvoType.MIN || | ||||
return null; | return null; | ||||
} | } | ||||
} | } | ||||
@Override | |||||
public void setValue(Double value) { | public void setValue(Double value) { | ||||
if (value == null) { | if (value == null) { | ||||
cfvo.unsetVal(); | cfvo.unsetVal(); |
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STIconSetType; | import org.openxmlformats.schemas.spreadsheetml.x2006.main.STIconSetType; | ||||
/** | /** | ||||
* High level representation for Icon / Multi-State Formatting | |||||
* High level representation for Icon / Multi-State Formatting | |||||
* component of Conditional Formatting settings | * component of Conditional Formatting settings | ||||
*/ | */ | ||||
public class XSSFIconMultiStateFormatting implements IconMultiStateFormatting { | public class XSSFIconMultiStateFormatting implements IconMultiStateFormatting { | ||||
_iconset = iconset; | _iconset = iconset; | ||||
} | } | ||||
@Override | |||||
public IconSet getIconSet() { | public IconSet getIconSet() { | ||||
String set = _iconset.getIconSet().toString(); | String set = _iconset.getIconSet().toString(); | ||||
return IconSet.byName(set); | return IconSet.byName(set); | ||||
} | } | ||||
@Override | |||||
public void setIconSet(IconSet set) { | public void setIconSet(IconSet set) { | ||||
STIconSetType.Enum xIconSet = STIconSetType.Enum.forString(set.name); | STIconSetType.Enum xIconSet = STIconSetType.Enum.forString(set.name); | ||||
_iconset.setIconSet(xIconSet); | _iconset.setIconSet(xIconSet); | ||||
} | } | ||||
@Override | |||||
public boolean isIconOnly() { | public boolean isIconOnly() { | ||||
if (_iconset.isSetShowValue()) | if (_iconset.isSetShowValue()) | ||||
return !_iconset.getShowValue(); | return !_iconset.getShowValue(); | ||||
return false; | return false; | ||||
} | } | ||||
@Override | |||||
public void setIconOnly(boolean only) { | public void setIconOnly(boolean only) { | ||||
_iconset.setShowValue(!only); | _iconset.setShowValue(!only); | ||||
} | } | ||||
@Override | |||||
public boolean isReversed() { | public boolean isReversed() { | ||||
if (_iconset.isSetReverse()) | if (_iconset.isSetReverse()) | ||||
return _iconset.getReverse(); | return _iconset.getReverse(); | ||||
return false; | return false; | ||||
} | } | ||||
@Override | |||||
public void setReversed(boolean reversed) { | public void setReversed(boolean reversed) { | ||||
_iconset.setReverse(reversed); | _iconset.setReverse(reversed); | ||||
} | } | ||||
@Override | |||||
public XSSFConditionalFormattingThreshold[] getThresholds() { | public XSSFConditionalFormattingThreshold[] getThresholds() { | ||||
CTCfvo[] cfvos = _iconset.getCfvoArray(); | CTCfvo[] cfvos = _iconset.getCfvoArray(); | ||||
XSSFConditionalFormattingThreshold[] t = | |||||
XSSFConditionalFormattingThreshold[] t = | |||||
new XSSFConditionalFormattingThreshold[cfvos.length]; | new XSSFConditionalFormattingThreshold[cfvos.length]; | ||||
for (int i=0; i<cfvos.length; i++) { | for (int i=0; i<cfvos.length; i++) { | ||||
t[i] = new XSSFConditionalFormattingThreshold(cfvos[i]); | t[i] = new XSSFConditionalFormattingThreshold(cfvos[i]); | ||||
} | } | ||||
return t; | return t; | ||||
} | } | ||||
@Override | |||||
public void setThresholds(ConditionalFormattingThreshold[] thresholds) { | public void setThresholds(ConditionalFormattingThreshold[] thresholds) { | ||||
CTCfvo[] cfvos = new CTCfvo[thresholds.length]; | CTCfvo[] cfvos = new CTCfvo[thresholds.length]; | ||||
for (int i=0; i<thresholds.length; i++) { | for (int i=0; i<thresholds.length; i++) { | ||||
} | } | ||||
_iconset.setCfvoArray(cfvos); | _iconset.setCfvoArray(cfvos); | ||||
} | } | ||||
@Override | |||||
public XSSFConditionalFormattingThreshold createThreshold() { | public XSSFConditionalFormattingThreshold createThreshold() { | ||||
return new XSSFConditionalFormattingThreshold(_iconset.addNewCfvo()); | return new XSSFConditionalFormattingThreshold(_iconset.addNewCfvo()); | ||||
} | } |
* @throws IOException if an error occurs while reading the data | * @throws IOException if an error occurs while reading the data | ||||
* @throws EncryptedDocumentException If the wrong password is given for a protected file | * @throws EncryptedDocumentException If the wrong password is given for a protected file | ||||
*/ | */ | ||||
@Override | |||||
@SuppressWarnings("resource") | @SuppressWarnings("resource") | ||||
public XSSFWorkbook create(File file, String password, boolean readOnly) throws IOException { | public XSSFWorkbook create(File file, String password, boolean readOnly) throws IOException { | ||||
FileMagic fm = FileMagic.valueOf(file); | FileMagic fm = FileMagic.valueOf(file); |
import org.apache.poi.xssf.model.MapInfo; | import org.apache.poi.xssf.model.MapInfo; | ||||
import org.apache.poi.xssf.usermodel.XSSFMap; | import org.apache.poi.xssf.usermodel.XSSFMap; | ||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | import org.apache.poi.xssf.usermodel.XSSFWorkbook; | ||||
import org.junit.jupiter.api.Disabled; | |||||
import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
import org.xml.sax.SAXParseException; | import org.xml.sax.SAXParseException; | ||||
} | } | ||||
@Test | @Test | ||||
@Disabled(value="Fails, but I don't know if it is ok or not...") | |||||
void testExportToXMLSingleAttributeNamespace() throws Exception { | void testExportToXMLSingleAttributeNamespace() throws Exception { | ||||
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMapping-singleattributenamespace.xlsx")) { | try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMapping-singleattributenamespace.xlsx")) { | ||||
for (XSSFMap map : wb.getCustomXMLMappings()) { | for (XSSFMap map : wb.getCustomXMLMappings()) { | ||||
XSSFExportToXml exporter = new XSSFExportToXml(map); | XSSFExportToXml exporter = new XSSFExportToXml(map); | ||||
UnsynchronizedByteArrayOutputStream os = new UnsynchronizedByteArrayOutputStream(); | UnsynchronizedByteArrayOutputStream os = new UnsynchronizedByteArrayOutputStream(); | ||||
exporter.exportToXML(os, true); | |||||
SAXParseException ex = assertThrows(SAXParseException.class, () -> exporter.exportToXML(os, true)); | |||||
assertEquals("schema_reference: Failed to read schema document 'Schema11', because 'file' access is not allowed due to restriction set by the accessExternalSchema property.", ex.getMessage().trim()); | |||||
} | } | ||||
} | } | ||||
} | } |
import org.junit.jupiter.api.AfterEach; | import org.junit.jupiter.api.AfterEach; | ||||
import org.junit.jupiter.api.Disabled; | import org.junit.jupiter.api.Disabled; | ||||
import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||
import org.junit.jupiter.params.ParameterizedTest; | |||||
import org.junit.jupiter.params.provider.ValueSource; | |||||
public final class TestDeferredSXSSFWorkbook extends BaseTestXWorkbook { | public final class TestDeferredSXSSFWorkbook extends BaseTestXWorkbook { | ||||
@Test | @Test | ||||
void existingWorkbook() throws IOException { | void existingWorkbook() throws IOException { | ||||
XSSFWorkbook xssfWb1 = new XSSFWorkbook(); | |||||
xssfWb1.createSheet("S1"); | |||||
DeferredSXSSFWorkbook wb1 = new DeferredSXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb1); | |||||
assertTrue(wb1.dispose()); | |||||
DeferredSXSSFWorkbook wb2 = new DeferredSXSSFWorkbook(xssfWb2); | |||||
assertEquals(1, wb2.getNumberOfSheets()); | |||||
Sheet sheet = wb2.getStreamingSheetAt(0); | |||||
assertNotNull(sheet); | |||||
assertEquals("S1", sheet.getSheetName()); | |||||
assertTrue(wb2.dispose()); | |||||
xssfWb2.close(); | |||||
xssfWb1.close(); | |||||
wb2.close(); | |||||
wb1.close(); | |||||
try (XSSFWorkbook xssfWb1 = new XSSFWorkbook()) { | |||||
xssfWb1.createSheet("S1"); | |||||
try (DeferredSXSSFWorkbook wb1 = new DeferredSXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb1)) { | |||||
assertTrue(wb1.dispose()); | |||||
try (DeferredSXSSFWorkbook wb2 = new DeferredSXSSFWorkbook(xssfWb2)) { | |||||
assertEquals(1, wb2.getNumberOfSheets()); | |||||
Sheet sheet = wb2.getStreamingSheetAt(0); | |||||
assertNotNull(sheet); | |||||
assertEquals("S1", sheet.getSheetName()); | |||||
assertTrue(wb2.dispose()); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void addToExistingWorkbook() throws IOException { | void addToExistingWorkbook() throws IOException { | ||||
XSSFWorkbook xssfWb1 = new XSSFWorkbook(); | |||||
xssfWb1.createSheet("S1"); | |||||
Sheet sheet = xssfWb1.createSheet("S2"); | |||||
Row row = sheet.createRow(1); | |||||
Cell cell = row.createCell(1); | |||||
cell.setCellValue("value 2_1_1"); | |||||
DeferredSXSSFWorkbook wb1 = new DeferredSXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb1); | |||||
assertTrue(wb1.dispose()); | |||||
xssfWb1.close(); | |||||
DeferredSXSSFWorkbook wb2 = new DeferredSXSSFWorkbook(xssfWb2); | |||||
// Add a row to the existing empty sheet | |||||
DeferredSXSSFSheet ssheet1 = wb2.getStreamingSheetAt(0); | |||||
ssheet1.setRowGenerator((ssxSheet) -> { | |||||
Row row1_1 = ssxSheet.createRow(1); | |||||
Cell cell1_1_1 = row1_1.createCell(1); | |||||
cell1_1_1.setCellValue("value 1_1_1"); | |||||
}); | |||||
// Add a row to the existing non-empty sheet | |||||
DeferredSXSSFSheet ssheet2 = wb2.getStreamingSheetAt(1); | |||||
ssheet2.setRowGenerator((ssxSheet) -> { | |||||
Row row2_2 = ssxSheet.createRow(2); | |||||
Cell cell2_2_1 = row2_2.createCell(1); | |||||
cell2_2_1.setCellValue("value 2_2_1"); | |||||
}); | |||||
// Add a sheet with one row | |||||
DeferredSXSSFSheet ssheet3 = wb2.createSheet("S3"); | |||||
ssheet3.setRowGenerator((ssxSheet) -> { | |||||
Row row3_1 = ssxSheet.createRow(1); | |||||
Cell cell3_1_1 = row3_1.createCell(1); | |||||
cell3_1_1.setCellValue("value 3_1_1"); | |||||
}); | |||||
XSSFWorkbook xssfWb3 = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb2); | |||||
wb2.close(); | |||||
assertEquals(3, xssfWb3.getNumberOfSheets()); | |||||
// Verify sheet 1 | |||||
XSSFSheet sheet1 = xssfWb3.getSheetAt(0); | |||||
assertEquals("S1", sheet1.getSheetName()); | |||||
assertEquals(1, sheet1.getPhysicalNumberOfRows()); | |||||
XSSFRow row1_1 = sheet1.getRow(1); | |||||
assertNotNull(row1_1); | |||||
XSSFCell cell1_1_1 = row1_1.getCell(1); | |||||
assertNotNull(cell1_1_1); | |||||
assertEquals("value 1_1_1", cell1_1_1.getStringCellValue()); | |||||
// Verify sheet 2 | |||||
XSSFSheet sheet2 = xssfWb3.getSheetAt(1); | |||||
assertEquals("S2", sheet2.getSheetName()); | |||||
assertEquals(2, sheet2.getPhysicalNumberOfRows()); | |||||
Row row2_1 = sheet2.getRow(1); | |||||
assertNotNull(row2_1); | |||||
Cell cell2_1_1 = row2_1.getCell(1); | |||||
assertNotNull(cell2_1_1); | |||||
assertEquals("value 2_1_1", cell2_1_1.getStringCellValue()); | |||||
XSSFRow row2_2 = sheet2.getRow(2); | |||||
assertNotNull(row2_2); | |||||
XSSFCell cell2_2_1 = row2_2.getCell(1); | |||||
assertNotNull(cell2_2_1); | |||||
assertEquals("value 2_2_1", cell2_2_1.getStringCellValue()); | |||||
// Verify sheet 3 | |||||
XSSFSheet sheet3 = xssfWb3.getSheetAt(2); | |||||
assertEquals("S3", sheet3.getSheetName()); | |||||
assertEquals(1, sheet3.getPhysicalNumberOfRows()); | |||||
XSSFRow row3_1 = sheet3.getRow(1); | |||||
assertNotNull(row3_1); | |||||
XSSFCell cell3_1_1 = row3_1.getCell(1); | |||||
assertNotNull(cell3_1_1); | |||||
assertEquals("value 3_1_1", cell3_1_1.getStringCellValue()); | |||||
xssfWb2.close(); | |||||
xssfWb3.close(); | |||||
wb1.close(); | |||||
try (XSSFWorkbook xssfWb1 = new XSSFWorkbook()) { | |||||
xssfWb1.createSheet("S1"); | |||||
Sheet sheet = xssfWb1.createSheet("S2"); | |||||
Row row = sheet.createRow(1); | |||||
Cell cell = row.createCell(1); | |||||
cell.setCellValue("value 2_1_1"); | |||||
try (DeferredSXSSFWorkbook wb1 = new DeferredSXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb1)) { | |||||
assertTrue(wb1.dispose()); | |||||
try (DeferredSXSSFWorkbook wb2 = new DeferredSXSSFWorkbook(xssfWb2)) { | |||||
// Add a row to the existing empty sheet | |||||
DeferredSXSSFSheet ssheet1 = wb2.getStreamingSheetAt(0); | |||||
ssheet1.setRowGenerator((ssxSheet) -> { | |||||
Row row1_1 = ssxSheet.createRow(1); | |||||
Cell cell1_1_1 = row1_1.createCell(1); | |||||
cell1_1_1.setCellValue("value 1_1_1"); | |||||
}); | |||||
// Add a row to the existing non-empty sheet | |||||
DeferredSXSSFSheet ssheet2 = wb2.getStreamingSheetAt(1); | |||||
ssheet2.setRowGenerator((ssxSheet) -> { | |||||
Row row2_2 = ssxSheet.createRow(2); | |||||
Cell cell2_2_1 = row2_2.createCell(1); | |||||
cell2_2_1.setCellValue("value 2_2_1"); | |||||
}); | |||||
// Add a sheet with one row | |||||
DeferredSXSSFSheet ssheet3 = wb2.createSheet("S3"); | |||||
ssheet3.setRowGenerator((ssxSheet) -> { | |||||
Row row3_1 = ssxSheet.createRow(1); | |||||
Cell cell3_1_1 = row3_1.createCell(1); | |||||
cell3_1_1.setCellValue("value 3_1_1"); | |||||
}); | |||||
try (XSSFWorkbook xssfWb3 = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb2)) { | |||||
assertEquals(3, xssfWb3.getNumberOfSheets()); | |||||
// Verify sheet 1 | |||||
XSSFSheet sheet1 = xssfWb3.getSheetAt(0); | |||||
assertEquals("S1", sheet1.getSheetName()); | |||||
assertEquals(1, sheet1.getPhysicalNumberOfRows()); | |||||
XSSFRow row1_1 = sheet1.getRow(1); | |||||
assertNotNull(row1_1); | |||||
XSSFCell cell1_1_1 = row1_1.getCell(1); | |||||
assertNotNull(cell1_1_1); | |||||
assertEquals("value 1_1_1", cell1_1_1.getStringCellValue()); | |||||
// Verify sheet 2 | |||||
XSSFSheet sheet2 = xssfWb3.getSheetAt(1); | |||||
assertEquals("S2", sheet2.getSheetName()); | |||||
assertEquals(2, sheet2.getPhysicalNumberOfRows()); | |||||
Row row2_1 = sheet2.getRow(1); | |||||
assertNotNull(row2_1); | |||||
Cell cell2_1_1 = row2_1.getCell(1); | |||||
assertNotNull(cell2_1_1); | |||||
assertEquals("value 2_1_1", cell2_1_1.getStringCellValue()); | |||||
XSSFRow row2_2 = sheet2.getRow(2); | |||||
assertNotNull(row2_2); | |||||
XSSFCell cell2_2_1 = row2_2.getCell(1); | |||||
assertNotNull(cell2_2_1); | |||||
assertEquals("value 2_2_1", cell2_2_1.getStringCellValue()); | |||||
// Verify sheet 3 | |||||
XSSFSheet sheet3 = xssfWb3.getSheetAt(2); | |||||
assertEquals("S3", sheet3.getSheetName()); | |||||
assertEquals(1, sheet3.getPhysicalNumberOfRows()); | |||||
XSSFRow row3_1 = sheet3.getRow(1); | |||||
assertNotNull(row3_1); | |||||
XSSFCell cell3_1_1 = row3_1.getCell(1); | |||||
assertNotNull(cell3_1_1); | |||||
assertEquals("value 3_1_1", cell3_1_1.getStringCellValue()); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void sheetdataWriter() throws IOException { | void sheetdataWriter() throws IOException { | ||||
DeferredSXSSFWorkbook wb = new DeferredSXSSFWorkbook(); | |||||
SXSSFSheet sh = wb.createSheet(); | |||||
assertSame(sh.getClass(), DeferredSXSSFSheet.class); | |||||
SheetDataWriter wr = sh.getSheetDataWriter(); | |||||
assertNull(wr); | |||||
wb.close(); | |||||
try (DeferredSXSSFWorkbook wb = new DeferredSXSSFWorkbook()) { | |||||
SXSSFSheet sh = wb.createSheet(); | |||||
assertSame(sh.getClass(), DeferredSXSSFSheet.class); | |||||
SheetDataWriter wr = sh.getSheetDataWriter(); | |||||
assertNull(wr); | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
@Test | @Test | ||||
void gzipSheetdataWriter() throws IOException { | void gzipSheetdataWriter() throws IOException { | ||||
DeferredSXSSFWorkbook wb = new DeferredSXSSFWorkbook(); | |||||
final int rowNum = 1000; | |||||
final int sheetNum = 5; | |||||
populateData(wb, 1000, 5); | |||||
XSSFWorkbook xwb = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb); | |||||
for (int i = 0; i < sheetNum; i++) { | |||||
Sheet sh = xwb.getSheetAt(i); | |||||
assertEquals("sheet" + i, sh.getSheetName()); | |||||
for (int j = 0; j < rowNum; j++) { | |||||
Row row = sh.getRow(j); | |||||
assertNotNull(row, "row[" + j + "]"); | |||||
Cell cell1 = row.getCell(0); | |||||
assertEquals(new CellReference(cell1).formatAsString(), cell1.getStringCellValue()); | |||||
Cell cell2 = row.getCell(1); | |||||
assertEquals(i, (int) cell2.getNumericCellValue()); | |||||
Cell cell3 = row.getCell(2); | |||||
assertEquals(j, (int) cell3.getNumericCellValue()); | |||||
try (DeferredSXSSFWorkbook wb = new DeferredSXSSFWorkbook()) { | |||||
final int rowNum = 1000; | |||||
final int sheetNum = 5; | |||||
populateData(wb); | |||||
try (XSSFWorkbook xwb = DeferredSXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) { | |||||
for (int i = 0; i < sheetNum; i++) { | |||||
Sheet sh = xwb.getSheetAt(i); | |||||
assertEquals("sheet" + i, sh.getSheetName()); | |||||
for (int j = 0; j < rowNum; j++) { | |||||
Row row = sh.getRow(j); | |||||
assertNotNull(row, "row[" + j + "]"); | |||||
Cell cell1 = row.getCell(0); | |||||
assertEquals(new CellReference(cell1).formatAsString(), cell1.getStringCellValue()); | |||||
Cell cell2 = row.getCell(1); | |||||
assertEquals(i, (int) cell2.getNumericCellValue()); | |||||
Cell cell3 = row.getCell(2); | |||||
assertEquals(j, (int) cell3.getNumericCellValue()); | |||||
} | |||||
} | |||||
assertTrue(wb.dispose()); | |||||
} | } | ||||
} | } | ||||
assertTrue(wb.dispose()); | |||||
xwb.close(); | |||||
wb.close(); | |||||
} | } | ||||
@Test | |||||
void workbookDispose() throws IOException { | |||||
DeferredSXSSFWorkbook wb1 = new DeferredSXSSFWorkbook(); | |||||
// the underlying writer is SheetDataWriter | |||||
assertWorkbookDispose(wb1); | |||||
wb1.close(); | |||||
DeferredSXSSFWorkbook wb2 = new DeferredSXSSFWorkbook(); | |||||
wb2.setCompressTempFiles(true); | |||||
// the underlying writer is GZIPSheetDataWriter | |||||
assertWorkbookDispose(wb2); | |||||
wb2.close(); | |||||
} | |||||
@ParameterizedTest | |||||
@ValueSource(booleans = {false, true}) | |||||
void workbookDispose(boolean compressTempFiles) throws IOException { | |||||
try (DeferredSXSSFWorkbook wb = new DeferredSXSSFWorkbook()) { | |||||
// compressTempFiles == false: the underlying writer is SheetDataWriter | |||||
// compressTempFiles == true: the underlying writer is GZIPSheetDataWriter | |||||
wb.setCompressTempFiles(compressTempFiles); | |||||
private static void assertWorkbookDispose(DeferredSXSSFWorkbook wb) { | |||||
populateData(wb, 1000, 5); | |||||
populateData(wb); | |||||
for (Sheet sheet : wb) { | |||||
DeferredSXSSFSheet sxSheet = (DeferredSXSSFSheet) sheet; | |||||
assertNull(sxSheet.getSheetDataWriter()); | |||||
} | |||||
for (Sheet sheet : wb) { | |||||
DeferredSXSSFSheet sxSheet = (DeferredSXSSFSheet) sheet; | |||||
assertNull(sxSheet.getSheetDataWriter()); | |||||
} | |||||
assertTrue(wb.dispose()); | |||||
assertTrue(wb.dispose()); | |||||
for (Sheet sheet : wb) { | |||||
DeferredSXSSFSheet sxSheet = (DeferredSXSSFSheet) sheet; | |||||
assertNull(sxSheet.getSheetDataWriter()); | |||||
for (Sheet sheet : wb) { | |||||
DeferredSXSSFSheet sxSheet = (DeferredSXSSFSheet) sheet; | |||||
assertNull(sxSheet.getSheetDataWriter()); | |||||
} | |||||
} | } | ||||
} | } | ||||
private static void populateData(DeferredSXSSFWorkbook wb, final int rowNum, final int sheetNum) { | |||||
for (int i = 0; i < sheetNum; i++) { | |||||
private static void populateData(DeferredSXSSFWorkbook wb) { | |||||
for (int i = 0; i < 5; i++) { | |||||
DeferredSXSSFSheet sheet = wb.createSheet("sheet" + i); | DeferredSXSSFSheet sheet = wb.createSheet("sheet" + i); | ||||
int index = i; | int index = i; | ||||
sheet.setRowGenerator((sh) -> { | sheet.setRowGenerator((sh) -> { | ||||
for (int j = 0; j < rowNum; j++) { | |||||
for (int j = 0; j < 1000; j++) { | |||||
Row row = sh.createRow(j); | Row row = sh.createRow(j); | ||||
Cell cell1 = row.createCell(0); | Cell cell1 = row.createCell(0); | ||||
cell1.setCellValue(new CellReference(cell1).formatAsString()); | cell1.setCellValue(new CellReference(cell1).formatAsString()); | ||||
} | } | ||||
} | } | ||||
void changeSheetNameWithSharedFormulas() { | |||||
/* not implemented */ | |||||
@Override | |||||
@Disabled("not implemented") | |||||
protected void changeSheetNameWithSharedFormulas() { | |||||
} | } | ||||
} | } |
import org.junit.jupiter.api.AfterEach; | import org.junit.jupiter.api.AfterEach; | ||||
import org.junit.jupiter.api.Disabled; | import org.junit.jupiter.api.Disabled; | ||||
import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||
import org.junit.jupiter.params.ParameterizedTest; | |||||
import org.junit.jupiter.params.provider.ValueSource; | |||||
public final class TestSXSSFWorkbook extends BaseTestXWorkbook { | public final class TestSXSSFWorkbook extends BaseTestXWorkbook { | ||||
@Test | @Test | ||||
void existingWorkbook() throws IOException { | void existingWorkbook() throws IOException { | ||||
XSSFWorkbook xssfWb1 = new XSSFWorkbook(); | |||||
xssfWb1.createSheet("S1"); | |||||
SXSSFWorkbook wb1 = new SXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1); | |||||
assertTrue(wb1.dispose()); | |||||
SXSSFWorkbook wb2 = new SXSSFWorkbook(xssfWb2); | |||||
assertEquals(1, wb2.getNumberOfSheets()); | |||||
Sheet sheet = wb2.getSheetAt(0); | |||||
assertNotNull(sheet); | |||||
assertEquals("S1", sheet.getSheetName()); | |||||
assertTrue(wb2.dispose()); | |||||
xssfWb2.close(); | |||||
xssfWb1.close(); | |||||
wb2.close(); | |||||
wb1.close(); | |||||
try (XSSFWorkbook xssfWb1 = new XSSFWorkbook()) { | |||||
xssfWb1.createSheet("S1"); | |||||
try (SXSSFWorkbook wb1 = new SXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1)) { | |||||
assertTrue(wb1.dispose()); | |||||
try (SXSSFWorkbook wb2 = new SXSSFWorkbook(xssfWb2)) { | |||||
assertEquals(1, wb2.getNumberOfSheets()); | |||||
Sheet sheet = wb2.getSheetAt(0); | |||||
assertNotNull(sheet); | |||||
assertEquals("S1", sheet.getSheetName()); | |||||
assertTrue(wb2.dispose()); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void useSharedStringsTable() throws Exception { | void useSharedStringsTable() throws Exception { | ||||
SXSSFWorkbook wb = new SXSSFWorkbook(null, 10, false, true); | |||||
SharedStringsTable sss = wb.getSharedStringSource(); | |||||
assertNotNull(sss); | |||||
Row row = wb.createSheet("S1").createRow(0); | |||||
row.createCell(0).setCellValue("A"); | |||||
row.createCell(1).setCellValue("B"); | |||||
row.createCell(2).setCellValue("A"); | |||||
XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb); | |||||
sss = wb.getSharedStringSource(); | |||||
assertEquals(2, sss.getUniqueCount()); | |||||
assertTrue(wb.dispose()); | |||||
Sheet sheet1 = xssfWorkbook.getSheetAt(0); | |||||
assertEquals("S1", sheet1.getSheetName()); | |||||
assertEquals(1, sheet1.getPhysicalNumberOfRows()); | |||||
row = sheet1.getRow(0); | |||||
assertNotNull(row); | |||||
Cell cell = row.getCell(0); | |||||
assertNotNull(cell); | |||||
assertEquals("A", cell.getStringCellValue()); | |||||
cell = row.getCell(1); | |||||
assertNotNull(cell); | |||||
assertEquals("B", cell.getStringCellValue()); | |||||
cell = row.getCell(2); | |||||
assertNotNull(cell); | |||||
assertEquals("A", cell.getStringCellValue()); | |||||
xssfWorkbook.close(); | |||||
wb.close(); | |||||
try (SXSSFWorkbook wb = new SXSSFWorkbook(null, 10, false, true)) { | |||||
SharedStringsTable sss = wb.getSharedStringSource(); | |||||
assertNotNull(sss); | |||||
Row row = wb.createSheet("S1").createRow(0); | |||||
row.createCell(0).setCellValue("A"); | |||||
row.createCell(1).setCellValue("B"); | |||||
row.createCell(2).setCellValue("A"); | |||||
try (XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) { | |||||
sss = wb.getSharedStringSource(); | |||||
assertEquals(2, sss.getUniqueCount()); | |||||
assertTrue(wb.dispose()); | |||||
Sheet sheet1 = xssfWorkbook.getSheetAt(0); | |||||
assertEquals("S1", sheet1.getSheetName()); | |||||
assertEquals(1, sheet1.getPhysicalNumberOfRows()); | |||||
row = sheet1.getRow(0); | |||||
assertNotNull(row); | |||||
Cell cell = row.getCell(0); | |||||
assertNotNull(cell); | |||||
assertEquals("A", cell.getStringCellValue()); | |||||
cell = row.getCell(1); | |||||
assertNotNull(cell); | |||||
assertEquals("B", cell.getStringCellValue()); | |||||
cell = row.getCell(2); | |||||
assertNotNull(cell); | |||||
assertEquals("A", cell.getStringCellValue()); | |||||
} | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void addToExistingWorkbook() throws IOException { | void addToExistingWorkbook() throws IOException { | ||||
XSSFWorkbook xssfWb1 = new XSSFWorkbook(); | |||||
xssfWb1.createSheet("S1"); | |||||
Sheet sheet = xssfWb1.createSheet("S2"); | |||||
Row row = sheet.createRow(1); | |||||
Cell cell = row.createCell(1); | |||||
cell.setCellValue("value 2_1_1"); | |||||
SXSSFWorkbook wb1 = new SXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1); | |||||
assertTrue(wb1.dispose()); | |||||
xssfWb1.close(); | |||||
SXSSFWorkbook wb2 = new SXSSFWorkbook(xssfWb2); | |||||
// Add a row to the existing empty sheet | |||||
Sheet sheet1 = wb2.getSheetAt(0); | |||||
Row row1_1 = sheet1.createRow(1); | |||||
Cell cell1_1_1 = row1_1.createCell(1); | |||||
cell1_1_1.setCellValue("value 1_1_1"); | |||||
// Add a row to the existing non-empty sheet | |||||
Sheet sheet2 = wb2.getSheetAt(1); | |||||
Row row2_2 = sheet2.createRow(2); | |||||
Cell cell2_2_1 = row2_2.createCell(1); | |||||
cell2_2_1.setCellValue("value 2_2_1"); | |||||
// Add a sheet with one row | |||||
Sheet sheet3 = wb2.createSheet("S3"); | |||||
Row row3_1 = sheet3.createRow(1); | |||||
Cell cell3_1_1 = row3_1.createCell(1); | |||||
cell3_1_1.setCellValue("value 3_1_1"); | |||||
XSSFWorkbook xssfWb3 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb2); | |||||
wb2.close(); | |||||
assertEquals(3, xssfWb3.getNumberOfSheets()); | |||||
// Verify sheet 1 | |||||
sheet1 = xssfWb3.getSheetAt(0); | |||||
assertEquals("S1", sheet1.getSheetName()); | |||||
assertEquals(1, sheet1.getPhysicalNumberOfRows()); | |||||
row1_1 = sheet1.getRow(1); | |||||
assertNotNull(row1_1); | |||||
cell1_1_1 = row1_1.getCell(1); | |||||
assertNotNull(cell1_1_1); | |||||
assertEquals("value 1_1_1", cell1_1_1.getStringCellValue()); | |||||
// Verify sheet 2 | |||||
sheet2 = xssfWb3.getSheetAt(1); | |||||
assertEquals("S2", sheet2.getSheetName()); | |||||
assertEquals(2, sheet2.getPhysicalNumberOfRows()); | |||||
Row row2_1 = sheet2.getRow(1); | |||||
assertNotNull(row2_1); | |||||
Cell cell2_1_1 = row2_1.getCell(1); | |||||
assertNotNull(cell2_1_1); | |||||
assertEquals("value 2_1_1", cell2_1_1.getStringCellValue()); | |||||
row2_2 = sheet2.getRow(2); | |||||
assertNotNull(row2_2); | |||||
cell2_2_1 = row2_2.getCell(1); | |||||
assertNotNull(cell2_2_1); | |||||
assertEquals("value 2_2_1", cell2_2_1.getStringCellValue()); | |||||
// Verify sheet 3 | |||||
sheet3 = xssfWb3.getSheetAt(2); | |||||
assertEquals("S3", sheet3.getSheetName()); | |||||
assertEquals(1, sheet3.getPhysicalNumberOfRows()); | |||||
row3_1 = sheet3.getRow(1); | |||||
assertNotNull(row3_1); | |||||
cell3_1_1 = row3_1.getCell(1); | |||||
assertNotNull(cell3_1_1); | |||||
assertEquals("value 3_1_1", cell3_1_1.getStringCellValue()); | |||||
xssfWb2.close(); | |||||
xssfWb3.close(); | |||||
wb1.close(); | |||||
try (XSSFWorkbook xssfWb1 = new XSSFWorkbook()) { | |||||
xssfWb1.createSheet("S1"); | |||||
Sheet sheet = xssfWb1.createSheet("S2"); | |||||
Row row = sheet.createRow(1); | |||||
Cell cell = row.createCell(1); | |||||
cell.setCellValue("value 2_1_1"); | |||||
try (SXSSFWorkbook wb1 = new SXSSFWorkbook(xssfWb1); | |||||
XSSFWorkbook xssfWb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb1)) { | |||||
assertTrue(wb1.dispose()); | |||||
try (SXSSFWorkbook wb2 = new SXSSFWorkbook(xssfWb2)) { | |||||
// Add a row to the existing empty sheet | |||||
Sheet sheet1 = wb2.getSheetAt(0); | |||||
Row row1_1 = sheet1.createRow(1); | |||||
Cell cell1_1_1 = row1_1.createCell(1); | |||||
cell1_1_1.setCellValue("value 1_1_1"); | |||||
// Add a row to the existing non-empty sheet | |||||
Sheet sheet2 = wb2.getSheetAt(1); | |||||
Row row2_2 = sheet2.createRow(2); | |||||
Cell cell2_2_1 = row2_2.createCell(1); | |||||
cell2_2_1.setCellValue("value 2_2_1"); | |||||
// Add a sheet with one row | |||||
Sheet sheet3 = wb2.createSheet("S3"); | |||||
Row row3_1 = sheet3.createRow(1); | |||||
Cell cell3_1_1 = row3_1.createCell(1); | |||||
cell3_1_1.setCellValue("value 3_1_1"); | |||||
try (XSSFWorkbook xssfWb3 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb2)) { | |||||
assertEquals(3, xssfWb3.getNumberOfSheets()); | |||||
// Verify sheet 1 | |||||
sheet1 = xssfWb3.getSheetAt(0); | |||||
assertEquals("S1", sheet1.getSheetName()); | |||||
assertEquals(1, sheet1.getPhysicalNumberOfRows()); | |||||
row1_1 = sheet1.getRow(1); | |||||
assertNotNull(row1_1); | |||||
cell1_1_1 = row1_1.getCell(1); | |||||
assertNotNull(cell1_1_1); | |||||
assertEquals("value 1_1_1", cell1_1_1.getStringCellValue()); | |||||
// Verify sheet 2 | |||||
sheet2 = xssfWb3.getSheetAt(1); | |||||
assertEquals("S2", sheet2.getSheetName()); | |||||
assertEquals(2, sheet2.getPhysicalNumberOfRows()); | |||||
Row row2_1 = sheet2.getRow(1); | |||||
assertNotNull(row2_1); | |||||
Cell cell2_1_1 = row2_1.getCell(1); | |||||
assertNotNull(cell2_1_1); | |||||
assertEquals("value 2_1_1", cell2_1_1.getStringCellValue()); | |||||
row2_2 = sheet2.getRow(2); | |||||
assertNotNull(row2_2); | |||||
cell2_2_1 = row2_2.getCell(1); | |||||
assertNotNull(cell2_2_1); | |||||
assertEquals("value 2_2_1", cell2_2_1.getStringCellValue()); | |||||
// Verify sheet 3 | |||||
sheet3 = xssfWb3.getSheetAt(2); | |||||
assertEquals("S3", sheet3.getSheetName()); | |||||
assertEquals(1, sheet3.getPhysicalNumberOfRows()); | |||||
row3_1 = sheet3.getRow(1); | |||||
assertNotNull(row3_1); | |||||
cell3_1_1 = row3_1.getCell(1); | |||||
assertNotNull(cell3_1_1); | |||||
assertEquals("value 3_1_1", cell3_1_1.getStringCellValue()); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void sheetdataWriter() throws IOException{ | void sheetdataWriter() throws IOException{ | ||||
SXSSFWorkbook wb = new SXSSFWorkbook(); | |||||
SXSSFSheet sh = wb.createSheet(); | |||||
SheetDataWriter wr = sh.getSheetDataWriter(); | |||||
assertSame(wr.getClass(), SheetDataWriter.class); | |||||
File tmp = wr.getTempFile(); | |||||
assertStartsWith(tmp.getName(), "poi-sxssf-sheet"); | |||||
assertEndsWith(tmp.getName(), ".xml"); | |||||
assertTrue(wb.dispose()); | |||||
wb.close(); | |||||
wb = new SXSSFWorkbook(); | |||||
wb.setCompressTempFiles(true); | |||||
sh = wb.createSheet(); | |||||
wr = sh.getSheetDataWriter(); | |||||
assertSame(wr.getClass(), GZIPSheetDataWriter.class); | |||||
tmp = wr.getTempFile(); | |||||
assertStartsWith(tmp.getName(), "poi-sxssf-sheet-xml"); | |||||
assertEndsWith(tmp.getName(), ".gz"); | |||||
assertTrue(wb.dispose()); | |||||
wb.close(); | |||||
try (SXSSFWorkbook wb = new SXSSFWorkbook()) { | |||||
SXSSFSheet sh = wb.createSheet(); | |||||
SheetDataWriter wr = sh.getSheetDataWriter(); | |||||
assertSame(wr.getClass(), SheetDataWriter.class); | |||||
File tmp = wr.getTempFile(); | |||||
assertStartsWith(tmp.getName(), "poi-sxssf-sheet"); | |||||
assertEndsWith(tmp.getName(), ".xml"); | |||||
assertTrue(wb.dispose()); | |||||
} | |||||
//Test escaping of Unicode control characters | |||||
wb = new SXSSFWorkbook(); | |||||
wb.createSheet("S1").createRow(0).createCell(0).setCellValue("value\u0019"); | |||||
XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb); | |||||
Cell cell = xssfWorkbook.getSheet("S1").getRow(0).getCell(0); | |||||
assertEquals("value?", cell.getStringCellValue()); | |||||
assertTrue(wb.dispose()); | |||||
wb.close(); | |||||
xssfWorkbook.close(); | |||||
} | |||||
try (SXSSFWorkbook wb = new SXSSFWorkbook()) { | |||||
wb.setCompressTempFiles(true); | |||||
SXSSFSheet sh = wb.createSheet(); | |||||
SheetDataWriter wr = sh.getSheetDataWriter(); | |||||
assertSame(wr.getClass(), GZIPSheetDataWriter.class); | |||||
File tmp = wr.getTempFile(); | |||||
assertStartsWith(tmp.getName(), "poi-sxssf-sheet-xml"); | |||||
assertEndsWith(tmp.getName(), ".gz"); | |||||
assertTrue(wb.dispose()); | |||||
} | |||||
@Test | |||||
void gzipSheetdataWriter() throws IOException { | |||||
SXSSFWorkbook wb = new SXSSFWorkbook(); | |||||
wb.setCompressTempFiles(true); | |||||
final int rowNum = 1000; | |||||
final int sheetNum = 5; | |||||
populateData(wb, 1000, 5); | |||||
XSSFWorkbook xwb = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb); | |||||
for(int i = 0; i < sheetNum; i++){ | |||||
Sheet sh = xwb.getSheetAt(i); | |||||
assertEquals("sheet" + i, sh.getSheetName()); | |||||
for(int j = 0; j < rowNum; j++){ | |||||
Row row = sh.getRow(j); | |||||
assertNotNull(row, "row[" + j + "]"); | |||||
Cell cell1 = row.getCell(0); | |||||
assertEquals(new CellReference(cell1).formatAsString(), cell1.getStringCellValue()); | |||||
Cell cell2 = row.getCell(1); | |||||
assertEquals(i, (int)cell2.getNumericCellValue()); | |||||
Cell cell3 = row.getCell(2); | |||||
assertEquals(j, (int)cell3.getNumericCellValue()); | |||||
//Test escaping of Unicode control characters | |||||
try (SXSSFWorkbook wb = new SXSSFWorkbook()) { | |||||
wb.createSheet("S1").createRow(0).createCell(0).setCellValue("value\u0019"); | |||||
try (XSSFWorkbook xssfWorkbook = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) { | |||||
Cell cell = xssfWorkbook.getSheet("S1").getRow(0).getCell(0); | |||||
assertEquals("value?", cell.getStringCellValue()); | |||||
assertTrue(wb.dispose()); | |||||
} | } | ||||
} | } | ||||
assertTrue(wb.dispose()); | |||||
xwb.close(); | |||||
wb.close(); | |||||
} | } | ||||
private static void assertWorkbookDispose(SXSSFWorkbook wb) | |||||
{ | |||||
populateData(wb, 1000, 5); | |||||
for (Sheet sheet : wb) { | |||||
SXSSFSheet sxSheet = (SXSSFSheet) sheet; | |||||
assertTrue(sxSheet.getSheetDataWriter().getTempFile().exists()); | |||||
} | |||||
assertTrue(wb.dispose()); | |||||
@Test | |||||
void gzipSheetdataWriter() throws IOException { | |||||
try (SXSSFWorkbook wb = new SXSSFWorkbook()) { | |||||
wb.setCompressTempFiles(true); | |||||
final int rowNum = 1000; | |||||
final int sheetNum = 5; | |||||
populateData(wb); | |||||
try (XSSFWorkbook xwb = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb)) { | |||||
for (int i = 0; i < sheetNum; i++) { | |||||
Sheet sh = xwb.getSheetAt(i); | |||||
assertEquals("sheet" + i, sh.getSheetName()); | |||||
for (int j = 0; j < rowNum; j++) { | |||||
Row row = sh.getRow(j); | |||||
assertNotNull(row, "row[" + j + "]"); | |||||
Cell cell1 = row.getCell(0); | |||||
assertEquals(new CellReference(cell1).formatAsString(), cell1.getStringCellValue()); | |||||
Cell cell2 = row.getCell(1); | |||||
assertEquals(i, (int) cell2.getNumericCellValue()); | |||||
Cell cell3 = row.getCell(2); | |||||
assertEquals(j, (int) cell3.getNumericCellValue()); | |||||
} | |||||
} | |||||
for (Sheet sheet : wb) { | |||||
SXSSFSheet sxSheet = (SXSSFSheet) sheet; | |||||
assertFalse(sxSheet.getSheetDataWriter().getTempFile().exists()); | |||||
assertTrue(wb.dispose()); | |||||
} | |||||
} | } | ||||
} | } | ||||
private static void populateData(Workbook wb, final int rowNum, final int sheetNum) { | |||||
for(int i = 0; i < sheetNum; i++){ | |||||
private static void populateData(Workbook wb) { | |||||
for(int i = 0; i < 5; i++){ | |||||
Sheet sh = wb.createSheet("sheet" + i); | Sheet sh = wb.createSheet("sheet" + i); | ||||
for(int j = 0; j < rowNum; j++){ | |||||
for(int j = 0; j < 1000; j++){ | |||||
Row row = sh.createRow(j); | Row row = sh.createRow(j); | ||||
Cell cell1 = row.createCell(0); | Cell cell1 = row.createCell(0); | ||||
cell1.setCellValue(new CellReference(cell1).formatAsString()); | cell1.setCellValue(new CellReference(cell1).formatAsString()); | ||||
} | } | ||||
} | } | ||||
@Test | |||||
void workbookDispose() throws IOException { | |||||
SXSSFWorkbook wb1 = new SXSSFWorkbook(); | |||||
// the underlying writer is SheetDataWriter | |||||
assertWorkbookDispose(wb1); | |||||
wb1.close(); | |||||
SXSSFWorkbook wb2 = new SXSSFWorkbook(); | |||||
wb2.setCompressTempFiles(true); | |||||
// the underlying writer is GZIPSheetDataWriter | |||||
assertWorkbookDispose(wb2); | |||||
wb2.close(); | |||||
@ParameterizedTest | |||||
@ValueSource(booleans = {false, true}) | |||||
void workbookDispose(boolean compressTempFiles) throws IOException { | |||||
try (SXSSFWorkbook wb = new SXSSFWorkbook()) { | |||||
// compressTempFiles == false: the underlying writer is SheetDataWriter | |||||
// compressTempFiles == true: the underlying writer is GZIPSheetDataWriter | |||||
wb.setCompressTempFiles(compressTempFiles); | |||||
populateData(wb); | |||||
for (Sheet sheet : wb) { | |||||
SXSSFSheet sxSheet = (SXSSFSheet) sheet; | |||||
assertTrue(sxSheet.getSheetDataWriter().getTempFile().exists()); | |||||
} | |||||
assertTrue(wb.dispose()); | |||||
for (Sheet sheet : wb) { | |||||
SXSSFSheet sxSheet = (SXSSFSheet) sheet; | |||||
assertFalse(sxSheet.getSheetDataWriter().getTempFile().exists()); | |||||
} | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void bug53515() throws Exception { | void bug53515() throws Exception { | ||||
try (Workbook wb1 = new SXSSFWorkbook(10)) { | try (Workbook wb1 = new SXSSFWorkbook(10)) { | ||||
populateWorkbook(wb1); | populateWorkbook(wb1); | ||||
saveTwice(wb1); | |||||
assertDoesNotThrow(() -> wb1.write(NULL_OUTPUT_STREAM)); | |||||
assertDoesNotThrow(() -> wb1.write(NULL_OUTPUT_STREAM)); | |||||
try (Workbook wb2 = new XSSFWorkbook()) { | try (Workbook wb2 = new XSSFWorkbook()) { | ||||
populateWorkbook(wb2); | populateWorkbook(wb2); | ||||
saveTwice(wb2); | |||||
assertDoesNotThrow(() -> wb2.write(NULL_OUTPUT_STREAM)); | |||||
assertDoesNotThrow(() -> wb2.write(NULL_OUTPUT_STREAM)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private static void saveTwice(Workbook wb) throws Exception { | |||||
for (int i = 0; i < 2; i++) { | |||||
try { | |||||
wb.write(NULL_OUTPUT_STREAM); | |||||
} catch (Exception e) { | |||||
throw new Exception("ERROR: failed on " + (i + 1) | |||||
+ "th time calling " + wb.getClass().getName() | |||||
+ ".write() with exception " + e.getMessage(), e); | |||||
} | |||||
} | |||||
} | |||||
@Test | @Test | ||||
void closeDoesNotModifyWorkbook() throws IOException { | void closeDoesNotModifyWorkbook() throws IOException { | ||||
final String filename = "SampleSS.xlsx"; | final String filename = "SampleSS.xlsx"; | ||||
char[] useless = new char[32767]; | char[] useless = new char[32767]; | ||||
Arrays.fill(useless, ' '); | Arrays.fill(useless, ' '); | ||||
for (int row = 0; row < 1; row++) { | |||||
for (int row = 0; row < 10; row++) { | |||||
Row r = s.createRow(row); | Row r = s.createRow(row); | ||||
for (int col = 0; col < 10; col++) { | for (int col = 0; col < 10; col++) { | ||||
char[] prefix = Integer.toHexString(row * 1000 + col).toCharArray(); | |||||
char[] prefix = Integer.toHexString(row * 10 + col).toCharArray(); | |||||
Arrays.fill(useless, 0, 10, ' '); | Arrays.fill(useless, 0, 10, ' '); | ||||
System.arraycopy(prefix, 0, useless, 0, prefix.length); | System.arraycopy(prefix, 0, useless, 0, prefix.length); | ||||
String ul = new String(useless); | String ul = new String(useless); | ||||
} | } | ||||
} | } | ||||
@Override | |||||
@Disabled("not implemented") | @Disabled("not implemented") | ||||
@Test | |||||
void changeSheetNameWithSharedFormulas() { | |||||
protected void changeSheetNameWithSharedFormulas() { | |||||
} | } | ||||
} | } |
import org.apache.poi.ss.util.CellReference; | import org.apache.poi.ss.util.CellReference; | ||||
import org.apache.poi.ss.util.CellUtil; | import org.apache.poi.ss.util.CellUtil; | ||||
import org.apache.poi.util.LocaleUtil; | import org.apache.poi.util.LocaleUtil; | ||||
import org.apache.commons.io.output.NullOutputStream; | |||||
import org.apache.poi.util.TempFile; | import org.apache.poi.util.TempFile; | ||||
import org.apache.poi.util.XMLHelper; | import org.apache.poi.util.XMLHelper; | ||||
import org.apache.poi.xssf.SXSSFITestDataProvider; | import org.apache.poi.xssf.SXSSFITestDataProvider; | ||||
import org.junit.jupiter.params.provider.CsvSource; | import org.junit.jupiter.params.provider.CsvSource; | ||||
import org.junit.jupiter.params.provider.EnumSource; | import org.junit.jupiter.params.provider.EnumSource; | ||||
import org.junit.jupiter.params.provider.ValueSource; | import org.junit.jupiter.params.provider.ValueSource; | ||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcCell; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCell; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCells; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontImpl; | import org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontImpl; | ||||
import org.xml.sax.InputSource; | import org.xml.sax.InputSource; | ||||
import org.xml.sax.SAXParseException; | import org.xml.sax.SAXParseException; | ||||
CellStyle style = wb.createCellStyle(); | CellStyle style = wb.createCellStyle(); | ||||
style.setRotation((short) -90); | style.setRotation((short) -90); | ||||
cell.setCellStyle(style); | cell.setCellStyle(style); | ||||
assertEquals(180, style.getRotation()); | |||||
XSSFTestDataSamples.writeOut(wb, fileName); | XSSFTestDataSamples.writeOut(wb, fileName); | ||||
} | } |
xssfFont.setUnderline(Font.U_DOUBLE); | xssfFont.setUnderline(Font.U_DOUBLE); | ||||
assertEquals(ctFont.sizeOfUArray(),1); | assertEquals(ctFont.sizeOfUArray(),1); | ||||
assertEquals(STUnderlineValues.DOUBLE,ctFont.getUArray(0).getVal()); | |||||
assertSame(STUnderlineValues.DOUBLE,ctFont.getUArray(0).getVal()); | |||||
xssfFont.setUnderline(FontUnderline.DOUBLE_ACCOUNTING); | xssfFont.setUnderline(FontUnderline.DOUBLE_ACCOUNTING); | ||||
assertEquals(ctFont.sizeOfUArray(),1); | assertEquals(ctFont.sizeOfUArray(),1); | ||||
assertEquals(STUnderlineValues.DOUBLE_ACCOUNTING,ctFont.getUArray(0).getVal()); | |||||
assertSame(STUnderlineValues.DOUBLE_ACCOUNTING,ctFont.getUArray(0).getVal()); | |||||
} | } | ||||
@Test | @Test | ||||
assertEquals(FontScheme.MAJOR,font.getScheme()); | assertEquals(FontScheme.MAJOR,font.getScheme()); | ||||
font.setScheme(FontScheme.NONE); | font.setScheme(FontScheme.NONE); | ||||
assertEquals(STFontScheme.NONE,ctFont.getSchemeArray(0).getVal()); | |||||
assertSame(STFontScheme.NONE,ctFont.getSchemeArray(0).getVal()); | |||||
} | } | ||||
@Test | @Test | ||||
assertEquals(Font.SS_NONE,font.getTypeOffset()); | assertEquals(Font.SS_NONE,font.getTypeOffset()); | ||||
font.setTypeOffset(XSSFFont.SS_SUPER); | font.setTypeOffset(XSSFFont.SS_SUPER); | ||||
assertEquals(STVerticalAlignRun.SUPERSCRIPT,ctFont.getVertAlignArray(0).getVal()); | |||||
assertSame(STVerticalAlignRun.SUPERSCRIPT,ctFont.getVertAlignArray(0).getVal()); | |||||
} | } | ||||
// store test from TestSheetUtil here as it uses XSSF | // store test from TestSheetUtil here as it uses XSSF | ||||
@Test | @Test | ||||
void testCanComputeWidthXSSF() throws IOException { | void testCanComputeWidthXSSF() throws IOException { | ||||
try (Workbook wb = new XSSFWorkbook()) { | try (Workbook wb = new XSSFWorkbook()) { | ||||
// cannot check on result because on some machines we get back false here! | // cannot check on result because on some machines we get back false here! | ||||
SheetUtil.canComputeColumnWidth(wb.getFontAt(0)); | |||||
assertDoesNotThrow(() -> SheetUtil.canComputeColumnWidth(wb.getFontAt(0))); | |||||
} | } | ||||
} | } | ||||
font.setFontName("some non existing font name"); | font.setFontName("some non existing font name"); | ||||
// Even with invalid fonts we still get back useful data most of the time... | // Even with invalid fonts we still get back useful data most of the time... | ||||
SheetUtil.canComputeColumnWidth(font); | |||||
assertDoesNotThrow(() -> SheetUtil.canComputeColumnWidth(font)); | |||||
} | } | ||||
/** | /** |
sheet.createFreezePane(2, 4); | sheet.createFreezePane(2, 4); | ||||
assertEquals(2.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getXSplit(), 0.0); | assertEquals(2.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getXSplit(), 0.0); | ||||
assertEquals(STPane.BOTTOM_RIGHT, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getActivePane()); | |||||
assertSame(STPane.BOTTOM_RIGHT, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getActivePane()); | |||||
sheet.createFreezePane(3, 6, 10, 10); | sheet.createFreezePane(3, 6, 10, 10); | ||||
assertEquals(3.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getXSplit(), 0.0); | assertEquals(3.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getXSplit(), 0.0); | ||||
// assertEquals(10, sheet.getTopRow()); | // assertEquals(10, sheet.getTopRow()); | ||||
// assertEquals(10, sheet.getLeftCol()); | // assertEquals(10, sheet.getLeftCol()); | ||||
sheet.createSplitPane(4, 8, 12, 12, 1); | sheet.createSplitPane(4, 8, 12, 12, 1); | ||||
assertEquals(8.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getYSplit(), 0.0); | assertEquals(8.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getYSplit(), 0.0); | ||||
assertEquals(STPane.BOTTOM_RIGHT, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getActivePane()); | |||||
assertSame(STPane.BOTTOM_RIGHT, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getActivePane()); | |||||
workbook.close(); | workbook.close(); | ||||
} | } | ||||
CTCalcPr calcPr = wb1.getCTWorkbook().addNewCalcPr(); | CTCalcPr calcPr = wb1.getCTWorkbook().addNewCalcPr(); | ||||
calcPr.setCalcMode(STCalcMode.MANUAL); | calcPr.setCalcMode(STCalcMode.MANUAL); | ||||
sheet.setForceFormulaRecalculation(true); | sheet.setForceFormulaRecalculation(true); | ||||
assertEquals(STCalcMode.AUTO, calcPr.getCalcMode()); | |||||
assertSame(STCalcMode.AUTO, calcPr.getCalcMode()); | |||||
// Check | // Check | ||||
sheet.setForceFormulaRecalculation(false); | sheet.setForceFormulaRecalculation(false); | ||||
wb.close(); | wb.close(); | ||||
} | } | ||||
private void testCopyOneRow(String copyRowsTestWorkbook) throws IOException { | |||||
final double FLOAT_PRECISION = 1e-9; | |||||
final XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook(copyRowsTestWorkbook); | |||||
final XSSFSheet sheet = wb.getSheetAt(0); | |||||
final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); | |||||
sheet.copyRows(1, 1, 6, defaultCopyPolicy); | |||||
final Row srcRow = sheet.getRow(1); | |||||
final Row destRow = sheet.getRow(6); | |||||
int col = 0; | |||||
Cell cell; | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Source row ->", cell.getStringCellValue()); | |||||
// Style | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Red", cell.getStringCellValue(), "[Style] B7 cell value"); | |||||
assertEquals(CellUtil.getCell(srcRow, 1).getCellStyle(), cell.getCellStyle(), "[Style] B7 cell style"); | |||||
// Blank | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.BLANK, cell.getCellType(), "[Blank] C7 cell type"); | |||||
// Error | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.ERROR, cell.getCellType(), "[Error] D7 cell type"); | |||||
final FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
//FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
assertEquals(FormulaError.NA, error, "[Error] D7 cell value"); | |||||
// Date | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Date] E7 cell type"); | |||||
final Date date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 1).getTime(); | |||||
assertEquals(date, cell.getDateCellValue(), "[Date] E7 cell value"); | |||||
// Boolean | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.BOOLEAN, cell.getCellType(), "[Boolean] F7 cell type"); | |||||
assertTrue(cell.getBooleanCellValue(), "[Boolean] F7 cell value"); | |||||
// String | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.STRING, cell.getCellType(), "[String] G7 cell type"); | |||||
assertEquals("Hello", cell.getStringCellValue(), "[String] G7 cell value"); | |||||
// Int | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Int] H7 cell type"); | |||||
assertEquals(15, (int) cell.getNumericCellValue(), "[Int] H7 cell value"); | |||||
// Float | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Float] I7 cell type"); | |||||
assertEquals(12.5, cell.getNumericCellValue(), FLOAT_PRECISION, "[Float] I7 cell value"); | |||||
// Cell Formula | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Sheet1!J7", new CellReference(cell).formatAsString()); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula] J7 cell type"); | |||||
assertEquals("5+2", cell.getCellFormula(), "[Cell Formula] J7 cell formula"); | |||||
//System.out.println("Cell formula evaluation currently unsupported"); | |||||
// Cell Formula with Reference | |||||
// Formula row references should be adjusted by destRowNum-srcRowNum | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Sheet1!K7", new CellReference(cell).formatAsString()); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference] K7 cell type"); | |||||
assertEquals("J7+H$2", cell.getCellFormula(), "[Cell Formula with Reference] K7 cell formula"); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference spanning multiple rows] L7 cell type"); | |||||
assertEquals("G7&\" \"&G8", cell.getCellFormula(), "[Cell Formula with Reference spanning multiple rows] L7 cell formula"); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Area Reference] M7 cell type"); | |||||
assertEquals("SUM(H7:I8)", cell.getCellFormula(), "[Cell Formula with Area Reference] M7 cell formula"); | |||||
// Array Formula | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
//System.out.println("Array formulas currently unsupported"); | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
/* | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Array Formula] N7 cell type"); | |||||
assertEquals("{SUM(H7:J7*{1,2,3})}", cell.getCellFormula(), "[Array Formula] N7 cell formula"); | |||||
*/ | |||||
// Data Format | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Data Format] O7 cell type"); | |||||
assertEquals(100.20, cell.getNumericCellValue(), FLOAT_PRECISION, "[Data Format] O7 cell value"); | |||||
//FIXME: currently fails | |||||
final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; | |||||
assertEquals(moneyFormat, cell.getCellStyle().getDataFormatString(), "[Data Format] O7 data format"); | |||||
// Merged | |||||
cell = CellUtil.getCell(destRow, col); | |||||
assertEquals("Merged cells", cell.getStringCellValue(), "[Merged] P7:Q7 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P7:Q7")), "[Merged] P7:Q7 merged region"); | |||||
// Merged across multiple rows | |||||
// Microsoft Excel 2013 does not copy a merged region unless all rows of | |||||
// the source merged region are selected | |||||
// POI's behavior should match this behavior | |||||
col += 2; | |||||
cell = CellUtil.getCell(destRow, col); | |||||
// Note: this behavior deviates from Microsoft Excel, | |||||
// which will not overwrite a cell in destination row if merged region extends beyond the copied row. | |||||
// The Excel way would require: | |||||
//assertEquals("[Merged across multiple rows] R7:S8 merged region", "Should NOT be overwritten", cell.getStringCellValue()); | |||||
//assertFalse("[Merged across multiple rows] R7:S8 merged region", | |||||
// sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8"))); | |||||
// As currently implemented, cell value is copied but merged region is not copied | |||||
assertEquals("Merged cells across multiple rows", cell.getStringCellValue(), "[Merged across multiple rows] R7:S8 cell value"); | |||||
// shouldn't do 1-row merge | |||||
assertFalse(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S7")), "[Merged across multiple rows] R7:S7 merged region (one row)"); | |||||
//shouldn't do 2-row merge | |||||
assertFalse(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8")), "[Merged across multiple rows] R7:S8 merged region"); | |||||
// Make sure other rows are blank (off-by-one errors) | |||||
assertNull(sheet.getRow(5)); | |||||
assertNull(sheet.getRow(7)); | |||||
wb.close(); | |||||
} | |||||
private void testCopyMultipleRows(String copyRowsTestWorkbook) throws IOException { | |||||
final double FLOAT_PRECISION = 1e-9; | |||||
final XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook(copyRowsTestWorkbook); | |||||
final XSSFSheet sheet = wb.getSheetAt(0); | |||||
final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); | |||||
sheet.copyRows(0, 3, 8, defaultCopyPolicy); | |||||
sheet.getRow(0); | |||||
final Row srcRow1 = sheet.getRow(1); | |||||
final Row srcRow2 = sheet.getRow(2); | |||||
final Row srcRow3 = sheet.getRow(3); | |||||
final Row destHeaderRow = sheet.getRow(8); | |||||
final Row destRow1 = sheet.getRow(9); | |||||
final Row destRow2 = sheet.getRow(10); | |||||
final Row destRow3 = sheet.getRow(11); | |||||
int col = 0; | |||||
Cell cell; | |||||
// Header row should be copied | |||||
assertNotNull(destHeaderRow); | |||||
// Data rows | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Source row ->", cell.getStringCellValue()); | |||||
// Style | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Red", cell.getStringCellValue(), "[Style] B10 cell value"); | |||||
assertEquals(CellUtil.getCell(srcRow1, 1).getCellStyle(), cell.getCellStyle(), "[Style] B10 cell style"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("Blue", cell.getStringCellValue(), "[Style] B11 cell value"); | |||||
assertEquals(CellUtil.getCell(srcRow2, 1).getCellStyle(), cell.getCellStyle(), "[Style] B11 cell style"); | |||||
// Blank | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.BLANK, cell.getCellType(), "[Blank] C10 cell type"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.BLANK, cell.getCellType(), "[Blank] C11 cell type"); | |||||
// Error | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
//FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
assertEquals(CellType.ERROR, cell.getCellType(), "[Error] D10 cell type"); | |||||
assertEquals(FormulaError.NA, error, "[Error] D10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
//FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
assertEquals(CellType.ERROR, cell.getCellType(), "[Error] D11 cell type"); | |||||
assertEquals(FormulaError.NAME, error, "[Error] D11 cell value"); | |||||
// Date | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
Date date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 1).getTime(); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Date] E10 cell type"); | |||||
assertEquals(date, cell.getDateCellValue(), "[Date] E10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 2).getTime(); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Date] E11 cell type"); | |||||
assertEquals(date, cell.getDateCellValue(), "[Date] E11 cell value"); | |||||
// Boolean | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.BOOLEAN, cell.getCellType(), "[Boolean] F10 cell type"); | |||||
assertTrue(cell.getBooleanCellValue(), "[Boolean] F10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.BOOLEAN, cell.getCellType(), "[Boolean] F11 cell type"); | |||||
assertFalse(cell.getBooleanCellValue(), "[Boolean] F11 cell value"); | |||||
// String | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.STRING, cell.getCellType(), "[String] G10 cell type"); | |||||
assertEquals("Hello", cell.getStringCellValue(), "[String] G10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.STRING, cell.getCellType(), "[String] G11 cell type"); | |||||
assertEquals("World", cell.getStringCellValue(), "[String] G11 cell value"); | |||||
// Int | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Int] H10 cell type"); | |||||
assertEquals(15, (int) cell.getNumericCellValue(), "[Int] H10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Int] H11 cell type"); | |||||
assertEquals(42, (int) cell.getNumericCellValue(), "[Int] H11 cell value"); | |||||
// Float | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Float] I10 cell type"); | |||||
assertEquals(12.5, cell.getNumericCellValue(), FLOAT_PRECISION, "[Float] I10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Float] I11 cell type"); | |||||
assertEquals(5.5, cell.getNumericCellValue(), FLOAT_PRECISION, "[Float] I11 cell value"); | |||||
// Cell Formula | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula] J10 cell type"); | |||||
assertEquals("5+2", cell.getCellFormula(), "[Cell Formula] J10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula] J11 cell type"); | |||||
assertEquals("6+18", cell.getCellFormula(), "[Cell Formula] J11 cell formula"); | |||||
// Cell Formula with Reference | |||||
col++; | |||||
// Formula row references should be adjusted by destRowNum-srcRowNum | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference] K10 cell type"); | |||||
assertEquals("J10+H$2", cell.getCellFormula(), "[Cell Formula with Reference] K10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference] K11 cell type"); | |||||
assertEquals("J11+H$2", cell.getCellFormula(), "[Cell Formula with Reference] K11 cell formula"); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference spanning multiple rows] L10 cell type"); | |||||
assertEquals("G10&\" \"&G11", cell.getCellFormula(), "[Cell Formula with Reference spanning multiple rows] L10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference spanning multiple rows] L11 cell type"); | |||||
assertEquals("G11&\" \"&G12", cell.getCellFormula(), "[Cell Formula with Reference spanning multiple rows] L11 cell formula"); | |||||
// Cell Formula with Area Reference | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Area Reference] M10 cell type"); | |||||
assertEquals("SUM(H10:I11)", cell.getCellFormula(), "[Cell Formula with Area Reference] M10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
// Also acceptable: SUM($H10:I$3), but this AreaReference isn't in ascending order | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Area Reference] M11 cell type"); | |||||
assertEquals("SUM($H$3:I10)", cell.getCellFormula(), "[Cell Formula with Area Reference] M11 cell formula"); | |||||
// Array Formula | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
// System.out.println("Array formulas currently unsupported"); | |||||
/* | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Array Formula] N10 cell type"); | |||||
assertEquals("{SUM(H10:J10*{1,2,3})}", cell.getCellFormula(), "[Array Formula] N10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
assertEquals(CellType.FORMULA, cell.getCellType(). "[Array Formula] N11 cell type"); | |||||
assertEquals("{SUM(H11:J11*{1,2,3})}", cell.getCellFormula(). "[Array Formula] N11 cell formula"); | |||||
*/ | |||||
// Data Format | |||||
col++; | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Data Format] O10 cell type"); | |||||
assertEquals(100.20, cell.getNumericCellValue(), FLOAT_PRECISION, "[Data Format] O10 cell value"); | |||||
final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; | |||||
assertEquals(moneyFormat, cell.getCellStyle().getDataFormatString(), "[Data Format] O10 cell data format"); | |||||
// Merged | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Merged cells", cell.getStringCellValue(), "[Merged] P10:Q10 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P10:Q10")), "[Merged] P10:Q10 merged region"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("Merged cells", cell.getStringCellValue(), "[Merged] P11:Q11 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P11:Q11")), "[Merged] P11:Q11 merged region"); | |||||
// Should Q10/Q11 be checked? | |||||
// Merged across multiple rows | |||||
// Microsoft Excel 2013 does not copy a merged region unless all rows of | |||||
// the source merged region are selected | |||||
// POI's behavior should match this behavior | |||||
col += 2; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Merged cells across multiple rows", cell.getStringCellValue(), "[Merged across multiple rows] R10:S11 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R10:S11")), "[Merged across multiple rows] R10:S11 merged region"); | |||||
// Row 3 (zero-based) was empty, so Row 11 (zero-based) should be empty too. | |||||
if (srcRow3 == null) { | |||||
assertNull(destRow3, "Row 3 was empty, so Row 11 should be empty"); | |||||
} | |||||
// Make sure other rows are blank (off-by-one errors) | |||||
assertNull(sheet.getRow(7), "Off-by-one lower edge case"); //one row above destHeaderRow | |||||
assertNull(sheet.getRow(12), "Off-by-one upper edge case"); //one row below destRow3 | |||||
wb.close(); | |||||
} | |||||
@Test | @Test | ||||
void testCopyOneRow() throws IOException { | void testCopyOneRow() throws IOException { | ||||
testCopyOneRow("XSSFSheet.copyRows.xlsx"); | |||||
final double FLOAT_PRECISION = 1e-9; | |||||
try (final XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("XSSFSheet.copyRows.xlsx")) { | |||||
final XSSFSheet sheet = wb.getSheetAt(0); | |||||
final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); | |||||
sheet.copyRows(1, 1, 6, defaultCopyPolicy); | |||||
final Row srcRow = sheet.getRow(1); | |||||
final Row destRow = sheet.getRow(6); | |||||
int col = 0; | |||||
Cell cell; | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Source row ->", cell.getStringCellValue()); | |||||
// Style | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Red", cell.getStringCellValue(), "[Style] B7 cell value"); | |||||
assertEquals(CellUtil.getCell(srcRow, 1).getCellStyle(), cell.getCellStyle(), "[Style] B7 cell style"); | |||||
// Blank | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.BLANK, cell.getCellType(), "[Blank] C7 cell type"); | |||||
// Error | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.ERROR, cell.getCellType(), "[Error] D7 cell type"); | |||||
final FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
//FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
assertEquals(FormulaError.NA, error, "[Error] D7 cell value"); | |||||
// Date | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Date] E7 cell type"); | |||||
final Date date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 1).getTime(); | |||||
assertEquals(date, cell.getDateCellValue(), "[Date] E7 cell value"); | |||||
// Boolean | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.BOOLEAN, cell.getCellType(), "[Boolean] F7 cell type"); | |||||
assertTrue(cell.getBooleanCellValue(), "[Boolean] F7 cell value"); | |||||
// String | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.STRING, cell.getCellType(), "[String] G7 cell type"); | |||||
assertEquals("Hello", cell.getStringCellValue(), "[String] G7 cell value"); | |||||
// Int | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Int] H7 cell type"); | |||||
assertEquals(15, (int) cell.getNumericCellValue(), "[Int] H7 cell value"); | |||||
// Float | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Float] I7 cell type"); | |||||
assertEquals(12.5, cell.getNumericCellValue(), FLOAT_PRECISION, "[Float] I7 cell value"); | |||||
// Cell Formula | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Sheet1!J7", new CellReference(cell).formatAsString()); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula] J7 cell type"); | |||||
assertEquals("5+2", cell.getCellFormula(), "[Cell Formula] J7 cell formula"); | |||||
//System.out.println("Cell formula evaluation currently unsupported"); | |||||
// Cell Formula with Reference | |||||
// Formula row references should be adjusted by destRowNum-srcRowNum | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Sheet1!K7", new CellReference(cell).formatAsString()); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference] K7 cell type"); | |||||
assertEquals("J7+H$2", cell.getCellFormula(), "[Cell Formula with Reference] K7 cell formula"); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference spanning multiple rows] L7 cell type"); | |||||
assertEquals("G7&\" \"&G8", cell.getCellFormula(), "[Cell Formula with Reference spanning multiple rows] L7 cell formula"); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Area Reference] M7 cell type"); | |||||
assertEquals("SUM(H7:I8)", cell.getCellFormula(), "[Cell Formula with Area Reference] M7 cell formula"); | |||||
// Array Formula | |||||
col++; | |||||
// cell = CellUtil.getCell(destRow, col++); | |||||
// Array formulas currently unsupported" | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
// assertEquals(CellType.FORMULA, cell.getCellType(), "[Array Formula] N7 cell type"); | |||||
// assertEquals("{SUM(H7:J7*{1,2,3})}", cell.getCellFormula(), "[Array Formula] N7 cell formula"); | |||||
// Data Format | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Data Format] O7 cell type"); | |||||
assertEquals(100.20, cell.getNumericCellValue(), FLOAT_PRECISION, "[Data Format] O7 cell value"); | |||||
//FIXME: currently fails | |||||
final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; | |||||
assertEquals(moneyFormat, cell.getCellStyle().getDataFormatString(), "[Data Format] O7 data format"); | |||||
// Merged | |||||
cell = CellUtil.getCell(destRow, col); | |||||
assertEquals("Merged cells", cell.getStringCellValue(), "[Merged] P7:Q7 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P7:Q7")), "[Merged] P7:Q7 merged region"); | |||||
// Merged across multiple rows | |||||
// Microsoft Excel 2013 does not copy a merged region unless all rows of | |||||
// the source merged region are selected | |||||
// POI's behavior should match this behavior | |||||
col += 2; | |||||
cell = CellUtil.getCell(destRow, col); | |||||
// Note: this behavior deviates from Microsoft Excel, | |||||
// which will not overwrite a cell in destination row if merged region extends beyond the copied row. | |||||
// The Excel way would require: | |||||
//assertEquals("[Merged across multiple rows] R7:S8 merged region", "Should NOT be overwritten", cell.getStringCellValue()); | |||||
//assertFalse("[Merged across multiple rows] R7:S8 merged region", | |||||
// sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8"))); | |||||
// As currently implemented, cell value is copied but merged region is not copied | |||||
assertEquals("Merged cells across multiple rows", cell.getStringCellValue(), "[Merged across multiple rows] R7:S8 cell value"); | |||||
// shouldn't do 1-row merge | |||||
assertFalse(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S7")), "[Merged across multiple rows] R7:S7 merged region (one row)"); | |||||
//shouldn't do 2-row merge | |||||
assertFalse(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8")), "[Merged across multiple rows] R7:S8 merged region"); | |||||
// Make sure other rows are blank (off-by-one errors) | |||||
assertNull(sheet.getRow(5)); | |||||
assertNull(sheet.getRow(7)); | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
void testCopyMultipleRows() throws IOException { | void testCopyMultipleRows() throws IOException { | ||||
testCopyMultipleRows("XSSFSheet.copyRows.xlsx"); | |||||
final double FLOAT_PRECISION = 1e-9; | |||||
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("XSSFSheet.copyRows.xlsx")) { | |||||
final XSSFSheet sheet = wb.getSheetAt(0); | |||||
final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); | |||||
sheet.copyRows(0, 3, 8, defaultCopyPolicy); | |||||
sheet.getRow(0); | |||||
final Row srcRow1 = sheet.getRow(1); | |||||
final Row srcRow2 = sheet.getRow(2); | |||||
final Row srcRow3 = sheet.getRow(3); | |||||
final Row destHeaderRow = sheet.getRow(8); | |||||
final Row destRow1 = sheet.getRow(9); | |||||
final Row destRow2 = sheet.getRow(10); | |||||
final Row destRow3 = sheet.getRow(11); | |||||
int col = 0; | |||||
Cell cell; | |||||
// Header row should be copied | |||||
assertNotNull(destHeaderRow); | |||||
// Data rows | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Source row ->", cell.getStringCellValue()); | |||||
// Style | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Red", cell.getStringCellValue(), "[Style] B10 cell value"); | |||||
assertEquals(CellUtil.getCell(srcRow1, 1).getCellStyle(), cell.getCellStyle(), "[Style] B10 cell style"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("Blue", cell.getStringCellValue(), "[Style] B11 cell value"); | |||||
assertEquals(CellUtil.getCell(srcRow2, 1).getCellStyle(), cell.getCellStyle(), "[Style] B11 cell style"); | |||||
// Blank | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.BLANK, cell.getCellType(), "[Blank] C10 cell type"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.BLANK, cell.getCellType(), "[Blank] C11 cell type"); | |||||
// Error | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
//FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
assertEquals(CellType.ERROR, cell.getCellType(), "[Error] D10 cell type"); | |||||
assertEquals(FormulaError.NA, error, "[Error] D10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
//FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
assertEquals(CellType.ERROR, cell.getCellType(), "[Error] D11 cell type"); | |||||
assertEquals(FormulaError.NAME, error, "[Error] D11 cell value"); | |||||
// Date | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
Date date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 1).getTime(); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Date] E10 cell type"); | |||||
assertEquals(date, cell.getDateCellValue(), "[Date] E10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 2).getTime(); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Date] E11 cell type"); | |||||
assertEquals(date, cell.getDateCellValue(), "[Date] E11 cell value"); | |||||
// Boolean | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.BOOLEAN, cell.getCellType(), "[Boolean] F10 cell type"); | |||||
assertTrue(cell.getBooleanCellValue(), "[Boolean] F10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.BOOLEAN, cell.getCellType(), "[Boolean] F11 cell type"); | |||||
assertFalse(cell.getBooleanCellValue(), "[Boolean] F11 cell value"); | |||||
// String | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.STRING, cell.getCellType(), "[String] G10 cell type"); | |||||
assertEquals("Hello", cell.getStringCellValue(), "[String] G10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.STRING, cell.getCellType(), "[String] G11 cell type"); | |||||
assertEquals("World", cell.getStringCellValue(), "[String] G11 cell value"); | |||||
// Int | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Int] H10 cell type"); | |||||
assertEquals(15, (int) cell.getNumericCellValue(), "[Int] H10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Int] H11 cell type"); | |||||
assertEquals(42, (int) cell.getNumericCellValue(), "[Int] H11 cell value"); | |||||
// Float | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Float] I10 cell type"); | |||||
assertEquals(12.5, cell.getNumericCellValue(), FLOAT_PRECISION, "[Float] I10 cell value"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Float] I11 cell type"); | |||||
assertEquals(5.5, cell.getNumericCellValue(), FLOAT_PRECISION, "[Float] I11 cell value"); | |||||
// Cell Formula | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula] J10 cell type"); | |||||
assertEquals("5+2", cell.getCellFormula(), "[Cell Formula] J10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula] J11 cell type"); | |||||
assertEquals("6+18", cell.getCellFormula(), "[Cell Formula] J11 cell formula"); | |||||
// Cell Formula with Reference | |||||
col++; | |||||
// Formula row references should be adjusted by destRowNum-srcRowNum | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference] K10 cell type"); | |||||
assertEquals("J10+H$2", cell.getCellFormula(), "[Cell Formula with Reference] K10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference] K11 cell type"); | |||||
assertEquals("J11+H$2", cell.getCellFormula(), "[Cell Formula with Reference] K11 cell formula"); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference spanning multiple rows] L10 cell type"); | |||||
assertEquals("G10&\" \"&G11", cell.getCellFormula(), "[Cell Formula with Reference spanning multiple rows] L10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Reference spanning multiple rows] L11 cell type"); | |||||
assertEquals("G11&\" \"&G12", cell.getCellFormula(), "[Cell Formula with Reference spanning multiple rows] L11 cell formula"); | |||||
// Cell Formula with Area Reference | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Area Reference] M10 cell type"); | |||||
assertEquals("SUM(H10:I11)", cell.getCellFormula(), "[Cell Formula with Area Reference] M10 cell formula"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
// Also acceptable: SUM($H10:I$3), but this AreaReference isn't in ascending order | |||||
assertEquals(CellType.FORMULA, cell.getCellType(), "[Cell Formula with Area Reference] M11 cell type"); | |||||
assertEquals("SUM($H$3:I10)", cell.getCellFormula(), "[Cell Formula with Area Reference] M11 cell formula"); | |||||
// Array Formula | |||||
col++; | |||||
// cell = CellUtil.getCell(destRow1, col); | |||||
// Array formulas currently unsupported | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
// assertEquals(CellType.FORMULA, cell.getCellType(), "[Array Formula] N10 cell type"); | |||||
// assertEquals("{SUM(H10:J10*{1,2,3})}", cell.getCellFormula(), "[Array Formula] N10 cell formula"); | |||||
// cell = CellUtil.getCell(destRow2, col); | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
// assertEquals(CellType.FORMULA, cell.getCellType(). "[Array Formula] N11 cell type"); | |||||
// assertEquals("{SUM(H11:J11*{1,2,3})}", cell.getCellFormula(). "[Array Formula] N11 cell formula"); | |||||
// Data Format | |||||
col++; | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals(CellType.NUMERIC, cell.getCellType(), "[Data Format] O10 cell type"); | |||||
assertEquals(100.20, cell.getNumericCellValue(), FLOAT_PRECISION, "[Data Format] O10 cell value"); | |||||
final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; | |||||
assertEquals(moneyFormat, cell.getCellStyle().getDataFormatString(), "[Data Format] O10 cell data format"); | |||||
// Merged | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Merged cells", cell.getStringCellValue(), "[Merged] P10:Q10 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P10:Q10")), "[Merged] P10:Q10 merged region"); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("Merged cells", cell.getStringCellValue(), "[Merged] P11:Q11 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P11:Q11")), "[Merged] P11:Q11 merged region"); | |||||
// Should Q10/Q11 be checked? | |||||
// Merged across multiple rows | |||||
// Microsoft Excel 2013 does not copy a merged region unless all rows of | |||||
// the source merged region are selected | |||||
// POI's behavior should match this behavior | |||||
col += 2; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Merged cells across multiple rows", cell.getStringCellValue(), "[Merged across multiple rows] R10:S11 cell value"); | |||||
assertTrue(sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R10:S11")), "[Merged across multiple rows] R10:S11 merged region"); | |||||
// Row 3 (zero-based) was empty, so Row 11 (zero-based) should be empty too. | |||||
if (srcRow3 == null) { | |||||
assertNull(destRow3, "Row 3 was empty, so Row 11 should be empty"); | |||||
} | |||||
// Make sure other rows are blank (off-by-one errors) | |||||
assertNull(sheet.getRow(7), "Off-by-one lower edge case"); //one row above destHeaderRow | |||||
assertNull(sheet.getRow(12), "Off-by-one upper edge case"); //one row below destRow3 | |||||
} | |||||
} | } | ||||
@Test | @Test | ||||
@Test | @Test | ||||
public void bug65120() throws IOException { | public void bug65120() throws IOException { | ||||
XSSFWorkbook wb = new XSSFWorkbook(); | |||||
CreationHelper creationHelper = wb.getCreationHelper(); | |||||
Sheet sheet1 = wb.createSheet(); | |||||
Cell cell1 = sheet1.createRow(0).createCell(0); | |||||
Comment comment1 = sheet1.createDrawingPatriarch().createCellComment(creationHelper.createClientAnchor()); | |||||
cell1.setCellComment(comment1); | |||||
Sheet sheet2 = wb.createSheet(); | |||||
Cell cell2 = sheet2.createRow(0).createCell(0); | |||||
Comment comment2 = sheet2.createDrawingPatriarch().createCellComment(creationHelper.createClientAnchor()); | |||||
cell2.setCellComment(comment2); | |||||
wb.removeSheetAt(0); | |||||
Sheet sheet3 = wb.createSheet(); | |||||
Cell cell3 = sheet3.createRow(0).createCell(0); | |||||
Comment comment3 = sheet3.createDrawingPatriarch().createCellComment(creationHelper.createClientAnchor()); | |||||
cell3.setCellComment(comment3); | |||||
wb.close(); | |||||
try (XSSFWorkbook wb = new XSSFWorkbook()) { | |||||
XSSFCreationHelper creationHelper = wb.getCreationHelper(); | |||||
XSSFSheet sheet1 = wb.createSheet(); | |||||
Cell cell1 = sheet1.createRow(0).createCell(0); | |||||
XSSFDrawing dwg1 = sheet1.createDrawingPatriarch(); | |||||
XSSFComment comment1 = dwg1.createCellComment(creationHelper.createClientAnchor()); | |||||
cell1.setCellComment(comment1); | |||||
XSSFVMLDrawing vml0 = sheet1.getVMLDrawing(false); | |||||
assertEquals("/xl/drawings/vmlDrawing0.vml", vml0.getPackagePart().getPartName().getName()); | |||||
XSSFSheet sheet2 = wb.createSheet(); | |||||
Cell cell2 = sheet2.createRow(0).createCell(0); | |||||
XSSFDrawing dwg2 = sheet2.createDrawingPatriarch(); | |||||
Comment comment2 = dwg2.createCellComment(creationHelper.createClientAnchor()); | |||||
cell2.setCellComment(comment2); | |||||
XSSFVMLDrawing vml1 = sheet2.getVMLDrawing(false); | |||||
assertEquals("/xl/drawings/vmlDrawing1.vml", vml1.getPackagePart().getPartName().getName()); | |||||
wb.removeSheetAt(0); | |||||
XSSFSheet sheet3 = wb.createSheet(); | |||||
Cell cell3 = sheet3.createRow(0).createCell(0); | |||||
XSSFDrawing dwg3 = sheet3.createDrawingPatriarch(); | |||||
Comment comment3 = dwg3.createCellComment(creationHelper.createClientAnchor()); | |||||
cell3.setCellComment(comment3); | |||||
XSSFVMLDrawing vml2 = sheet3.getVMLDrawing(false); | |||||
assertEquals("/xl/drawings/vmlDrawing2.vml", vml2.getPackagePart().getPartName().getName()); | |||||
} | |||||
} | } | ||||
} | } |
} | } | ||||
public byte[] getRawData(List<? extends EmfPlusObjectData> continuedObjectData) { | public byte[] getRawData(List<? extends EmfPlusObjectData> continuedObjectData) { | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
try { | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
bos.write(getBrushBytes()); | bos.write(getBrushBytes()); | ||||
if (continuedObjectData != null) { | if (continuedObjectData != null) { | ||||
for (EmfPlusObjectData od : continuedObjectData) { | for (EmfPlusObjectData od : continuedObjectData) { | ||||
bos.write(((EmfPlusBrush)od).getBrushBytes()); | bos.write(((EmfPlusBrush)od).getBrushBytes()); | ||||
} | } | ||||
} | } | ||||
return bos.toByteArray(); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
return bos.toByteArray(); | |||||
} | } | ||||
} | } | ||||
public byte[] getRawData(List<? extends EmfPlusObjectData> continuedObjectData) { | public byte[] getRawData(List<? extends EmfPlusObjectData> continuedObjectData) { | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
try { | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
bos.write(getImageData()); | bos.write(getImageData()); | ||||
if (continuedObjectData != null) { | if (continuedObjectData != null) { | ||||
for (EmfPlusObjectData od : continuedObjectData) { | for (EmfPlusObjectData od : continuedObjectData) { | ||||
bos.write(((EmfPlusImage)od).getImageData()); | bos.write(((EmfPlusImage)od).getImageData()); | ||||
} | } | ||||
} | } | ||||
return bos.toByteArray(); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
} | } | ||||
return bos.toByteArray(); | |||||
} | } | ||||
@Override | @Override |
import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataPlus; | import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataPlus; | ||||
import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataWMF; | import org.apache.poi.hemf.record.emf.HemfComment.EmfCommentDataWMF; | ||||
import org.apache.poi.hemf.record.emf.HemfRecord; | import org.apache.poi.hemf.record.emf.HemfRecord; | ||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage; | |||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusBitmapDataType; | import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusBitmapDataType; | ||||
import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage; | import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage; | ||||
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObject; | import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObject; | ||||
@Override | @Override | ||||
public boolean hasNext() { | public boolean hasNext() { | ||||
return moveNext(); | |||||
} | |||||
private boolean moveNext() { | |||||
if (iterStack.isEmpty()) { | if (iterStack.isEmpty()) { | ||||
return false; | return false; | ||||
} | } | ||||
final int objectId = epo.getObjectId(); | final int objectId = epo.getObjectId(); | ||||
HwmfEmbedded emb = new HwmfEmbedded(); | |||||
EmfPlusImage img = epo.getObjectData(); | |||||
assert(img.getImageDataType() != null); | |||||
final HwmfEmbedded emb = new HwmfEmbedded(); | |||||
int totalSize = epo.getTotalObjectSize(); | |||||
// totalSize is only set, if there are multiple chunks | |||||
final int totalSize = epo.getTotalObjectSize() == 0 | |||||
? ((EmfPlusImage)epo.getObjectData()).getImageData().length | |||||
: epo.getTotalObjectSize(); | |||||
IOUtils.safelyAllocateCheck(totalSize, MAX_RECORD_LENGTH); | IOUtils.safelyAllocateCheck(totalSize, MAX_RECORD_LENGTH); | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(epo.getTotalObjectSize()); | |||||
try { | |||||
for (;;) { | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(totalSize)) { | |||||
boolean hasNext = false; | |||||
do { | |||||
EmfPlusImage img = epo.getObjectData(); | |||||
assert(img.getImageDataType() != null); | |||||
assert(!hasNext || img.getImageDataType() == HemfPlusImage.EmfPlusImageDataType.CONTINUED); | |||||
bos.write(img.getImageData()); | bos.write(img.getImageData()); | ||||
current = null; | current = null; | ||||
//noinspection ConstantConditions | |||||
if (hasNext() && | |||||
hasNext = moveNext() && | |||||
(current instanceof EmfPlusObject) && | (current instanceof EmfPlusObject) && | ||||
((epo = (EmfPlusObject) current).getObjectId() == objectId) && | ((epo = (EmfPlusObject) current).getObjectId() == objectId) && | ||||
bos.size() < totalSize-16 | |||||
) { | |||||
img = epo.getObjectData(); | |||||
} else { | |||||
return emb; | |||||
} | |||||
} | |||||
bos.size() < totalSize-16; | |||||
} while (hasNext); | |||||
emb.setData(bos.toByteArray()); | |||||
return emb; | |||||
} catch (IOException ignored) { | } catch (IOException ignored) { | ||||
// UnsynchronizedByteArrayOutputStream doesn't throw IOException | // UnsynchronizedByteArrayOutputStream doesn't throw IOException | ||||
return null; | return null; | ||||
} finally { | |||||
emb.setData(bos.toByteArray()); | |||||
} | } | ||||
} | } | ||||
} | } |
@Override | @Override | ||||
public byte[] getData(){ | public byte[] getData(){ | ||||
byte[] rawdata = getRawData(); | byte[] rawdata = getRawData(); | ||||
try { | |||||
try (UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream()) { | |||||
byte[] macheader = new byte[512]; | byte[] macheader = new byte[512]; | ||||
UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(); | |||||
out.write(macheader); | out.write(macheader); | ||||
int pos = CHECKSUM_SIZE*getUIDInstanceCount(); | int pos = CHECKSUM_SIZE*getUIDInstanceCount(); | ||||
byte[] pict = read(rawdata, pos); | byte[] pict = read(rawdata, pos); | ||||
throw new EOFException(); | throw new EOFException(); | ||||
} | } | ||||
byte[] chunk = new byte[4096]; | byte[] chunk = new byte[4096]; | ||||
UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(header.getWmfSize()); | |||||
try (InflaterInputStream inflater = new InflaterInputStream(bis)) { | |||||
int count; | |||||
while ((count = inflater.read(chunk)) >= 0) { | |||||
out.write(chunk, 0, count); | |||||
// PICT zip-stream can be erroneous, so we clear the array to determine | |||||
// the maximum of read bytes, after the inflater crashed | |||||
bytefill(chunk, (byte) 0); | |||||
} | |||||
} catch (Exception e) { | |||||
int lastLen; | |||||
for (lastLen = chunk.length - 1; lastLen >= 0 && chunk[lastLen] == 0; lastLen--) ; | |||||
if (++lastLen > 0) { | |||||
if (header.getWmfSize() > out.size()) { | |||||
// sometimes the wmfsize is smaller than the amount of already successfully read bytes | |||||
// in this case we take the lastLen as-is, otherwise we truncate it to the given size | |||||
lastLen = Math.min(lastLen, header.getWmfSize() - out.size()); | |||||
try (UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(header.getWmfSize())) { | |||||
try (InflaterInputStream inflater = new InflaterInputStream(bis)) { | |||||
int count; | |||||
while ((count = inflater.read(chunk)) >= 0) { | |||||
out.write(chunk, 0, count); | |||||
// PICT zip-stream can be erroneous, so we clear the array to determine | |||||
// the maximum of read bytes, after the inflater crashed | |||||
bytefill(chunk, (byte) 0); | |||||
} | |||||
} catch (Exception e) { | |||||
int lastLen = chunk.length - 1; | |||||
while (lastLen >= 0 && chunk[lastLen] == 0) { | |||||
lastLen--; | |||||
} | } | ||||
out.write(chunk, 0, lastLen); | |||||
if (++lastLen > 0) { | |||||
if (header.getWmfSize() > out.size()) { | |||||
// sometimes the wmfsize is smaller than the amount of already successfully read bytes | |||||
// in this case we take the lastLen as-is, otherwise we truncate it to the given size | |||||
lastLen = Math.min(lastLen, header.getWmfSize() - out.size()); | |||||
} | |||||
out.write(chunk, 0, lastLen); | |||||
} | |||||
// End of picture marker for PICT is 0x00 0xFF | |||||
LOG.atError().withThrowable(e).log("PICT zip-stream is invalid, read as much as possible. Uncompressed length of header: {} / Read bytes: {}", box(header.getWmfSize()), box(out.size())); | |||||
} | } | ||||
// End of picture marker for PICT is 0x00 0xFF | |||||
LOG.atError().withThrowable(e).log("PICT zip-stream is invalid, read as much as possible. Uncompressed length of header: {} / Read bytes: {}", box(header.getWmfSize()),box(out.size())); | |||||
return out.toByteArray(); | |||||
} | } | ||||
return out.toByteArray(); | |||||
} | } | ||||
@Override | @Override |
// Write out our children, and stuff them into the Escher layer | // Write out our children, and stuff them into the Escher layer | ||||
// Grab the children's data | // Grab the children's data | ||||
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(); | |||||
for (org.apache.poi.hslf.record.Record r : _children) r.writeOut(baos); | |||||
byte[] data = baos.toByteArray(); | |||||
// Save in the escher layer | |||||
_escherRecord.setData(data); | |||||
try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) { | |||||
for (org.apache.poi.hslf.record.Record r : _children) { | |||||
r.writeOut(baos); | |||||
} | |||||
// Save in the escher layer | |||||
_escherRecord.setData(baos.toByteArray()); | |||||
} | |||||
} | } | ||||
/** | /** |
import java.util.zip.DeflaterOutputStream; | import java.util.zip.DeflaterOutputStream; | ||||
import java.util.zip.InflaterInputStream; | import java.util.zip.InflaterInputStream; | ||||
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; | |||||
import org.apache.commons.io.input.BoundedInputStream; | import org.apache.commons.io.input.BoundedInputStream; | ||||
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; | |||||
import org.apache.poi.util.GenericRecordUtil; | import org.apache.poi.util.GenericRecordUtil; | ||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.LittleEndian; | import org.apache.poi.util.LittleEndian; | ||||
* @param data the embedded data. | * @param data the embedded data. | ||||
*/ | */ | ||||
public void setData(byte[] data) throws IOException { | public void setData(byte[] data) throws IOException { | ||||
UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(); | |||||
//first four bytes is the length of the raw data | |||||
byte[] b = new byte[4]; | |||||
LittleEndian.putInt(b, 0, data.length); | |||||
out.write(b); | |||||
DeflaterOutputStream def = new DeflaterOutputStream(out); | |||||
def.write(data, 0, data.length); | |||||
def.finish(); | |||||
// TODO: CHECK if it's correct that DeflaterOutputStream is only finished and not closed? | |||||
_data = out.toByteArray(); | |||||
LittleEndian.putInt(_header, 4, _data.length); | |||||
try (UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(); | |||||
DeflaterOutputStream def = new DeflaterOutputStream(out)) { | |||||
//first four bytes is the length of the raw data | |||||
byte[] b = new byte[4]; | |||||
LittleEndian.putInt(b, 0, data.length); | |||||
out.write(b); | |||||
def.write(data, 0, data.length); | |||||
def.finish(); | |||||
// TODO: CHECK if it's correct that DeflaterOutputStream is only finished and not closed? | |||||
_data = out.toByteArray(); | |||||
LittleEndian.putInt(_header, 4, _data.length); | |||||
} | |||||
} | } | ||||
/** | /** |
//cached dgg | //cached dgg | ||||
private EscherDggRecord dgg; | private EscherDggRecord dgg; | ||||
protected PPDrawingGroup(byte[] source, int start, int len) { | |||||
PPDrawingGroup(byte[] source, int start, int len) { | |||||
// Get the header | // Get the header | ||||
_header = Arrays.copyOfRange(source, start, start+8); | _header = Arrays.copyOfRange(source, start, start+8); | ||||
@Override | @Override | ||||
public void writeOut(OutputStream out) throws IOException { | public void writeOut(OutputStream out) throws IOException { | ||||
UnsynchronizedByteArrayOutputStream bout = new UnsynchronizedByteArrayOutputStream(); | |||||
for (EscherRecord r : dggContainer) { | |||||
if (r.getRecordId() == EscherContainerRecord.BSTORE_CONTAINER){ | |||||
EscherContainerRecord bstore = (EscherContainerRecord)r; | |||||
UnsynchronizedByteArrayOutputStream b2 = new UnsynchronizedByteArrayOutputStream(); | |||||
for (EscherRecord br : bstore) { | |||||
byte[] b = new byte[36+8]; | |||||
br.serialize(0, b); | |||||
b2.write(b); | |||||
byte[] bstorehead = new byte[8]; | |||||
byte[] recordBytes = new byte[36 + 8]; | |||||
try (UnsynchronizedByteArrayOutputStream bout = new UnsynchronizedByteArrayOutputStream(); | |||||
UnsynchronizedByteArrayOutputStream recordBuf = new UnsynchronizedByteArrayOutputStream()) { | |||||
for (EscherRecord r : dggContainer) { | |||||
if (r.getRecordId() == EscherContainerRecord.BSTORE_CONTAINER) { | |||||
EscherContainerRecord bstore = (EscherContainerRecord) r; | |||||
recordBuf.reset(); | |||||
for (EscherRecord br : bstore) { | |||||
br.serialize(0, recordBytes); | |||||
recordBuf.write(recordBytes); | |||||
} | |||||
LittleEndian.putShort(bstorehead, 0, bstore.getOptions()); | |||||
LittleEndian.putShort(bstorehead, 2, bstore.getRecordId()); | |||||
LittleEndian.putInt(bstorehead, 4, recordBuf.size()); | |||||
bout.write(bstorehead); | |||||
recordBuf.writeTo(bout); | |||||
} else { | |||||
bout.write(r.serialize()); | |||||
} | } | ||||
byte[] bstorehead = new byte[8]; | |||||
LittleEndian.putShort(bstorehead, 0, bstore.getOptions()); | |||||
LittleEndian.putShort(bstorehead, 2, bstore.getRecordId()); | |||||
LittleEndian.putInt(bstorehead, 4, b2.size()); | |||||
bout.write(bstorehead); | |||||
bout.write(b2.toByteArray()); | |||||
} else { | |||||
bout.write(r.serialize()); | |||||
} | } | ||||
} | |||||
int size = bout.size(); | |||||
int size = bout.size(); | |||||
// Update the size (header bytes 5-8) | |||||
LittleEndian.putInt(_header,4,size+8); | |||||
// Update the size (header bytes 5-8) | |||||
LittleEndian.putInt(_header, 4, size + 8); | |||||
// Write out our header | |||||
out.write(_header); | |||||
// Write out our header | |||||
out.write(_header); | |||||
byte[] dgghead = new byte[8]; | |||||
LittleEndian.putShort(dgghead, 0, dggContainer.getOptions()); | |||||
LittleEndian.putShort(dgghead, 2, dggContainer.getRecordId()); | |||||
LittleEndian.putInt(dgghead, 4, size); | |||||
out.write(dgghead); | |||||
byte[] dgghead = new byte[8]; | |||||
LittleEndian.putShort(dgghead, 0, dggContainer.getOptions()); | |||||
LittleEndian.putShort(dgghead, 2, dggContainer.getRecordId()); | |||||
LittleEndian.putInt(dgghead, 4, size); | |||||
out.write(dgghead); | |||||
// Finally, write out the children | |||||
bout.writeTo(out); | |||||
// Finally, write out the children | |||||
bout.writeTo(out); | |||||
} | |||||
} | } | ||||
public EscherContainerRecord getDggContainer(){ | public EscherContainerRecord getDggContainer(){ |
*/ | */ | ||||
public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException { | public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException { | ||||
// Create a UnsynchronizedByteArrayOutputStream to hold everything in | // Create a UnsynchronizedByteArrayOutputStream to hold everything in | ||||
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(); | |||||
// Write out our header, less the size | |||||
baos.write(new byte[] {headerA,headerB}); | |||||
byte[] typeB = new byte[2]; | |||||
LittleEndian.putShort(typeB,0,(short)type); | |||||
baos.write(typeB); | |||||
baos.write(new byte[] {0,0,0,0}); | |||||
// Write out our children | |||||
for (Record aChildren : children) { | |||||
aChildren.writeOut(baos); | |||||
} | |||||
try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) { | |||||
// Write out our header, less the size | |||||
baos.write(new byte[]{headerA, headerB}); | |||||
byte[] typeB = new byte[2]; | |||||
LittleEndian.putShort(typeB, 0, (short) type); | |||||
baos.write(typeB); | |||||
baos.write(new byte[]{0, 0, 0, 0}); | |||||
// Write out our children | |||||
for (Record aChildren : children) { | |||||
aChildren.writeOut(baos); | |||||
} | |||||
// Grab the bytes back | |||||
byte[] toWrite = baos.toByteArray(); | |||||
// Grab the bytes back | |||||
byte[] toWrite = baos.toByteArray(); | |||||
// Update our header with the size | |||||
// Don't forget to knock 8 more off, since we don't include the | |||||
// header in the size | |||||
LittleEndian.putInt(toWrite,4,(toWrite.length-8)); | |||||
// Update our header with the size | |||||
// Don't forget to knock 8 more off, since we don't include the header in the size | |||||
LittleEndian.putInt(toWrite, 4, (toWrite.length - 8)); | |||||
// Write out the bytes | |||||
out.write(toWrite); | |||||
// Write out the bytes | |||||
out.write(toWrite); | |||||
} | |||||
} | } | ||||
/** | /** |
*/ | */ | ||||
private void updateRawContents() throws IOException { | private void updateRawContents() throws IOException { | ||||
if (initialised) { | if (initialised) { | ||||
// Only update the style bytes, if the styles have been potentially | |||||
// changed | |||||
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(); | |||||
// First up, we need to serialise the paragraph properties | |||||
for(TextPropCollection tpc : paragraphStyles) { | |||||
tpc.writeOut(baos); | |||||
// Only update the style bytes, if the styles have been potentially changed | |||||
try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) { | |||||
// First up, we need to serialise the paragraph properties | |||||
for (TextPropCollection tpc : paragraphStyles) { | |||||
tpc.writeOut(baos); | |||||
} | |||||
// Now, we do the character ones | |||||
for (TextPropCollection tpc : charStyles) { | |||||
tpc.writeOut(baos); | |||||
} | |||||
rawContents = baos.toByteArray(); | |||||
} | } | ||||
// Now, we do the character ones | |||||
for(TextPropCollection tpc : charStyles) { | |||||
tpc.writeOut(baos); | |||||
} | |||||
rawContents = baos.toByteArray(); | |||||
} | } | ||||
// Now ensure that the header size is correct | // Now ensure that the header size is correct |
*/ | */ | ||||
public void setParentSize(int size) { | public void setParentSize(int size) { | ||||
assert(size > 0); | assert(size > 0); | ||||
int covered = 0; | |||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
TextSpecInfoRun[] runs = getTextSpecInfoRuns(); | |||||
assert(runs.length > 0); | |||||
for (int i=0; i<runs.length && covered < size; i++) { | |||||
TextSpecInfoRun run = runs[i]; | |||||
if (covered + run.getLength() > size || i == runs.length-1) { | |||||
run.setLength(size-covered); | |||||
} | |||||
covered += run.getLength(); | |||||
try { | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
TextSpecInfoRun[] runs = getTextSpecInfoRuns(); | |||||
int remaining = size; | |||||
int idx = 0; | |||||
for (TextSpecInfoRun run : runs) { | |||||
int len = run.getLength(); | |||||
if (len > remaining || idx == runs.length - 1) { | |||||
run.setLength(len = remaining); | |||||
} | |||||
remaining -= len; | |||||
run.writeOut(bos); | run.writeOut(bos); | ||||
} catch (IOException e) { | |||||
throw new HSLFException(e); | |||||
idx++; | |||||
} | } | ||||
} | |||||
_data = bos.toByteArray(); | |||||
// Update the size (header bytes 5-8) | |||||
LittleEndian.putInt(_header, 4, _data.length); | |||||
_data = bos.toByteArray(); | |||||
// Update the size (header bytes 5-8) | |||||
LittleEndian.putInt(_header, 4, _data.length); | |||||
} catch (IOException e) { | |||||
throw new HSLFException(e); | |||||
} | |||||
} | } | ||||
/** | /** |
if (!(current instanceof HwmfEscape)) { | if (!(current instanceof HwmfEscape)) { | ||||
return null; | return null; | ||||
} | } | ||||
final HwmfEscape esc = (HwmfEscape)current; | |||||
assert(esc.getEscapeFunction() == EscapeFunction.META_ESCAPE_ENHANCED_METAFILE); | |||||
WmfEscapeEMF img = esc.getEscapeData(); | |||||
assert(img.isValid()); | |||||
current = null; | |||||
final HwmfEmbedded emb = new HwmfEmbedded(); | final HwmfEmbedded emb = new HwmfEmbedded(); | ||||
emb.setEmbeddedType(HwmfEmbeddedType.EMF); | emb.setEmbeddedType(HwmfEmbeddedType.EMF); | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
try { | |||||
for (;;) { | |||||
bos.write(img.getEmfData()); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
WmfEscapeEMF img; | |||||
do { | |||||
final HwmfEscape esc = (HwmfEscape)current; | |||||
assert(esc.getEscapeFunction() == EscapeFunction.META_ESCAPE_ENHANCED_METAFILE); | |||||
img = esc.getEscapeData(); | |||||
assert(img.isValid()); | |||||
bos.write(img.getEmfData()); | |||||
current = null; | current = null; | ||||
if (img.getRemainingBytes() > 0 && hasNext() && (current instanceof HwmfEscape)) { | |||||
img = ((HwmfEscape)current).getEscapeData(); | |||||
} else { | |||||
return emb; | |||||
} | |||||
} | |||||
} while (img.getRemainingBytes() > 0 && hasNext() && (current instanceof HwmfEscape)); | |||||
emb.setData(bos.toByteArray()); | |||||
return emb; | |||||
} catch (IOException ignored) { | } catch (IOException ignored) { | ||||
// UnsynchronizedByteArrayOutputStream doesn't throw IOException | // UnsynchronizedByteArrayOutputStream doesn't throw IOException | ||||
return null; | return null; | ||||
} finally { | |||||
emb.setData(bos.toByteArray()); | |||||
} | } | ||||
} | } | ||||
} | } |
} | } | ||||
jar { | jar { | ||||
dependsOn cacheJava9 | |||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) { | if (JavaVersion.current() == JavaVersion.VERSION_1_8) { | ||||
into('META-INF/versions/9') { | into('META-INF/versions/9') { | ||||
from JAVA9_SRC include '*.class' | from JAVA9_SRC include '*.class' | ||||
// Create a separate jar for test-code to depend on it in other projects | // Create a separate jar for test-code to depend on it in other projects | ||||
// See http://stackoverflow.com/questions/5144325/gradle-test-dependency | // See http://stackoverflow.com/questions/5144325/gradle-test-dependency | ||||
task testJar(type: Jar, dependsOn: testClasses) { | |||||
task testJar(type: Jar, dependsOn: [ testClasses, cacheTest9 ]) { | |||||
destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") | destinationDirectory = file("../build/dist/maven/${project.archivesBaseName}-tests") | ||||
classifier 'tests' | classifier 'tests' |
private final byte[] bytes = new byte[LENGTH]; | private final byte[] bytes = new byte[LENGTH]; | ||||
/** | /** | ||||
* Creates a {@link ClassID} and reads its value from a byte array. | |||||
* Creates a ClassID and reads its value from a byte array. | |||||
* | * | ||||
* @param src The byte array to read from. | * @param src The byte array to read from. | ||||
* @param offset The offset of the first byte to read. | * @param offset The offset of the first byte to read. | ||||
/** | /** | ||||
* Creates a {@link ClassID} and initializes its value with 0x00 bytes. | |||||
* Creates a ClassID and initializes its value with 0x00 bytes. | |||||
*/ | */ | ||||
public ClassID() { | public ClassID() { | ||||
Arrays.fill(bytes, (byte)0); | Arrays.fill(bytes, (byte)0); | ||||
/** | /** | ||||
* Creates a {@link ClassID} from a human-readable representation of the Class ID in standard | |||||
* Creates a ClassID from a human-readable representation of the Class ID in standard | |||||
* format {@code "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"}. | * format {@code "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"}. | ||||
* | * | ||||
* @param externalForm representation of the Class ID represented by this object. | * @param externalForm representation of the Class ID represented by this object. | ||||
/** | /** | ||||
* @return The number of bytes occupied by this object in the byte stream. | * @return The number of bytes occupied by this object in the byte stream. | ||||
*/ | */ | ||||
@SuppressWarnings("java:S1845") | |||||
public int length() { | public int length() { | ||||
return LENGTH; | return LENGTH; | ||||
} | } | ||||
/** | /** | ||||
* Gets the bytes making out the class ID. They are returned in correct order, i.e. big-endian. | * Gets the bytes making out the class ID. They are returned in correct order, i.e. big-endian. | ||||
* | * | ||||
return bytes; | return bytes; | ||||
} | } | ||||
/** | /** | ||||
* Sets the bytes making out the class ID. | * Sets the bytes making out the class ID. | ||||
* | * | ||||
System.arraycopy(bytes, 0, this.bytes, 0, LENGTH); | System.arraycopy(bytes, 0, this.bytes, 0, LENGTH); | ||||
} | } | ||||
/** | /** | ||||
* Reads the class ID's value from a byte array by turning little-endian into big-endian. | * Reads the class ID's value from a byte array by turning little-endian into big-endian. | ||||
* | * | ||||
; | ; | ||||
} | } | ||||
/** | |||||
* @see Object#hashCode() | |||||
*/ | |||||
@Override | @Override | ||||
public int hashCode() { | public int hashCode() { | ||||
return toString().hashCode(); | return toString().hashCode(); |
} | } | ||||
private byte[] toBytes() throws WritingNotSupportedException, IOException { | private byte[] toBytes() throws WritingNotSupportedException, IOException { | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
LittleEndianOutputStream leos = new LittleEndianOutputStream(bos); | |||||
/* Write the number of sections in this property set stream. */ | |||||
final int nrSections = getSectionCount(); | |||||
/* Write the property set's header. */ | |||||
leos.writeShort(getByteOrder()); | |||||
leos.writeShort(getFormat()); | |||||
leos.writeInt(getOSVersion()); | |||||
putClassId(bos, getClassID()); | |||||
leos.writeInt(nrSections); | |||||
assert(bos.size() == OFFSET_HEADER); | |||||
final int[][] offsets = new int[getSectionCount()][2]; | |||||
/* Write the section list, i.e. the references to the sections. Each | |||||
* entry in the section list consist of the section's class ID and the | |||||
* section's offset relative to the beginning of the stream. */ | |||||
int secCnt = 0; | |||||
for (final Section section : getSections()) { | |||||
final ClassID formatID = section.getFormatID(); | |||||
if (formatID == null) { | |||||
throw new NoFormatIDException(); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
LittleEndianOutputStream leos = new LittleEndianOutputStream(bos)) { | |||||
/* Write the number of sections in this property set stream. */ | |||||
final int nrSections = getSectionCount(); | |||||
/* Write the property set's header. */ | |||||
leos.writeShort(getByteOrder()); | |||||
leos.writeShort(getFormat()); | |||||
leos.writeInt(getOSVersion()); | |||||
putClassId(bos, getClassID()); | |||||
leos.writeInt(nrSections); | |||||
assert (bos.size() == OFFSET_HEADER); | |||||
final int[][] offsets = new int[getSectionCount()][2]; | |||||
/* Write the section list, i.e. the references to the sections. Each | |||||
* entry in the section list consist of the section's class ID and the | |||||
* section's offset relative to the beginning of the stream. */ | |||||
int secCnt = 0; | |||||
for (final Section section : getSections()) { | |||||
final ClassID formatID = section.getFormatID(); | |||||
if (formatID == null) { | |||||
throw new NoFormatIDException(); | |||||
} | |||||
putClassId(bos, formatID); | |||||
offsets[secCnt++][0] = bos.size(); | |||||
// offset dummy - filled later | |||||
leos.writeInt(-1); | |||||
} | } | ||||
putClassId(bos, formatID); | |||||
offsets[secCnt++][0] = bos.size(); | |||||
// offset dummy - filled later | |||||
leos.writeInt(-1); | |||||
} | |||||
/* Write the sections themselves. */ | |||||
secCnt = 0; | |||||
for (final Section section : getSections()) { | |||||
offsets[secCnt++][1] = bos.size(); | |||||
section.write(bos); | |||||
} | |||||
/* Write the sections themselves. */ | |||||
secCnt = 0; | |||||
for (final Section section : getSections()) { | |||||
offsets[secCnt++][1] = bos.size(); | |||||
section.write(bos); | |||||
} | |||||
byte[] result = bos.toByteArray(); | |||||
for (int[] off : offsets) { | |||||
LittleEndian.putInt(result, off[0], off[1]); | |||||
} | |||||
byte[] result = bos.toByteArray(); | |||||
for (int[] off : offsets) { | |||||
LittleEndian.putInt(result, off[0], off[1]); | |||||
} | |||||
return result; | |||||
return result; | |||||
} | |||||
} | } | ||||
/** | /** |
} | } | ||||
final int[][] offsets = new int[properties.size()][2]; | final int[][] offsets = new int[properties.size()][2]; | ||||
final UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
final LittleEndianOutputStream leos = new LittleEndianOutputStream(bos); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
LittleEndianOutputStream leos = new LittleEndianOutputStream(bos)) { | |||||
/* Write the section's length - dummy value, fixed later */ | |||||
leos.writeInt(-1); | |||||
/* Write the section's length - dummy value, fixed later */ | |||||
leos.writeInt(-1); | |||||
/* Write the section's number of properties: */ | |||||
leos.writeInt(properties.size()); | |||||
/* Write the section's number of properties: */ | |||||
leos.writeInt(properties.size()); | |||||
int propCnt = 0; | |||||
for (Property p : properties.values()) { | |||||
/* Write the property list entry. */ | |||||
leos.writeUInt(p.getID()); | |||||
// dummy offset to be fixed later | |||||
offsets[propCnt++][0] = bos.size(); | |||||
leos.writeInt(-1); | |||||
} | |||||
int propCnt = 0; | |||||
for (Property p : properties.values()) { | |||||
/* Write the property list entry. */ | |||||
leos.writeUInt(p.getID()); | |||||
// dummy offset to be fixed later | |||||
offsets[propCnt++][0] = bos.size(); | |||||
leos.writeInt(-1); | |||||
} | |||||
/* Write the properties and the property list into their respective | |||||
* streams: */ | |||||
propCnt = 0; | |||||
for (Property p : properties.values()) { | |||||
offsets[propCnt++][1] = bos.size(); | |||||
/* If the property ID is not equal 0 we write the property and all | |||||
* is fine. However, if it equals 0 we have to write the section's | |||||
* dictionary which has an implicit type only and an explicit | |||||
* value. */ | |||||
if (p.getID() != 0) { | |||||
/* Write the property and update the position to the next | |||||
* property. */ | |||||
p.write(bos, codepage); | |||||
} else { | |||||
writeDictionary(bos, codepage); | |||||
/* Write the properties and the property list into their respective | |||||
* streams: */ | |||||
propCnt = 0; | |||||
for (Property p : properties.values()) { | |||||
offsets[propCnt++][1] = bos.size(); | |||||
/* If the property ID is not equal 0 we write the property and all | |||||
* is fine. However, if it equals 0 we have to write the section's | |||||
* dictionary which has an implicit type only and an explicit | |||||
* value. */ | |||||
if (p.getID() != 0) { | |||||
/* Write the property and update the position to the next | |||||
* property. */ | |||||
p.write(bos, codepage); | |||||
} else { | |||||
writeDictionary(bos, codepage); | |||||
} | |||||
} | } | ||||
} | |||||
byte[] result = bos.toByteArray(); | |||||
LittleEndian.putInt(result, 0, bos.size()); | |||||
byte[] result = bos.toByteArray(); | |||||
LittleEndian.putInt(result, 0, bos.size()); | |||||
for (int[] off : offsets) { | |||||
LittleEndian.putUInt(result, off[0], off[1]); | |||||
} | |||||
for (int[] off : offsets) { | |||||
LittleEndian.putUInt(result, off[0], off[1]); | |||||
} | |||||
out.write(result); | |||||
out.write(result); | |||||
return bos.size(); | |||||
return bos.size(); | |||||
} | |||||
} | } | ||||
/** | /** |
*/ | */ | ||||
@Deprecated | @Deprecated | ||||
public byte[] readAllContinuedRemainder() { | public byte[] readAllContinuedRemainder() { | ||||
UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(2 * MAX_RECORD_DATA_SIZE); | |||||
try (UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream(2 * MAX_RECORD_DATA_SIZE)) { | |||||
while (true) { | |||||
byte[] b = readRemainder(); | |||||
out.write(b, 0, b.length); | |||||
if (!isContinueNext()) { | |||||
break; | |||||
while (true) { | |||||
byte[] b = readRemainder(); | |||||
out.write(b, 0, b.length); | |||||
if (!isContinueNext()) { | |||||
break; | |||||
} | |||||
nextRecord(); | |||||
} | } | ||||
nextRecord(); | |||||
return out.toByteArray(); | |||||
} catch (IOException ex) { | |||||
throw new RecordFormatException(ex); | |||||
} | } | ||||
return out.toByteArray(); | |||||
} | } | ||||
/** The remaining number of bytes in the <i>current</i> record. | /** The remaining number of bytes in the <i>current</i> record. |
* Only newer style formatting rules have priorities. For older ones, | * Only newer style formatting rules have priorities. For older ones, | ||||
* we don't know priority for these, other than definition/model order, | * we don't know priority for these, other than definition/model order, | ||||
* which appears to be what Excel uses. | * which appears to be what Excel uses. | ||||
* @see org.apache.poi.ss.usermodel.ConditionalFormattingRule#getPriority() | |||||
*/ | */ | ||||
@Override | |||||
public int getPriority() { | public int getPriority() { | ||||
CFRule12Record rule12 = getCFRule12Record(false); | CFRule12Record rule12 = getCFRule12Record(false); | ||||
if (rule12 == null) return 0; | if (rule12 == null) return 0; | ||||
/** | /** | ||||
* Always true for HSSF files, per Microsoft Excel documentation | * Always true for HSSF files, per Microsoft Excel documentation | ||||
* @see org.apache.poi.ss.usermodel.ConditionalFormattingRule#getStopIfTrue() | |||||
*/ | */ | ||||
@Override | |||||
public boolean getStopIfTrue() { | public boolean getStopIfTrue() { | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Always null for HSSF records, until someone figures out where to find it | * Always null for HSSF records, until someone figures out where to find it | ||||
* @see org.apache.poi.ss.usermodel.ConditionalFormattingRule#getNumberFormat() | |||||
*/ | */ | ||||
@Override | |||||
public ExcelNumberFormat getNumberFormat() { | public ExcelNumberFormat getNumberFormat() { | ||||
return null; | return null; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return - font formatting object if defined, <code>null</code> otherwise | |||||
* @return - font formatting object if defined, {@code null} otherwise | |||||
*/ | */ | ||||
@Override | |||||
public HSSFFontFormatting getFontFormatting() { | public HSSFFontFormatting getFontFormatting() { | ||||
return getFontFormatting(false); | return getFontFormatting(false); | ||||
} | } | ||||
/** | /** | ||||
* create a new font formatting structure if it does not exist, | * create a new font formatting structure if it does not exist, | ||||
* otherwise just return existing object. | * otherwise just return existing object. | ||||
* @return - font formatting object, never returns <code>null</code>. | |||||
* @return - font formatting object, never returns {@code null}. | |||||
*/ | */ | ||||
@Override | |||||
public HSSFFontFormatting createFontFormatting() { | public HSSFFontFormatting createFontFormatting() { | ||||
return getFontFormatting(true); | return getFontFormatting(true); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return - border formatting object if defined, <code>null</code> otherwise | |||||
* @return - border formatting object if defined, {@code null} otherwise | |||||
*/ | */ | ||||
@Override | |||||
public HSSFBorderFormatting getBorderFormatting() { | public HSSFBorderFormatting getBorderFormatting() { | ||||
return getBorderFormatting(false); | return getBorderFormatting(false); | ||||
} | } | ||||
/** | /** | ||||
* create a new border formatting structure if it does not exist, | * create a new border formatting structure if it does not exist, | ||||
* otherwise just return existing object. | * otherwise just return existing object. | ||||
* @return - border formatting object, never returns <code>null</code>. | |||||
* @return - border formatting object, never returns {@code null}. | |||||
*/ | */ | ||||
@Override | |||||
public HSSFBorderFormatting createBorderFormatting() { | public HSSFBorderFormatting createBorderFormatting() { | ||||
return getBorderFormatting(true); | return getBorderFormatting(true); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return - pattern formatting object if defined, <code>null</code> otherwise | |||||
* @return - pattern formatting object if defined, {@code null} otherwise | |||||
*/ | */ | ||||
@Override | |||||
public HSSFPatternFormatting getPatternFormatting() | public HSSFPatternFormatting getPatternFormatting() | ||||
{ | { | ||||
return getPatternFormatting(false); | return getPatternFormatting(false); | ||||
/** | /** | ||||
* create a new pattern formatting structure if it does not exist, | * create a new pattern formatting structure if it does not exist, | ||||
* otherwise just return existing object. | * otherwise just return existing object. | ||||
* @return - pattern formatting object, never returns <code>null</code>. | |||||
* @return - pattern formatting object, never returns {@code null}. | |||||
*/ | */ | ||||
@Override | |||||
public HSSFPatternFormatting createPatternFormatting() | public HSSFPatternFormatting createPatternFormatting() | ||||
{ | { | ||||
return getPatternFormatting(true); | return getPatternFormatting(true); | ||||
} | } | ||||
/** | /** | ||||
* @return databar / data-bar formatting object if defined, <code>null</code> otherwise | |||||
* @return databar / data-bar formatting object if defined, {@code null} otherwise | |||||
*/ | */ | ||||
@Override | |||||
public HSSFDataBarFormatting getDataBarFormatting() { | public HSSFDataBarFormatting getDataBarFormatting() { | ||||
return getDataBarFormatting(false); | return getDataBarFormatting(false); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return icon / multi-state formatting object if defined, <code>null</code> otherwise | |||||
* @return icon / multi-state formatting object if defined, {@code null} otherwise | |||||
*/ | */ | ||||
@Override | |||||
public HSSFIconMultiStateFormatting getMultiStateFormatting() { | public HSSFIconMultiStateFormatting getMultiStateFormatting() { | ||||
return getMultiStateFormatting(false); | return getMultiStateFormatting(false); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @return color scale / gradient formatting object if defined, <code>null</code> otherwise | |||||
* @return color scale / gradient formatting object if defined, {@code null} otherwise | |||||
*/ | */ | ||||
@Override | |||||
public HSSFColorScaleFormatting getColorScaleFormatting() { | public HSSFColorScaleFormatting getColorScaleFormatting() { | ||||
return getColorScaleFormatting(false); | return getColorScaleFormatting(false); | ||||
} | } | ||||
/** | /** | ||||
* always null (not a filter condition) or {@link ConditionFilterType#FILTER} if it is. | * always null (not a filter condition) or {@link ConditionFilterType#FILTER} if it is. | ||||
* @see org.apache.poi.ss.usermodel.ConditionalFormattingRule#getConditionFilterType() | |||||
*/ | */ | ||||
@Override | |||||
public ConditionFilterType getConditionFilterType() { | public ConditionFilterType getConditionFilterType() { | ||||
return getConditionType() == ConditionType.FILTER ? ConditionFilterType.FILTER : null; | return getConditionType() == ConditionType.FILTER ? ConditionFilterType.FILTER : null; | ||||
} | } | ||||
@Override | |||||
public ConditionFilterData getFilterConfiguration() { | public ConditionFilterData getFilterConfiguration() { | ||||
return null; | return null; | ||||
} | } | ||||
return cfRuleRecord.getComparisonOperation(); | return cfRuleRecord.getComparisonOperation(); | ||||
} | } | ||||
@Override | |||||
public String getFormula1() | public String getFormula1() | ||||
{ | { | ||||
return toFormulaString(cfRuleRecord.getParsedExpression1()); | return toFormulaString(cfRuleRecord.getParsedExpression1()); | ||||
} | } | ||||
@Override | |||||
public String getFormula2() { | public String getFormula2() { | ||||
byte conditionType = cfRuleRecord.getConditionType(); | byte conditionType = cfRuleRecord.getConditionType(); | ||||
if (conditionType == CELL_COMPARISON) { | if (conditionType == CELL_COMPARISON) { | ||||
return null; | return null; | ||||
} | } | ||||
@Override | |||||
public String getText() { | public String getText() { | ||||
return null; // not available here, unless it exists and is unimplemented in cfRuleRecord | return null; // not available here, unless it exists and is unimplemented in cfRuleRecord | ||||
} | } | ||||
* Conditional format rules don't define stripes, so always 0 | * Conditional format rules don't define stripes, so always 0 | ||||
* @see org.apache.poi.ss.usermodel.DifferentialStyleProvider#getStripeSize() | * @see org.apache.poi.ss.usermodel.DifferentialStyleProvider#getStripeSize() | ||||
*/ | */ | ||||
@Override | |||||
public int getStripeSize() { | public int getStripeSize() { | ||||
return 0; | return 0; | ||||
} | } |
private final HSSFSheet sheet; | private final HSSFSheet sheet; | ||||
private final HSSFWorkbook workbook; | private final HSSFWorkbook workbook; | ||||
protected HSSFConditionalFormattingThreshold(Threshold threshold, HSSFSheet sheet) { | |||||
HSSFConditionalFormattingThreshold(Threshold threshold, HSSFSheet sheet) { | |||||
this.threshold = threshold; | this.threshold = threshold; | ||||
this.sheet = sheet; | this.sheet = sheet; | ||||
this.workbook = sheet.getWorkbook(); | this.workbook = sheet.getWorkbook(); | ||||
} | } | ||||
protected Threshold getThreshold() { | |||||
Threshold getThreshold() { | |||||
return threshold; | return threshold; | ||||
} | } | ||||
@Override | |||||
public RangeType getRangeType() { | public RangeType getRangeType() { | ||||
return RangeType.byId(threshold.getType()); | return RangeType.byId(threshold.getType()); | ||||
} | } | ||||
@Override | |||||
public void setRangeType(RangeType type) { | public void setRangeType(RangeType type) { | ||||
threshold.setType((byte)type.id); | threshold.setType((byte)type.id); | ||||
} | } | ||||
@Override | |||||
public String getFormula() { | public String getFormula() { | ||||
return toFormulaString(threshold.getParsedExpression(), workbook); | return toFormulaString(threshold.getParsedExpression(), workbook); | ||||
} | } | ||||
@Override | |||||
public void setFormula(String formula) { | public void setFormula(String formula) { | ||||
threshold.setParsedExpression(parseFormula(formula, sheet)); | threshold.setParsedExpression(parseFormula(formula, sheet)); | ||||
} | } | ||||
@Override | |||||
public Double getValue() { | public Double getValue() { | ||||
return threshold.getValue(); | return threshold.getValue(); | ||||
} | } | ||||
@Override | |||||
public void setValue(Double value) { | public void setValue(Double value) { | ||||
threshold.setValue(value); | threshold.setValue(value); | ||||
} | } |
import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold; | import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold; | ||||
/** | /** | ||||
* High level representation for Icon / Multi-State Formatting | |||||
* High level representation for Icon / Multi-State Formatting | |||||
* component of Conditional Formatting settings | * component of Conditional Formatting settings | ||||
*/ | */ | ||||
public final class HSSFIconMultiStateFormatting implements org.apache.poi.ss.usermodel.IconMultiStateFormatting { | public final class HSSFIconMultiStateFormatting implements org.apache.poi.ss.usermodel.IconMultiStateFormatting { | ||||
private final HSSFSheet sheet; | private final HSSFSheet sheet; | ||||
private final CFRule12Record cfRule12Record; | |||||
private final IconMultiStateFormatting iconFormatting; | private final IconMultiStateFormatting iconFormatting; | ||||
protected HSSFIconMultiStateFormatting(CFRule12Record cfRule12Record, HSSFSheet sheet) { | |||||
HSSFIconMultiStateFormatting(CFRule12Record cfRule12Record, HSSFSheet sheet) { | |||||
this.sheet = sheet; | this.sheet = sheet; | ||||
this.cfRule12Record = cfRule12Record; | |||||
this.iconFormatting = this.cfRule12Record.getMultiStateFormatting(); | |||||
this.iconFormatting = cfRule12Record.getMultiStateFormatting(); | |||||
} | } | ||||
@Override | |||||
public IconSet getIconSet() { | public IconSet getIconSet() { | ||||
return iconFormatting.getIconSet(); | return iconFormatting.getIconSet(); | ||||
} | } | ||||
@Override | |||||
public void setIconSet(IconSet set) { | public void setIconSet(IconSet set) { | ||||
iconFormatting.setIconSet(set); | iconFormatting.setIconSet(set); | ||||
} | } | ||||
@Override | |||||
public boolean isIconOnly() { | public boolean isIconOnly() { | ||||
return iconFormatting.isIconOnly(); | return iconFormatting.isIconOnly(); | ||||
} | } | ||||
@Override | |||||
public void setIconOnly(boolean only) { | public void setIconOnly(boolean only) { | ||||
iconFormatting.setIconOnly(only); | iconFormatting.setIconOnly(only); | ||||
} | } | ||||
@Override | |||||
public boolean isReversed() { | public boolean isReversed() { | ||||
return iconFormatting.isReversed(); | return iconFormatting.isReversed(); | ||||
} | } | ||||
@Override | |||||
public void setReversed(boolean reversed) { | public void setReversed(boolean reversed) { | ||||
iconFormatting.setReversed(reversed); | iconFormatting.setReversed(reversed); | ||||
} | } | ||||
@Override | |||||
public HSSFConditionalFormattingThreshold[] getThresholds() { | public HSSFConditionalFormattingThreshold[] getThresholds() { | ||||
Threshold[] t = iconFormatting.getThresholds(); | Threshold[] t = iconFormatting.getThresholds(); | ||||
HSSFConditionalFormattingThreshold[] ht = new HSSFConditionalFormattingThreshold[t.length]; | HSSFConditionalFormattingThreshold[] ht = new HSSFConditionalFormattingThreshold[t.length]; | ||||
return ht; | return ht; | ||||
} | } | ||||
@Override | |||||
public void setThresholds(ConditionalFormattingThreshold[] thresholds) { | public void setThresholds(ConditionalFormattingThreshold[] thresholds) { | ||||
Threshold[] t = new Threshold[thresholds.length]; | Threshold[] t = new Threshold[thresholds.length]; | ||||
for (int i=0; i<t.length; i++) { | for (int i=0; i<t.length; i++) { | ||||
iconFormatting.setThresholds(t); | iconFormatting.setThresholds(t); | ||||
} | } | ||||
@Override | |||||
public HSSFConditionalFormattingThreshold createThreshold() { | public HSSFConditionalFormattingThreshold createThreshold() { | ||||
return new HSSFConditionalFormattingThreshold(new IconMultiStateThreshold(), sheet); | return new HSSFConditionalFormattingThreshold(new IconMultiStateThreshold(), sheet); | ||||
} | } |
* | * | ||||
* @return The created workbook | * @return The created workbook | ||||
*/ | */ | ||||
@Override | |||||
public HSSFWorkbook create() { | public HSSFWorkbook create() { | ||||
return new HSSFWorkbook(); | return new HSSFWorkbook(); | ||||
} | } | ||||
* Note that in order to properly release resources the | * Note that in order to properly release resources the | ||||
* Workbook should be closed after use. | * Workbook should be closed after use. | ||||
*/ | */ | ||||
@Override | |||||
public HSSFWorkbook create(final DirectoryNode root, String password) throws IOException { | public HSSFWorkbook create(final DirectoryNode root, String password) throws IOException { | ||||
boolean passwordSet = false; | boolean passwordSet = false; | ||||
if (password != null) { | if (password != null) { | ||||
} | } | ||||
@Override | @Override | ||||
@SuppressWarnings("java:S2095") | |||||
public Workbook create(File file, String password, boolean readOnly) throws IOException { | public Workbook create(File file, String password, boolean readOnly) throws IOException { | ||||
boolean passwordSet = false; | boolean passwordSet = false; | ||||
if (password != null) { | if (password != null) { | ||||
try { | try { | ||||
return new HSSFWorkbook(fs, true); | return new HSSFWorkbook(fs, true); | ||||
} catch (RuntimeException e) { | } catch (RuntimeException e) { | ||||
// we need to close the filesystem | |||||
// if we encounter an exception to | |||||
// not leak file handles | |||||
// we need to close the filesystem if we encounter an exception to not leak file handles | |||||
fs.close(); | fs.close(); | ||||
throw e; | throw e; | ||||
} | } | ||||
} finally { | } finally { |
import javax.crypto.SecretKey; | import javax.crypto.SecretKey; | ||||
import javax.crypto.spec.SecretKeySpec; | import javax.crypto.spec.SecretKeySpec; | ||||
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; | |||||
import org.apache.commons.io.input.BoundedInputStream; | |||||
import org.apache.poi.EncryptedDocumentException; | import org.apache.poi.EncryptedDocumentException; | ||||
import org.apache.poi.poifs.crypt.ChunkedCipherInputStream; | import org.apache.poi.poifs.crypt.ChunkedCipherInputStream; | ||||
import org.apache.poi.poifs.crypt.CryptoFunctions; | import org.apache.poi.poifs.crypt.CryptoFunctions; | ||||
import org.apache.poi.poifs.crypt.HashAlgorithm; | import org.apache.poi.poifs.crypt.HashAlgorithm; | ||||
import org.apache.poi.poifs.filesystem.DirectoryNode; | import org.apache.poi.poifs.filesystem.DirectoryNode; | ||||
import org.apache.poi.poifs.filesystem.DocumentInputStream; | import org.apache.poi.poifs.filesystem.DocumentInputStream; | ||||
import org.apache.poi.poifs.filesystem.DocumentNode; | |||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | import org.apache.poi.poifs.filesystem.POIFSFileSystem; | ||||
import org.apache.poi.util.BitField; | import org.apache.poi.util.BitField; | ||||
import org.apache.poi.util.BitFieldFactory; | import org.apache.poi.util.BitFieldFactory; | ||||
import org.apache.commons.io.input.BoundedInputStream; | |||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.LittleEndian; | import org.apache.poi.util.LittleEndian; | ||||
import org.apache.poi.util.LittleEndianInputStream; | import org.apache.poi.util.LittleEndianInputStream; | ||||
*/ | */ | ||||
public POIFSFileSystem getSummaryEntries(DirectoryNode root, String encryptedStream) | public POIFSFileSystem getSummaryEntries(DirectoryNode root, String encryptedStream) | ||||
throws IOException, GeneralSecurityException { | throws IOException, GeneralSecurityException { | ||||
DocumentNode es = (DocumentNode) root.getEntry(encryptedStream); | |||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
try (DocumentInputStream dis = root.createDocumentInputStream(es)) { | |||||
IOUtils.copy(dis, bos); | |||||
} | |||||
POIFSFileSystem fsOut = null; | POIFSFileSystem fsOut = null; | ||||
try ( | try ( | ||||
CryptoAPIDocumentInputStream sbis = new CryptoAPIDocumentInputStream(this, bos.toByteArray()); | |||||
DocumentInputStream dis = root.createDocumentInputStream(root.getEntry(encryptedStream)); | |||||
CryptoAPIDocumentInputStream sbis = new CryptoAPIDocumentInputStream(this, IOUtils.toByteArray(dis)); | |||||
LittleEndianInputStream leis = new LittleEndianInputStream(sbis) | LittleEndianInputStream leis = new LittleEndianInputStream(sbis) | ||||
) { | ) { | ||||
int streamDescriptorArrayOffset = (int) leis.readUInt(); | int streamDescriptorArrayOffset = (int) leis.readUInt(); |
* | * | ||||
* @since 3.15-beta2 | * @since 3.15-beta2 | ||||
*/ | */ | ||||
@SuppressWarnings("unused") | |||||
public class VBAMacroReader implements Closeable { | public class VBAMacroReader implements Closeable { | ||||
private static final Logger LOGGER = LogManager.getLogger(VBAMacroReader.class); | private static final Logger LOGGER = LogManager.getLogger(VBAMacroReader.class); | ||||
private static String readUnicode(InputStream is) throws IOException { | private static String readUnicode(InputStream is) throws IOException { | ||||
//reads null-terminated unicode string | //reads null-terminated unicode string | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
int b0 = IOUtils.readByte(is); | |||||
int b1 = IOUtils.readByte(is); | |||||
int read = 2; | |||||
while ((b0 + b1) != 0 && read < MAX_STRING_LENGTH) { | |||||
bos.write(b0); | |||||
bos.write(b1); | |||||
b0 = IOUtils.readByte(is); | |||||
b1 = IOUtils.readByte(is); | |||||
read += 2; | |||||
} | |||||
if (read >= MAX_STRING_LENGTH) { | |||||
LOGGER.atWarn().log("stopped reading unicode name after {} bytes", box(read)); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
int b0 = IOUtils.readByte(is); | |||||
int b1 = IOUtils.readByte(is); | |||||
int read = 2; | |||||
while ((b0 + b1) != 0 && read < MAX_STRING_LENGTH) { | |||||
bos.write(b0); | |||||
bos.write(b1); | |||||
b0 = IOUtils.readByte(is); | |||||
b1 = IOUtils.readByte(is); | |||||
read += 2; | |||||
} | |||||
if (read >= MAX_STRING_LENGTH) { | |||||
LOGGER.atWarn().log("stopped reading unicode name after {} bytes", box(read)); | |||||
} | |||||
return bos.toString(StandardCharsets.UTF_16LE); | |||||
} | } | ||||
return bos.toString(StandardCharsets.UTF_16LE); | |||||
} | } | ||||
private static String readMBCS(int firstByte, InputStream is, Charset charset) throws IOException { | private static String readMBCS(int firstByte, InputStream is, Charset charset) throws IOException { | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
int len = 0; | |||||
int b = firstByte; | |||||
while (b > 0 && len < MAX_STRING_LENGTH) { | |||||
++len; | |||||
bos.write(b); | |||||
b = IOUtils.readByte(is); | |||||
} | |||||
return bos.toString(charset); | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) { | |||||
int len = 0; | |||||
int b = firstByte; | |||||
while (b > 0 && len < MAX_STRING_LENGTH) { | |||||
++len; | |||||
bos.write(b); | |||||
b = IOUtils.readByte(is); | |||||
} | |||||
return bos.toString(charset); | |||||
} | |||||
} | } | ||||
/** | /** | ||||
*/ | */ | ||||
private static byte[] findCompressedStreamWBruteForce(InputStream is) throws IOException { | private static byte[] findCompressedStreamWBruteForce(InputStream is) throws IOException { | ||||
//buffer to memory for multiple tries | //buffer to memory for multiple tries | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
IOUtils.copy(is, bos); | |||||
byte[] compressed = bos.toByteArray(); | |||||
byte[] compressed = IOUtils.toByteArray(is); | |||||
byte[] decompressed = null; | byte[] decompressed = null; | ||||
for (int i = 0; i < compressed.length; i++) { | for (int i = 0; i < compressed.length; i++) { | ||||
if (compressed[i] == 0x01 && i < compressed.length-1) { | if (compressed[i] == 0x01 && i < compressed.length-1) { | ||||
} | } | ||||
private static byte[] tryToDecompress(InputStream is) { | private static byte[] tryToDecompress(InputStream is) { | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(); | |||||
try { | |||||
IOUtils.copy(new RLEDecompressingInputStream(is), bos); | |||||
try (RLEDecompressingInputStream ris = new RLEDecompressingInputStream(is)) { | |||||
return IOUtils.toByteArray(ris); | |||||
} catch (IllegalArgumentException | IOException | IllegalStateException e){ | } catch (IllegalArgumentException | IOException | IllegalStateException e){ | ||||
return null; | return null; | ||||
} | } | ||||
return bos.toByteArray(); | |||||
} | } | ||||
} | } |
protected EmbeddedData extract(DirectoryNode dn) throws IOException { | protected EmbeddedData extract(DirectoryNode dn) throws IOException { | ||||
assert(canExtract(dn)); | assert(canExtract(dn)); | ||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(20000); | |||||
try (POIFSFileSystem dest = new POIFSFileSystem()) { | |||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(20000); | |||||
POIFSFileSystem dest = new POIFSFileSystem()) { | |||||
copyNodes(dn, dest.getRoot()); | copyNodes(dn, dest.getRoot()); | ||||
// start with a reasonable big size | // start with a reasonable big size | ||||
dest.writeFilesystem(bos); | dest.writeFilesystem(bos); | ||||
return new EmbeddedData(dn.getName(), bos.toByteArray(), CONTENT_TYPE_BYTES); | |||||
} | } | ||||
return new EmbeddedData(dn.getName(), bos.toByteArray(), CONTENT_TYPE_BYTES); | |||||
} | } | ||||
protected EmbeddedData extract(Picture source) throws IOException { | protected EmbeddedData extract(Picture source) throws IOException { |
* icon and which Yellow or Red.</p> | * icon and which Yellow or Red.</p> | ||||
*/ | */ | ||||
public interface ConditionalFormattingThreshold { | public interface ConditionalFormattingThreshold { | ||||
public enum RangeType { | |||||
enum RangeType { | |||||
/** Number / Parameter */ | /** Number / Parameter */ | ||||
NUMBER(1, "num"), | NUMBER(1, "num"), | ||||
/** The minimum value from the range */ | /** The minimum value from the range */ | ||||
UNALLOCATED(6, null), | UNALLOCATED(6, null), | ||||
/** Formula result */ | /** Formula result */ | ||||
FORMULA(7, "formula"); | FORMULA(7, "formula"); | ||||
/** Numeric ID of the type */ | /** Numeric ID of the type */ | ||||
public final int id; | public final int id; | ||||
/** Name (system) of the type */ | /** Name (system) of the type */ | ||||
public final String name; | public final String name; | ||||
public String toString() { | public String toString() { | ||||
return id + " - " + name; | return id + " - " + name; | ||||
} | } | ||||
public static RangeType byId(int id) { | public static RangeType byId(int id) { | ||||
return values()[id-1]; // 1-based IDs | return values()[id-1]; // 1-based IDs | ||||
} | } | ||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
private RangeType(int id, String name) { | |||||
RangeType(int id, String name) { | |||||
this.id = id; this.name = name; | this.id = id; this.name = name; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Get the Range Type used | * Get the Range Type used | ||||
*/ | */ | ||||
RangeType getRangeType(); | RangeType getRangeType(); | ||||
/** | /** | ||||
* Changes the Range Type used | * Changes the Range Type used | ||||
* | |||||
* | |||||
* <p>If you change the range type, you need to | * <p>If you change the range type, you need to | ||||
* ensure that the Formula and Value parameters | * ensure that the Formula and Value parameters | ||||
* are compatible with it before saving</p> | * are compatible with it before saving</p> | ||||
*/ | */ | ||||
void setRangeType(RangeType type); | void setRangeType(RangeType type); | ||||
/** | /** | ||||
* Formula to use to calculate the threshold, | * Formula to use to calculate the threshold, | ||||
* or <code>null</code> if no formula | |||||
* or {@code null} if no formula | |||||
*/ | */ | ||||
String getFormula(); | String getFormula(); | ||||
/** | /** | ||||
* Sets the formula used to calculate the threshold, | * Sets the formula used to calculate the threshold, | ||||
* or unsets it if <code>null</code> is given. | |||||
* or unsets it if {@code null} is given. | |||||
*/ | */ | ||||
void setFormula(String formula); | void setFormula(String formula); | ||||
/** | /** | ||||
* Gets the value used for the threshold, or | |||||
* <code>null</code> if there isn't one. | |||||
* Gets the value used for the threshold, or | |||||
* {@code null} if there isn't one. | |||||
*/ | */ | ||||
Double getValue(); | Double getValue(); | ||||
/** | /** | ||||
* Sets the value used for the threshold. | |||||
* <p>If the type is {@link RangeType#PERCENT} or | |||||
* Sets the value used for the threshold. | |||||
* <p>If the type is {@link RangeType#PERCENT} or | |||||
* {@link RangeType#PERCENTILE} it must be between 0 and 100. | * {@link RangeType#PERCENTILE} it must be between 0 and 100. | ||||
* <p>If the type is {@link RangeType#MIN} or {@link RangeType#MAX} | * <p>If the type is {@link RangeType#MIN} or {@link RangeType#MAX} | ||||
* or {@link RangeType#FORMULA} it shouldn't be set. | * or {@link RangeType#FORMULA} it shouldn't be set. | ||||
* <p>Use <code>null</code> to unset | |||||
* <p>Use {@code null} to unset | |||||
*/ | */ | ||||
void setValue(Double value); | void setValue(Double value); | ||||
} | } |
return true; | return true; | ||||
} | } | ||||
@SuppressWarnings("java:S3516") | |||||
protected boolean printNumber(String name, Object o) { | protected boolean printNumber(String name, Object o) { | ||||
Number n = (Number)o; | Number n = (Number)o; | ||||
printName(name); | printName(name); |
} | } | ||||
final int len = Math.min(length, maxLength); | final int len = Math.min(length, maxLength); | ||||
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(len == Integer.MAX_VALUE ? 4096 : len); | |||||
try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(len == Integer.MAX_VALUE ? 4096 : len)) { | |||||
byte[] buffer = new byte[4096]; | |||||
int totalBytes = 0, readBytes; | |||||
do { | |||||
readBytes = stream.read(buffer, 0, Math.min(buffer.length, len - totalBytes)); | |||||
totalBytes += Math.max(readBytes, 0); | |||||
if (readBytes > 0) { | |||||
baos.write(buffer, 0, readBytes); | |||||
} | |||||
byte[] buffer = new byte[4096]; | |||||
int totalBytes = 0, readBytes; | |||||
do { | |||||
readBytes = stream.read(buffer, 0, Math.min(buffer.length, len-totalBytes)); | |||||
totalBytes += Math.max(readBytes,0); | |||||
if (readBytes > 0) { | |||||
baos.write(buffer, 0, readBytes); | |||||
} | |||||
checkByteSizeLimit(totalBytes); | |||||
} while (totalBytes < len && readBytes > -1); | |||||
checkByteSizeLimit(totalBytes); | |||||
} while (totalBytes < len && readBytes > -1); | |||||
if (maxLength != Integer.MAX_VALUE && totalBytes == maxLength) { | |||||
throw new IOException("MaxLength (" + maxLength + ") reached - stream seems to be invalid."); | |||||
} | |||||
if (maxLength != Integer.MAX_VALUE && totalBytes == maxLength) { | |||||
throw new IOException("MaxLength ("+maxLength+") reached - stream seems to be invalid."); | |||||
} | |||||
if (len != Integer.MAX_VALUE && totalBytes < len) { | |||||
throw new EOFException("unexpected EOF - expected len: " + len + " - actual len: " + totalBytes); | |||||
} | |||||
if (len != Integer.MAX_VALUE && totalBytes < len) { | |||||
throw new EOFException("unexpected EOF - expected len: "+len+" - actual len: "+totalBytes); | |||||
return baos.toByteArray(); | |||||
} | } | ||||
return baos.toByteArray(); | |||||
} | } | ||||
private static void checkLength(long length, int maxLength) { | private static void checkLength(long length, int maxLength) { |
import static org.hamcrest.MatcherAssert.assertThat; | import static org.hamcrest.MatcherAssert.assertThat; | ||||
import static org.junit.jupiter.api.Assertions.assertNotNull; | import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||
import static org.junit.jupiter.api.Assertions.assertTrue; | import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
import static org.junit.jupiter.api.Assertions.fail; | |||||
import static org.junit.jupiter.api.Assumptions.assumeTrue; | |||||
import java.io.File; | import java.io.File; | ||||
import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||
* Util class for POI JUnit TestCases, which provide additional features | * Util class for POI JUnit TestCases, which provide additional features | ||||
*/ | */ | ||||
@Internal | @Internal | ||||
@SuppressWarnings("java:S2187") | |||||
public final class POITestCase { | public final class POITestCase { | ||||
private POITestCase() { | private POITestCase() { | ||||
assertTrue(new ReflectionEquals(expected, "$jacocoData").matches(actual)); | assertTrue(new ReflectionEquals(expected, "$jacocoData").matches(actual)); | ||||
} | } | ||||
/** | |||||
* Rather than adding {@literal @}Ignore to known-failing tests, | |||||
* write the test so that it notifies us if it starts passing. | |||||
* This is useful for closing related or forgotten bugs. | |||||
* | |||||
* An Example: | |||||
* <code><pre> | |||||
* public static int add(int a, int b) { | |||||
* // a known bug in behavior that has not been fixed yet | |||||
* raise UnsupportedOperationException("add"); | |||||
* } | |||||
* | |||||
* {@literal @}Test | |||||
* void knownFailingUnitTest() { | |||||
* try { | |||||
* assertEquals(2, add(1,1)); | |||||
* // this test fails because the assumption that this bug had not been fixed is false | |||||
* testPassesNow(12345); | |||||
* } catch (UnsupportedOperationException e) { | |||||
* // test is skipped because the assumption that this bug had not been fixed is true | |||||
* skipTest(e); | |||||
* } | |||||
* } | |||||
* | |||||
* Once passing, this unit test can be rewritten as: | |||||
* {@literal @}Test | |||||
* void knownPassingUnitTest() { | |||||
* assertEquals(2, add(1,1)); | |||||
* } | |||||
* | |||||
* If you have a better idea how to simplify test code while still notifying | |||||
* us when a previous known-failing test now passes, please improve these. | |||||
* As a bonus, a known-failing test that fails should not be counted as a | |||||
* passing test. | |||||
* | |||||
* One possible alternative is to expect the known exception, but without | |||||
* a clear message that it is a good thing to no longer get the expected | |||||
* exception once the test passes. | |||||
* {@literal @}Test(expected=UnsupportedOperationException.class) | |||||
* void knownFailingUnitTest() { | |||||
* assertEquals(2, add(1,1)); | |||||
* } | |||||
* | |||||
* @param e the exception that was caught that will no longer | |||||
* be raised when the bug is fixed | |||||
*/ | |||||
public static void skipTest(Throwable e) { | |||||
assumeTrue(e != null, "This test currently fails with"); | |||||
} | |||||
/** | |||||
* @see #skipTest(Throwable) | |||||
* | |||||
* @param bug the bug number corresponding to a known bug in bugzilla | |||||
*/ | |||||
public static void testPassesNow(int bug) { | |||||
fail("This test passes now. Please update the unit test and bug " + bug + "."); | |||||
} | |||||
public static void assertBetween(String message, int value, int min, int max) { | |||||
assertTrue(min <= value, message + ": " + value + " is less than the minimum value of " + min); | |||||
assertTrue(value <= max, message + ": " + value + " is greater than the maximum value of " + max); | |||||
} | |||||
/** | /** | ||||
* Ensures that the temporary directory is defined and exists and | * Ensures that the temporary directory is defined and exists and |
} | } | ||||
@Test | @Test | ||||
@SuppressWarnings("java:S2699") | |||||
void test58896() throws IOException { | void test58896() throws IOException { | ||||
final int nrows = 160; | final int nrows = 160; | ||||
final int ncols = 139; | final int ncols = 139; | ||||
// ******************************* | // ******************************* | ||||
// First cell of array formula, OK | // First cell of array formula, OK | ||||
int rowId = 0; | |||||
int cellId = 1; | |||||
final int rowId = 0; | |||||
final int cellId = 1; | |||||
Row row = sheet.getRow(rowId); | Row row = sheet.getRow(rowId); | ||||
Cell cell = row.getCell(cellId); | Cell cell = row.getCell(cellId); | ||||
// ******************************* | // ******************************* | ||||
// Second cell of array formula, NOT OK for xlsx files | // Second cell of array formula, NOT OK for xlsx files | ||||
rowId = 1; | |||||
cellId = 1; | |||||
row = sheet.getRow(rowId); | row = sheet.getRow(rowId); | ||||
cell = row.getCell(cellId); | cell = row.getCell(cellId); | ||||
assertEquals("A1", cell.getCellFormula()); | assertEquals("A1", cell.getCellFormula()); |
package org.apache.poi.ss.usermodel; | package org.apache.poi.ss.usermodel; | ||||
import static org.apache.poi.POITestCase.assertBetween; | |||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||||
import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
import static org.junit.jupiter.api.Assertions.assertFalse; | import static org.junit.jupiter.api.Assertions.assertFalse; | ||||
/** | /** | ||||
* Common superclass for testing {@link org.apache.poi.hssf.usermodel.HSSFCell}, | * Common superclass for testing {@link org.apache.poi.hssf.usermodel.HSSFCell}, | ||||
* {@link org.apache.poi.xssf.usermodel.XSSFCell} and | |||||
* {@link org.apache.poi.xssf.streaming.SXSSFCell} | |||||
* XSSFCell and SXSSFCell | |||||
*/ | */ | ||||
public abstract class BaseTestSheet { | public abstract class BaseTestSheet { | ||||
private static final int ROW_COUNT = 40000; | private static final int ROW_COUNT = 40000; | ||||
assertCollectionEquals(mergedRegions.values(), sheet.getMergedRegions()); | assertCollectionEquals(mergedRegions.values(), sheet.getMergedRegions()); | ||||
Collection<Integer> removed = Arrays.asList(0, 2, 3, 6, 8); | Collection<Integer> removed = Arrays.asList(0, 2, 3, 6, 8); | ||||
mergedRegions.keySet().removeAll(removed); | |||||
removed.forEach(mergedRegions.keySet()::remove); | |||||
sheet.removeMergedRegions(removed); | sheet.removeMergedRegions(removed); | ||||
assertCollectionEquals(mergedRegions.values(), sheet.getMergedRegions()); | assertCollectionEquals(mergedRegions.values(), sheet.getMergedRegions()); | ||||
} | } | ||||
assertBetween("Date column width", s.getColumnWidth(1), 4750, 7300); | assertBetween("Date column width", s.getColumnWidth(1), 4750, 7300); | ||||
} | } | ||||
} | } | ||||
private static void assertBetween(String message, int value, int min, int max) { | |||||
assertTrue(min <= value, message + ": " + value + " is less than the minimum value of " + min); | |||||
assertTrue(value <= max, message + ": " + value + " is greater than the maximum value of " + max); | |||||
} | |||||
} | } |
} | } | ||||
@Test | @Test | ||||
void changeSheetNameWithSharedFormulas() throws IOException { | |||||
protected void changeSheetNameWithSharedFormulas() throws IOException { | |||||
String sampleFile = "shared_formulas.xls" + (getClass().getName().contains("xssf") ? "x" : ""); | String sampleFile = "shared_formulas.xls" + (getClass().getName().contains("xssf") ? "x" : ""); | ||||
try (Workbook wb = _testDataProvider.openSampleWorkbook(sampleFile)) { | try (Workbook wb = _testDataProvider.openSampleWorkbook(sampleFile)) { |