merge down trunk

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hssf_cryptoapi@1755463 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-08-08 01:14:36 +00:00
parent 0bfefdfc04
commit 680683cf77
51 changed files with 1967 additions and 1249 deletions

View File

@ -17,7 +17,6 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!--
This build was tested with ant 1.9.4 although it will probably work with
other versions, however at least 1.8.0 is required.
@ -133,6 +132,7 @@ under the License.
<property name="ooxml.output.test.dir" location="build/ooxml-test-classes"/>
<property name="ooxml.testokfile" location="build/ooxml-testokfile.txt"/>
<property name="ooxml.lite.output.dir" location="build/ooxml-lite-classes"/>
<property name="ooxml.lite.testokfile" location="build/ooxml-lite-testokfile.txt"/>
<!-- XSSF/SXSSF subset of OOXML: -->
<property name="ooxml.ss.testokfile" location="build/ooxml-ss-testokfile.txt"/>
@ -169,6 +169,9 @@ under the License.
<property name="main.ant.url" value="${repository.m2}/maven2/org/apache/ant/ant/1.9.4/ant-1.9.4.jar"/>
<property name="main.antlauncher.jar" location="${main.lib}/ant-launcher-1.9.4.jar"/>
<property name="main.antlauncher.url" value="${repository.m2}/maven2/org/apache/ant/ant-launcher/1.9.4/ant-launcher-1.9.4.jar"/>
<property name="main.commons-collections4.jar" location="${main.lib}/commons-collections4-4.1.jar"/>
<property name="main.commons-collections4.url"
value="${repository.m2}/maven2/org/apache/commons/commons-collections4/4.1/commons-collections4-4.1.jar"/>
<!-- xml signature libs -->
<property name="dsig.xmlsec.jar" location="${compile.lib}/xmlsec-2.0.6.jar"/>
@ -192,8 +195,8 @@ under the License.
value="${repository.m2}/maven2/org/apache/xmlbeans/xmlbeans/2.6.0/xmlbeans-2.6.0.jar"/>
<!-- coverage libs -->
<property name="jacoco.zip" location="${main.lib}/jacoco-0.7.6.201602180812.zip"/>
<property name="jacoco.url" value="${repository.m2}/maven2/org/jacoco/jacoco/0.7.6.201602180812/jacoco-0.7.6.201602180812.zip"/>
<property name="jacoco.zip" location="${main.lib}/jacoco-0.7.7.201606060606.zip"/>
<property name="jacoco.url" value="${repository.m2}/maven2/org/jacoco/jacoco/0.7.7.201606060606/jacoco-0.7.7.201606060606.zip"/>
<property name="asm.jar" location="${main.lib}/asm-all-5.0.3.jar"/>
<property name="asm.url" value="${repository.m2}/maven2/org/ow2/asm/asm-all/5.0.3/asm-all-5.0.3.jar"/>
@ -285,6 +288,7 @@ under the License.
</condition>
<property name="findbugs.version" value="2.0.3" if:set="findbugs.jdk6"/>
<property name="findbugs.version" value="3.0.1" unless:set="findbugs.jdk6"/>
<echo message="Findbugs-Version: ${findbugs.version} for Java ${ant.java.version}"/>
<property name="findbugs.url" value="http://prdownloads.sourceforge.net/findbugs/findbugs-noUpdateChecks-${findbugs.version}.zip?download"/>
<property name="findbugs.jar" location="${main.lib}/findbugs-noUpdateChecks-${findbugs.version}.zip"/>
@ -303,6 +307,8 @@ under the License.
<!-- this can be overwriten to empty when running with Java 9 -->
<property name="maxpermsize" value="-XX:MaxPermSize=256m"/>
<property name="java9addmods" value="-Dthis.is.a.dummy=true"/>
<property name="java9addmodsvalue" value="-Dthis.is.a.dummy=true"/>
<path id="main.classpath">
<pathelement location="${main.commons-logging.jar}"/>
@ -310,6 +316,7 @@ under the License.
<pathelement location="${main.log4j.jar}"/>
<pathelement location="${main.junit.jar}"/>
<pathelement location="${main.hamcrest.jar}"/>
<pathelement location="${main.commons-collections4.jar}"/>
</path>
<path id="scratchpad.classpath">
@ -498,9 +505,7 @@ under the License.
<attribute name="src"/>
<attribute name="dest"/>
<sequential>
<local name="exists"/>
<available file="@{dest}" property="exists"/>
<!--fail unless:true="${exists}"
<!--fail
message="Java version might be uncapable to download https URLs - see https://stackoverflow.com/questions/6851461/java-why-does-ssl-handshake-give-could-not-generate-dh-keypair-exception">
<condition>
<and>
@ -509,7 +514,7 @@ under the License.
</and>
</condition>
</fail-->
<get src="@{src}" dest="@{dest}" unless:true="${exists}"/>
<get src="@{src}" dest="@{dest}" skipexisting="true"/>
</sequential>
</macrodef>
@ -531,12 +536,14 @@ under the License.
<include name="jacoco-0.7.2*"/>
<include name="jacoco-0.7.3*"/>
<include name="jacoco-0.7.4*"/>
<include name="jacoco-0.7.6*"/>
<include name="log4j-1.2.13*"/>
<include name="org.jacoco.*-0.6.*"/>
<include name="org.jacoco.*-0.7.1*"/>
<include name="org.jacoco.*-0.7.2*"/>
<include name="org.jacoco.*-0.7.3*"/>
<include name="org.jacoco.*-0.7.4*"/>
<include name="org.jacoco.*-0.7.6*"/>
<include name="dom4j*"/>
<include name="apache-rat-0.10*"/>
<include name="xercesImpl-*.jar"/>
@ -587,6 +594,7 @@ under the License.
<available file="${dsig.bouncycastle-pkix.jar}"/>
<available file="${dsig.xmlsec.jar}"/>
<available file="${dsig.sl4j-api.jar}"/>
<available file="${main.commons-collections4.jar}"/>
</and>
<isset property="disconnected"/>
</or>
@ -605,6 +613,7 @@ under the License.
<downloadfile src="${main.antlauncher.url}" dest="${main.antlauncher.jar}"/>
<downloadfile src="${asm.url}" dest="${asm.jar}"/>
<downloadfile src="${jacoco.url}" dest="${jacoco.zip}"/>
<downloadfile src="${main.commons-collections4.url}" dest="${main.commons-collections4.jar}"/>
<unzip src="${jacoco.zip}" dest=".">
<patternset>
<include name="lib/*.jar"/>
@ -1161,6 +1170,8 @@ under the License.
<group name="Main">
<classfiles>
<fileset dir="${main.output.dir}">
<!-- exclude some generated classes -->
<exclude name="org/apache/poi/sl/draw/binding/*.class"/>
<!-- exclude large test-class -->
<exclude name="org/apache/poi/hssf/usermodel/DummyGraphics2d.class"/>
</fileset>
@ -1242,6 +1253,8 @@ under the License.
<jvmarg value="-ea"/>
<jvmarg value="-Xmx256m"/>
<!-- jvmarg value="-Duser.timezone=UTC"/ -->
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${main.reports.test}">
@ -1258,6 +1271,16 @@ under the License.
<antcall target="-test-main-write-testfile"/>
</target>
<target name="test-report" depends="init">
<mkdir dir="build/report"/>
<junitreport todir="build/report">
<fileset dir="build">
<include name="*results/**/TEST-*.xml"/>
</fileset>
<report format="frames" todir="build/report"/>
</junitreport>
</target>
<target name="-test-property-check" unless="testcase">
<echo message="Please use -Dtestcase=org.your.testcase to run a single test"/>
<fail/>
@ -1288,6 +1311,8 @@ under the License.
<jvmarg value="-ea"/>
<jvmarg value="-Xmx256m"/>
<!-- jvmarg value="-Duser.timezone=UTC"/ -->
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${main.reports.test}">
@ -1335,6 +1360,8 @@ under the License.
and on Windows with jdk-1.5.22
-->
<jvmarg value="-Xmx256M"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${scratchpad.reports.test}">
@ -1373,6 +1400,8 @@ under the License.
<jvmarg value="${maxpermsize}"/>
<jvmarg value="-Xmx768M"/>
<jvmarg value="-ea"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<!-- jvmarg value="-Duser.timezone=UTC"/ -->
<formatter type="plain"/>
<formatter type="xml"/>
@ -1396,6 +1425,8 @@ under the License.
<syspropertyset refid="junit.properties"/>
<jvmarg value="-Xmx768M"/>
<jvmarg value="-ea"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${ooxml.reports.test}">
@ -1439,6 +1470,8 @@ under the License.
<jvmarg value="${maxpermsize}"/>
<jvmarg value="-Xmx768M"/>
<jvmarg value="-ea"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<!-- jvmarg value="-Duser.timezone=UTC"/ -->
<formatter type="plain"/>
<formatter type="xml"/>
@ -1485,6 +1518,8 @@ under the License.
<syspropertyset refid="junit.properties"/>
<jvmarg value="-ea"/>
<jvmarg value="-Xmx1512M"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${integration.reports.test}">
@ -1503,7 +1538,20 @@ under the License.
</target>
<!-- Section: test-ooxml-lite -->
<target name="compile-ooxml-lite" depends="compile-ooxml">
<target name="-compile-ooxml-lite-check">
<uptodate property="ooxml.lite.test.notRequired" targetfile="${ooxml.lite.testokfile}">
<srcfiles dir="${ooxml.src}"/>
<srcfiles dir="${ooxml.src.test}"/>
<srcfiles file="${ooxml.xsds.jar}"/>
<srcfiles file="${ooxml.security.jar}"/>
</uptodate>
</target>
<target name="compile-ooxml-lite" depends="-compile-ooxml-lite-check,compile-ooxml"
unless="ooxml.lite.test.notRequired">
<delete file="${ooxml.lite.testokfile}"/>
<echo message="Running ooxml-lite generator"/>
<property name="ooxml.lite-merged.dir" location="build/ooxml-lite-merged"/>
<mkdir dir="${ooxml.lite-merged.dir}"/>
@ -1522,6 +1570,8 @@ under the License.
<syspropertyset refid="junit.properties"/>
<jvmarg value="${maxpermsize}"/>
<jvmarg value="-Xmx512m"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<arg value="-ooxml"/>
<arg value="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar"/>
<arg value="-test"/>
@ -1529,6 +1579,8 @@ under the License.
<arg value="-dest"/>
<arg value="${ooxml.lite.output.dir}"/>
</java>
<echo file="${ooxml.lite.testokfile}" append="false" message="testok"/>
</target>
<target name="test-ooxml-lite" depends="jacocotask,compile-ooxml-xsds,compile-ooxml-lite">
@ -1557,6 +1609,8 @@ under the License.
<classpath refid="test.excelant.classpath"/>
<syspropertyset refid="junit.properties"/>
<jvmarg value="-ea"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${excelant.reports.test}">
@ -1868,6 +1922,7 @@ under the License.
<fileset dir="${main.lib}">
<include name="commons-codec-*.jar"/>
<include name="commons-logging-*.jar"/>
<include name="commons-collections4-*.jar"/>
<include name="junit-*.jar"/>
<include name="log4j-*.jar"/>
</fileset>
@ -2083,7 +2138,7 @@ under the License.
</forbiddenapis>
</target>
<target name="findbugs" depends="assemble">
<target name="findbugs" depends="jar">
<downloadfile src="${findbugs.url}" dest="${findbugs.jar}"/>
<property name="findbugs.home" value="build/findbugs" />
@ -2103,11 +2158,13 @@ under the License.
output="xml:withMessages"
outputFile="build/findbugs.xml"
effort="max"
failOnError="true"
excludeFilter="src/resources/devtools/findbugs-filters.xml">
<fileset dir="${dist.dir}/maven">
<include name="poi/poi-${version.id}.jar"/>
<include name="poi-scratchpad/poi-scratchpad-${version.id}.jar"/>
<include name="poi-ooxml/poi-ooxml-${version.id}.jar"/>
<include name="poi-excelant/poi-excelant-${version.id}.jar"/>
</fileset>
<auxClasspath path="${dsig.bouncycastle-pkix.jar}" />
<auxClasspath path="${dsig.bouncycastle-prov.jar}" />
@ -2117,9 +2174,11 @@ under the License.
<auxClasspath path="${ooxml.security.jar}" />
<auxClasspath path="${ooxml.curvesapi.jar}" />
<auxClasspath path="${ooxml.xmlbeans26.jar}" />
<auxClasspath path="${main.commons-collections4.jar}" />
<auxClasspath path="${main.commons-codec.jar}" />
<auxClasspath path="${main.commons-logging.jar}" />
<auxClasspath path="${main.junit.jar}" />
<auxClasspath path="${main.ant.jar}" />
<sourcePath path="src/java" />
<sourcePath path="src/ooxml/java" />
<sourcePath path="src/scratchpad/src" />

View File

@ -91,6 +91,11 @@
<scope>test</scope>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
</dependencies>
</project>

View File

@ -110,6 +110,11 @@
</build>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>

View File

@ -34,184 +34,184 @@ import org.apache.poi.ss.formula.FormulaShifter;
* @author Jason Height (jheight at chariot dot net dot au)
*/
public final class RowRecordsAggregate extends RecordAggregate {
private int _firstrow = -1;
private int _lastrow = -1;
private final Map<Integer, RowRecord> _rowRecords;
private final ValueRecordsAggregate _valuesAgg;
private final List<Record> _unknownRecords;
private final SharedValueManager _sharedValueManager;
private int _firstrow = -1;
private int _lastrow = -1;
private final Map<Integer, RowRecord> _rowRecords;
private final ValueRecordsAggregate _valuesAgg;
private final List<Record> _unknownRecords;
private final SharedValueManager _sharedValueManager;
// Cache values to speed up performance of
// Cache values to speed up performance of
// getStartRowNumberForBlock / getEndRowNumberForBlock, see Bugzilla 47405
private RowRecord[] _rowRecordValues = null;
/** Creates a new instance of ValueRecordsAggregate */
public RowRecordsAggregate() {
this(SharedValueManager.createEmpty());
}
private RowRecordsAggregate(SharedValueManager svm) {
if (svm == null) {
throw new IllegalArgumentException("SharedValueManager must be provided.");
}
_rowRecords = new TreeMap<Integer, RowRecord>();
_valuesAgg = new ValueRecordsAggregate();
_unknownRecords = new ArrayList<Record>();
_sharedValueManager = svm;
}
/** Creates a new instance of ValueRecordsAggregate */
public RowRecordsAggregate() {
this(SharedValueManager.createEmpty());
}
private RowRecordsAggregate(SharedValueManager svm) {
if (svm == null) {
throw new IllegalArgumentException("SharedValueManager must be provided.");
}
_rowRecords = new TreeMap<Integer, RowRecord>();
_valuesAgg = new ValueRecordsAggregate();
_unknownRecords = new ArrayList<Record>();
_sharedValueManager = svm;
}
/**
* @param rs record stream with all {@link SharedFormulaRecord}
* {@link ArrayRecord}, {@link TableRecord} {@link MergeCellsRecord} Records removed
* @param svm an initialised {@link SharedValueManager} (from the shared formula, array
* and table records of the current sheet). Never <code>null</code>.
*/
public RowRecordsAggregate(RecordStream rs, SharedValueManager svm) {
this(svm);
while(rs.hasNext()) {
Record rec = rs.getNext();
switch (rec.getSid()) {
case RowRecord.sid:
insertRow((RowRecord) rec);
continue;
/**
* @param rs record stream with all {@link SharedFormulaRecord}
* {@link ArrayRecord}, {@link TableRecord} {@link MergeCellsRecord} Records removed
* @param svm an initialised {@link SharedValueManager} (from the shared formula, array
* and table records of the current sheet). Never <code>null</code>.
*/
public RowRecordsAggregate(RecordStream rs, SharedValueManager svm) {
this(svm);
while(rs.hasNext()) {
Record rec = rs.getNext();
switch (rec.getSid()) {
case RowRecord.sid:
insertRow((RowRecord) rec);
continue;
case DConRefRecord.sid:
addUnknownRecord(rec);
continue;
case DBCellRecord.sid:
// end of 'Row Block'. Should only occur after cell records
// ignore DBCELL records because POI generates them upon re-serialization
continue;
}
if (rec instanceof UnknownRecord) {
// might need to keep track of where exactly these belong
addUnknownRecord(rec);
while (rs.peekNextSid() == ContinueRecord.sid) {
addUnknownRecord(rs.getNext());
}
continue;
}
if (rec instanceof MulBlankRecord) {
_valuesAgg.addMultipleBlanks((MulBlankRecord) rec);
continue;
}
if (!(rec instanceof CellValueRecordInterface)) {
throw new RuntimeException("Unexpected record type (" + rec.getClass().getName() + ")");
}
_valuesAgg.construct((CellValueRecordInterface)rec, rs, svm);
}
}
/**
* Handles UnknownRecords which appear within the row/cell records
*/
private void addUnknownRecord(Record rec) {
// ony a few distinct record IDs are encountered by the existing POI test cases:
// 0x1065 // many
// 0x01C2 // several
// 0x0034 // few
// No documentation could be found for these
// end of 'Row Block'. Should only occur after cell records
// ignore DBCELL records because POI generates them upon re-serialization
continue;
}
if (rec instanceof UnknownRecord) {
// might need to keep track of where exactly these belong
addUnknownRecord(rec);
while (rs.peekNextSid() == ContinueRecord.sid) {
addUnknownRecord(rs.getNext());
}
continue;
}
if (rec instanceof MulBlankRecord) {
_valuesAgg.addMultipleBlanks((MulBlankRecord) rec);
continue;
}
if (!(rec instanceof CellValueRecordInterface)) {
throw new RuntimeException("Unexpected record type (" + rec.getClass().getName() + ")");
}
_valuesAgg.construct((CellValueRecordInterface)rec, rs, svm);
}
}
/**
* Handles UnknownRecords which appear within the row/cell records
*/
private void addUnknownRecord(Record rec) {
// ony a few distinct record IDs are encountered by the existing POI test cases:
// 0x1065 // many
// 0x01C2 // several
// 0x0034 // few
// No documentation could be found for these
// keep the unknown records for re-serialization
_unknownRecords.add(rec);
}
public void insertRow(RowRecord row) {
// Integer integer = Integer.valueOf(row.getRowNumber());
_rowRecords.put(Integer.valueOf(row.getRowNumber()), row);
// Clear the cached values
_rowRecordValues = null;
if ((row.getRowNumber() < _firstrow) || (_firstrow == -1)) {
_firstrow = row.getRowNumber();
}
if ((row.getRowNumber() > _lastrow) || (_lastrow == -1)) {
_lastrow = row.getRowNumber();
}
}
// keep the unknown records for re-serialization
_unknownRecords.add(rec);
}
public void insertRow(RowRecord row) {
// Integer integer = Integer.valueOf(row.getRowNumber());
_rowRecords.put(Integer.valueOf(row.getRowNumber()), row);
// Clear the cached values
_rowRecordValues = null;
if ((row.getRowNumber() < _firstrow) || (_firstrow == -1)) {
_firstrow = row.getRowNumber();
}
if ((row.getRowNumber() > _lastrow) || (_lastrow == -1)) {
_lastrow = row.getRowNumber();
}
}
public void removeRow(RowRecord row) {
int rowIndex = row.getRowNumber();
_valuesAgg.removeAllCellsValuesForRow(rowIndex);
Integer key = Integer.valueOf(rowIndex);
RowRecord rr = _rowRecords.remove(key);
if (rr == null) {
throw new RuntimeException("Invalid row index (" + key.intValue() + ")");
}
if (row != rr) {
_rowRecords.put(key, rr);
throw new RuntimeException("Attempt to remove row that does not belong to this sheet");
}
// Clear the cached values
_rowRecordValues = null;
}
public void removeRow(RowRecord row) {
int rowIndex = row.getRowNumber();
_valuesAgg.removeAllCellsValuesForRow(rowIndex);
Integer key = Integer.valueOf(rowIndex);
RowRecord rr = _rowRecords.remove(key);
if (rr == null) {
throw new RuntimeException("Invalid row index (" + key.intValue() + ")");
}
if (row != rr) {
_rowRecords.put(key, rr);
throw new RuntimeException("Attempt to remove row that does not belong to this sheet");
}
// Clear the cached values
_rowRecordValues = null;
}
public RowRecord getRow(int rowIndex) {
public RowRecord getRow(int rowIndex) {
int maxrow = SpreadsheetVersion.EXCEL97.getLastRowIndex();
if (rowIndex < 0 || rowIndex > maxrow) {
throw new IllegalArgumentException("The row number must be between 0 and " + maxrow + ", but had: " + rowIndex);
}
return _rowRecords.get(Integer.valueOf(rowIndex));
}
throw new IllegalArgumentException("The row number must be between 0 and " + maxrow + ", but had: " + rowIndex);
}
return _rowRecords.get(Integer.valueOf(rowIndex));
}
public int getPhysicalNumberOfRows()
{
return _rowRecords.size();
}
public int getPhysicalNumberOfRows()
{
return _rowRecords.size();
}
public int getFirstRowNum()
{
return _firstrow;
}
public int getFirstRowNum()
{
return _firstrow;
}
public int getLastRowNum()
{
return _lastrow;
}
public int getLastRowNum()
{
return _lastrow;
}
/** Returns the number of row blocks.
* <p/>The row blocks are goupings of rows that contain the DBCell record
* after them
*/
public int getRowBlockCount() {
int size = _rowRecords.size()/DBCellRecord.BLOCK_SIZE;
if ((_rowRecords.size() % DBCellRecord.BLOCK_SIZE) != 0)
size++;
return size;
}
/** Returns the number of row blocks.
* <p/>The row blocks are goupings of rows that contain the DBCell record
* after them
*/
public int getRowBlockCount() {
int size = _rowRecords.size()/DBCellRecord.BLOCK_SIZE;
if ((_rowRecords.size() % DBCellRecord.BLOCK_SIZE) != 0)
size++;
return size;
}
private int getRowBlockSize(int block) {
return RowRecord.ENCODED_SIZE * getRowCountForBlock(block);
}
private int getRowBlockSize(int block) {
return RowRecord.ENCODED_SIZE * getRowCountForBlock(block);
}
/** Returns the number of physical rows within a block*/
public int getRowCountForBlock(int block) {
int startIndex = block * DBCellRecord.BLOCK_SIZE;
int endIndex = startIndex + DBCellRecord.BLOCK_SIZE - 1;
if (endIndex >= _rowRecords.size())
endIndex = _rowRecords.size()-1;
/** Returns the number of physical rows within a block*/
public int getRowCountForBlock(int block) {
int startIndex = block * DBCellRecord.BLOCK_SIZE;
int endIndex = startIndex + DBCellRecord.BLOCK_SIZE - 1;
if (endIndex >= _rowRecords.size())
endIndex = _rowRecords.size()-1;
return endIndex-startIndex+1;
}
return endIndex-startIndex+1;
}
/** Returns the physical row number of the first row in a block*/
private int getStartRowNumberForBlock(int block) {
int startIndex = block * DBCellRecord.BLOCK_SIZE;
/** Returns the physical row number of the first row in a block*/
private int getStartRowNumberForBlock(int block) {
int startIndex = block * DBCellRecord.BLOCK_SIZE;
if(_rowRecordValues == null){
if (_rowRecordValues == null) {
_rowRecordValues = _rowRecords.values().toArray(new RowRecord[_rowRecords.size()]);
}
try {
return _rowRecordValues[startIndex].getRowNumber();
} catch(ArrayIndexOutOfBoundsException e) {
throw new RuntimeException("Did not find start row for block " + block);
}
}
throw new RuntimeException("Did not find start row for block " + block);
}
}
/** Returns the physical row number of the end row in a block*/
private int getEndRowNumberForBlock(int block) {
int endIndex = ((block + 1)*DBCellRecord.BLOCK_SIZE)-1;
if (endIndex >= _rowRecords.size())
endIndex = _rowRecords.size()-1;
/** Returns the physical row number of the end row in a block*/
private int getEndRowNumberForBlock(int block) {
int endIndex = ((block + 1)*DBCellRecord.BLOCK_SIZE)-1;
if (endIndex >= _rowRecords.size())
endIndex = _rowRecords.size()-1;
if(_rowRecordValues == null){
if (_rowRecordValues == null){
_rowRecordValues = _rowRecords.values().toArray(new RowRecord[_rowRecords.size()]);
}
@ -219,287 +219,287 @@ public final class RowRecordsAggregate extends RecordAggregate {
return _rowRecordValues[endIndex].getRowNumber();
} catch(ArrayIndexOutOfBoundsException e) {
throw new RuntimeException("Did not find end row for block " + block);
}
}
}
}
private int visitRowRecordsForBlock(int blockIndex, RecordVisitor rv) {
final int startIndex = blockIndex*DBCellRecord.BLOCK_SIZE;
final int endIndex = startIndex + DBCellRecord.BLOCK_SIZE;
private int visitRowRecordsForBlock(int blockIndex, RecordVisitor rv) {
final int startIndex = blockIndex*DBCellRecord.BLOCK_SIZE;
final int endIndex = startIndex + DBCellRecord.BLOCK_SIZE;
Iterator<RowRecord> rowIterator = _rowRecords.values().iterator();
Iterator<RowRecord> rowIterator = _rowRecords.values().iterator();
//Given that we basically iterate through the rows in order,
//For a performance improvement, it would be better to return an instance of
//an iterator and use that instance throughout, rather than recreating one and
//having to move it to the right position.
int i=0;
for (;i<startIndex;i++)
rowIterator.next();
int result = 0;
while(rowIterator.hasNext() && (i++ < endIndex)) {
Record rec = rowIterator.next();
result += rec.getRecordSize();
rv.visitRecord(rec);
}
return result;
}
//Given that we basically iterate through the rows in order,
//For a performance improvement, it would be better to return an instance of
//an iterator and use that instance throughout, rather than recreating one and
//having to move it to the right position.
int i=0;
for (;i<startIndex;i++)
rowIterator.next();
int result = 0;
while(rowIterator.hasNext() && (i++ < endIndex)) {
Record rec = rowIterator.next();
result += rec.getRecordSize();
rv.visitRecord(rec);
}
return result;
}
@Override
public void visitContainedRecords(RecordVisitor rv) {
PositionTrackingVisitor stv = new PositionTrackingVisitor(rv, 0);
//DBCells are serialized before row records.
final int blockCount = getRowBlockCount();
for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) {
// Serialize a block of rows.
// Hold onto the position of the first row in the block
int pos=0;
// Hold onto the size of this block that was serialized
final int rowBlockSize = visitRowRecordsForBlock(blockIndex, rv);
pos += rowBlockSize;
// Serialize a block of cells for those rows
final int startRowNumber = getStartRowNumberForBlock(blockIndex);
final int endRowNumber = getEndRowNumberForBlock(blockIndex);
DBCellRecord.Builder dbcrBuilder = new DBCellRecord.Builder();
// Note: Cell references start from the second row...
int cellRefOffset = (rowBlockSize - RowRecord.ENCODED_SIZE);
for (int row = startRowNumber; row <= endRowNumber; row++) {
if (_valuesAgg.rowHasCells(row)) {
stv.setPosition(0);
_valuesAgg.visitCellsForRow(row, stv);
int rowCellSize = stv.getPosition();
pos += rowCellSize;
// Add the offset to the first cell for the row into the
// DBCellRecord.
dbcrBuilder.addCellOffset(cellRefOffset);
cellRefOffset = rowCellSize;
}
}
// Calculate Offset from the start of a DBCellRecord to the first Row
rv.visitRecord(dbcrBuilder.build(pos));
}
for (Record _unknownRecord : _unknownRecords) {
// Potentially breaking the file here since we don't know exactly where to write these records
rv.visitRecord(_unknownRecord);
}
}
PositionTrackingVisitor stv = new PositionTrackingVisitor(rv, 0);
//DBCells are serialized before row records.
final int blockCount = getRowBlockCount();
for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) {
// Serialize a block of rows.
// Hold onto the position of the first row in the block
int pos=0;
// Hold onto the size of this block that was serialized
final int rowBlockSize = visitRowRecordsForBlock(blockIndex, rv);
pos += rowBlockSize;
// Serialize a block of cells for those rows
final int startRowNumber = getStartRowNumberForBlock(blockIndex);
final int endRowNumber = getEndRowNumberForBlock(blockIndex);
DBCellRecord.Builder dbcrBuilder = new DBCellRecord.Builder();
// Note: Cell references start from the second row...
int cellRefOffset = (rowBlockSize - RowRecord.ENCODED_SIZE);
for (int row = startRowNumber; row <= endRowNumber; row++) {
if (_valuesAgg.rowHasCells(row)) {
stv.setPosition(0);
_valuesAgg.visitCellsForRow(row, stv);
int rowCellSize = stv.getPosition();
pos += rowCellSize;
// Add the offset to the first cell for the row into the
// DBCellRecord.
dbcrBuilder.addCellOffset(cellRefOffset);
cellRefOffset = rowCellSize;
}
}
// Calculate Offset from the start of a DBCellRecord to the first Row
rv.visitRecord(dbcrBuilder.build(pos));
}
for (Record _unknownRecord : _unknownRecords) {
// Potentially breaking the file here since we don't know exactly where to write these records
rv.visitRecord(_unknownRecord);
}
}
public Iterator<RowRecord> getIterator() {
return _rowRecords.values().iterator();
}
public Iterator<RowRecord> getIterator() {
return _rowRecords.values().iterator();
}
public int findStartOfRowOutlineGroup(int row) {
// Find the start of the group.
RowRecord rowRecord = this.getRow( row );
int level = rowRecord.getOutlineLevel();
int currentRow = row;
while (currentRow >= 0 && this.getRow( currentRow ) != null) {
rowRecord = this.getRow( currentRow );
if (rowRecord.getOutlineLevel() < level) {
return currentRow + 1;
}
currentRow--;
}
public int findStartOfRowOutlineGroup(int row) {
// Find the start of the group.
RowRecord rowRecord = this.getRow( row );
int level = rowRecord.getOutlineLevel();
int currentRow = row;
while (currentRow >= 0 && this.getRow( currentRow ) != null) {
rowRecord = this.getRow( currentRow );
if (rowRecord.getOutlineLevel() < level) {
return currentRow + 1;
}
currentRow--;
}
return currentRow + 1;
}
return currentRow + 1;
}
public int findEndOfRowOutlineGroup(int row) {
int level = getRow( row ).getOutlineLevel();
int currentRow;
for (currentRow = row; currentRow < getLastRowNum(); currentRow++) {
if (getRow(currentRow) == null || getRow(currentRow).getOutlineLevel() < level) {
break;
}
}
public int findEndOfRowOutlineGroup(int row) {
int level = getRow( row ).getOutlineLevel();
int currentRow;
for (currentRow = row; currentRow < getLastRowNum(); currentRow++) {
if (getRow(currentRow) == null || getRow(currentRow).getOutlineLevel() < level) {
break;
}
}
return currentRow-1;
}
return currentRow-1;
}
/**
* Hide all rows at or below the current outline level
* @return index of the <em>next<em> row after the last row that gets hidden
*/
private int writeHidden(RowRecord pRowRecord, int row) {
int rowIx = row;
RowRecord rowRecord = pRowRecord;
int level = rowRecord.getOutlineLevel();
while (rowRecord != null && getRow(rowIx).getOutlineLevel() >= level) {
rowRecord.setZeroHeight(true);
rowIx++;
rowRecord = getRow(rowIx);
}
return rowIx;
}
/**
* Hide all rows at or below the current outline level
* @return index of the <em>next<em> row after the last row that gets hidden
*/
private int writeHidden(RowRecord pRowRecord, int row) {
int rowIx = row;
RowRecord rowRecord = pRowRecord;
int level = rowRecord.getOutlineLevel();
while (rowRecord != null && getRow(rowIx).getOutlineLevel() >= level) {
rowRecord.setZeroHeight(true);
rowIx++;
rowRecord = getRow(rowIx);
}
return rowIx;
}
public void collapseRow(int rowNumber) {
public void collapseRow(int rowNumber) {
// Find the start of the group.
int startRow = findStartOfRowOutlineGroup(rowNumber);
RowRecord rowRecord = getRow(startRow);
// Find the start of the group.
int startRow = findStartOfRowOutlineGroup(rowNumber);
RowRecord rowRecord = getRow(startRow);
// Hide all the columns until the end of the group
int nextRowIx = writeHidden(rowRecord, startRow);
// Hide all the columns until the end of the group
int nextRowIx = writeHidden(rowRecord, startRow);
RowRecord row = getRow(nextRowIx);
if (row == null) {
row = createRow(nextRowIx);
insertRow(row);
}
// Write collapse field
row.setColapsed(true);
}
RowRecord row = getRow(nextRowIx);
if (row == null) {
row = createRow(nextRowIx);
insertRow(row);
}
// Write collapse field
row.setColapsed(true);
}
/**
* Create a row record.
*
* @param rowNumber row number
* @return RowRecord created for the passed in row number
* @see org.apache.poi.hssf.record.RowRecord
*/
public static RowRecord createRow(int rowNumber) {
return new RowRecord(rowNumber);
}
/**
* Create a row record.
*
* @param rowNumber row number
* @return RowRecord created for the passed in row number
* @see org.apache.poi.hssf.record.RowRecord
*/
public static RowRecord createRow(int rowNumber) {
return new RowRecord(rowNumber);
}
public boolean isRowGroupCollapsed(int row) {
int collapseRow = findEndOfRowOutlineGroup(row) + 1;
public boolean isRowGroupCollapsed(int row) {
int collapseRow = findEndOfRowOutlineGroup(row) + 1;
return getRow(collapseRow) != null && getRow(collapseRow).getColapsed();
}
return getRow(collapseRow) != null && getRow(collapseRow).getColapsed();
}
public void expandRow(int rowNumber) {
if (rowNumber == -1)
return;
public void expandRow(int rowNumber) {
if (rowNumber == -1)
return;
// If it is already expanded do nothing.
if (!isRowGroupCollapsed(rowNumber)) {
return;
}
// If it is already expanded do nothing.
if (!isRowGroupCollapsed(rowNumber)) {
return;
}
// Find the start of the group.
int startIdx = findStartOfRowOutlineGroup(rowNumber);
RowRecord row = getRow(startIdx);
// Find the start of the group.
int startIdx = findStartOfRowOutlineGroup(rowNumber);
RowRecord row = getRow(startIdx);
// Find the end of the group.
int endIdx = findEndOfRowOutlineGroup(rowNumber);
// Find the end of the group.
int endIdx = findEndOfRowOutlineGroup(rowNumber);
// expand:
// collapsed bit must be unset
// hidden bit gets unset _if_ surrounding groups are expanded you can determine
// this by looking at the hidden bit of the enclosing group. You will have
// to look at the start and the end of the current group to determine which
// is the enclosing group
// hidden bit only is altered for this outline level. ie. don't un-collapse contained groups
if (!isRowGroupHiddenByParent(rowNumber)) {
for (int i = startIdx; i <= endIdx; i++) {
RowRecord otherRow = getRow(i);
if (row.getOutlineLevel() == otherRow.getOutlineLevel() || !isRowGroupCollapsed(i)) {
otherRow.setZeroHeight(false);
}
}
}
// expand:
// collapsed bit must be unset
// hidden bit gets unset _if_ surrounding groups are expanded you can determine
// this by looking at the hidden bit of the enclosing group. You will have
// to look at the start and the end of the current group to determine which
// is the enclosing group
// hidden bit only is altered for this outline level. ie. don't un-collapse contained groups
if (!isRowGroupHiddenByParent(rowNumber)) {
for (int i = startIdx; i <= endIdx; i++) {
RowRecord otherRow = getRow(i);
if (row.getOutlineLevel() == otherRow.getOutlineLevel() || !isRowGroupCollapsed(i)) {
otherRow.setZeroHeight(false);
}
}
}
// Write collapse field
getRow(endIdx + 1).setColapsed(false);
}
// Write collapse field
getRow(endIdx + 1).setColapsed(false);
}
public boolean isRowGroupHiddenByParent(int row) {
// Look out outline details of end
int endLevel;
boolean endHidden;
int endOfOutlineGroupIdx = findEndOfRowOutlineGroup(row);
if (getRow(endOfOutlineGroupIdx + 1) == null) {
endLevel = 0;
endHidden = false;
} else {
endLevel = getRow(endOfOutlineGroupIdx + 1).getOutlineLevel();
endHidden = getRow(endOfOutlineGroupIdx + 1).getZeroHeight();
}
public boolean isRowGroupHiddenByParent(int row) {
// Look out outline details of end
int endLevel;
boolean endHidden;
int endOfOutlineGroupIdx = findEndOfRowOutlineGroup(row);
if (getRow(endOfOutlineGroupIdx + 1) == null) {
endLevel = 0;
endHidden = false;
} else {
endLevel = getRow(endOfOutlineGroupIdx + 1).getOutlineLevel();
endHidden = getRow(endOfOutlineGroupIdx + 1).getZeroHeight();
}
// Look out outline details of start
int startLevel;
boolean startHidden;
int startOfOutlineGroupIdx = findStartOfRowOutlineGroup( row );
if (startOfOutlineGroupIdx - 1 < 0 || getRow(startOfOutlineGroupIdx - 1) == null) {
startLevel = 0;
startHidden = false;
} else {
startLevel = getRow(startOfOutlineGroupIdx - 1).getOutlineLevel();
startHidden = getRow(startOfOutlineGroupIdx - 1).getZeroHeight();
}
// Look out outline details of start
int startLevel;
boolean startHidden;
int startOfOutlineGroupIdx = findStartOfRowOutlineGroup( row );
if (startOfOutlineGroupIdx - 1 < 0 || getRow(startOfOutlineGroupIdx - 1) == null) {
startLevel = 0;
startHidden = false;
} else {
startLevel = getRow(startOfOutlineGroupIdx - 1).getOutlineLevel();
startHidden = getRow(startOfOutlineGroupIdx - 1).getZeroHeight();
}
if (endLevel > startLevel) {
return endHidden;
}
if (endLevel > startLevel) {
return endHidden;
}
return startHidden;
}
/**
* Returns an iterator for the cell values
*/
public Iterator<CellValueRecordInterface> getCellValueIterator() {
return _valuesAgg.iterator();
}
return startHidden;
}
/**
* Returns an iterator for the cell values
*/
public Iterator<CellValueRecordInterface> getCellValueIterator() {
return _valuesAgg.iterator();
}
public IndexRecord createIndexRecord(int indexRecordOffset, int sizeOfInitialSheetRecords) {
IndexRecord result = new IndexRecord();
result.setFirstRow(_firstrow);
result.setLastRowAdd1(_lastrow + 1);
// Calculate the size of the records from the end of the BOF
// and up to the RowRecordsAggregate...
public IndexRecord createIndexRecord(int indexRecordOffset, int sizeOfInitialSheetRecords) {
IndexRecord result = new IndexRecord();
result.setFirstRow(_firstrow);
result.setLastRowAdd1(_lastrow + 1);
// Calculate the size of the records from the end of the BOF
// and up to the RowRecordsAggregate...
// Add the references to the DBCells in the IndexRecord (one for each block)
// Note: The offsets are relative to the Workbook BOF. Assume that this is
// 0 for now.....
// Add the references to the DBCells in the IndexRecord (one for each block)
// Note: The offsets are relative to the Workbook BOF. Assume that this is
// 0 for now.....
int blockCount = getRowBlockCount();
// Calculate the size of this IndexRecord
int indexRecSize = IndexRecord.getRecordSizeForBlockCount(blockCount);
int blockCount = getRowBlockCount();
// Calculate the size of this IndexRecord
int indexRecSize = IndexRecord.getRecordSizeForBlockCount(blockCount);
int currentOffset = indexRecordOffset + indexRecSize + sizeOfInitialSheetRecords;
int currentOffset = indexRecordOffset + indexRecSize + sizeOfInitialSheetRecords;
for (int block = 0; block < blockCount; block++) {
// each row-block has a DBCELL record.
// The offset of each DBCELL record needs to be updated in the INDEX record
for (int block = 0; block < blockCount; block++) {
// each row-block has a DBCELL record.
// The offset of each DBCELL record needs to be updated in the INDEX record
// account for row records in this row-block
currentOffset += getRowBlockSize(block);
// account for cell value records after those
currentOffset += _valuesAgg.getRowCellBlockSize(
getStartRowNumberForBlock(block), getEndRowNumberForBlock(block));
// account for row records in this row-block
currentOffset += getRowBlockSize(block);
// account for cell value records after those
currentOffset += _valuesAgg.getRowCellBlockSize(
getStartRowNumberForBlock(block), getEndRowNumberForBlock(block));
// currentOffset is now the location of the DBCELL record for this row-block
result.addDbcell(currentOffset);
// Add space required to write the DBCELL record (whose reference was just added).
currentOffset += (8 + (getRowCountForBlock(block) * 2));
}
return result;
}
public void insertCell(CellValueRecordInterface cvRec) {
_valuesAgg.insertCell(cvRec);
}
public void removeCell(CellValueRecordInterface cvRec) {
if (cvRec instanceof FormulaRecordAggregate) {
((FormulaRecordAggregate)cvRec).notifyFormulaChanging();
}
_valuesAgg.removeCell(cvRec);
}
public FormulaRecordAggregate createFormula(int row, int col) {
FormulaRecord fr = new FormulaRecord();
fr.setRow(row);
fr.setColumn((short) col);
return new FormulaRecordAggregate(fr, null, _sharedValueManager);
}
public void updateFormulasAfterRowShift(FormulaShifter formulaShifter, int currentExternSheetIndex) {
_valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex);
}
public DimensionsRecord createDimensions() {
DimensionsRecord result = new DimensionsRecord();
result.setFirstRow(_firstrow);
result.setLastRow(_lastrow);
result.setFirstCol((short) _valuesAgg.getFirstCellNum());
result.setLastCol((short) _valuesAgg.getLastCellNum());
return result;
}
// currentOffset is now the location of the DBCELL record for this row-block
result.addDbcell(currentOffset);
// Add space required to write the DBCELL record (whose reference was just added).
currentOffset += (8 + (getRowCountForBlock(block) * 2));
}
return result;
}
public void insertCell(CellValueRecordInterface cvRec) {
_valuesAgg.insertCell(cvRec);
}
public void removeCell(CellValueRecordInterface cvRec) {
if (cvRec instanceof FormulaRecordAggregate) {
((FormulaRecordAggregate)cvRec).notifyFormulaChanging();
}
_valuesAgg.removeCell(cvRec);
}
public FormulaRecordAggregate createFormula(int row, int col) {
FormulaRecord fr = new FormulaRecord();
fr.setRow(row);
fr.setColumn((short) col);
return new FormulaRecordAggregate(fr, null, _sharedValueManager);
}
public void updateFormulasAfterRowShift(FormulaShifter formulaShifter, int currentExternSheetIndex) {
_valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex);
}
public DimensionsRecord createDimensions() {
DimensionsRecord result = new DimensionsRecord();
result.setFirstRow(_firstrow);
result.setLastRow(_lastrow);
result.setFirstCol((short) _valuesAgg.getFirstCellNum());
result.setLastCol((short) _valuesAgg.getLastCellNum());
return result;
}
}

View File

@ -19,10 +19,10 @@ package org.apache.poi.hssf.usermodel;
import java.util.Map;
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.IStabilityClassifier;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.WorkbookEvaluatorProvider;
import org.apache.poi.ss.formula.eval.BoolEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.NumericValueEval;
@ -33,8 +33,6 @@ import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.Internal;
@ -45,362 +43,251 @@ import org.apache.poi.util.Internal;
* cell values. Be sure to call {@link #clearAllCachedResultValues()} if any workbook cells are changed between
* calls to evaluate~ methods on this class.
*/
public class HSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider {
public class HSSFFormulaEvaluator extends BaseFormulaEvaluator {
private final HSSFWorkbook _book;
private final WorkbookEvaluator _bookEvaluator;
private final HSSFWorkbook _book;
public HSSFFormulaEvaluator(HSSFWorkbook workbook) {
this(workbook, null);
}
/**
* @param workbook The workbook to perform the formula evaluations in
* @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
*/
public HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) {
this(workbook, stabilityClassifier, null);
}
/**
* @param workbook The workbook to perform the formula evaluations in
public HSSFFormulaEvaluator(HSSFWorkbook workbook) {
this(workbook, null);
}
/**
* @param workbook The workbook to perform the formula evaluations in
* @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
*/
private HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
_book = workbook;
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder);
}
*/
public HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) {
this(workbook, stabilityClassifier, null);
}
/**
* @param workbook The workbook to perform the formula evaluations in
* @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
*/
public static HSSFFormulaEvaluator create(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
return new HSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder);
}
/**
* @param workbook The workbook to perform the formula evaluations in
* @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
*/
private HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
super(new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder));
_book = workbook;
}
/**
* @param workbook The workbook to perform the formula evaluations in
* @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
*/
public static HSSFFormulaEvaluator create(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
return new HSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder);
}
/**
* Coordinates several formula evaluators together so that formulas that involve external
* references can be evaluated.
* @param workbookNames the simple file names used to identify the workbooks in formulas
* with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
* @param evaluators all evaluators for the full set of workbooks required by the formulas.
*/
public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {
WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];
for (int i = 0; i < wbEvals.length; i++) {
wbEvals[i] = evaluators[i]._bookEvaluator;
}
CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);
}
/**
* Coordinates several formula evaluators together so that formulas that involve external
* references can be evaluated.
* @param workbookNames the simple file names used to identify the workbooks in formulas
* with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
* @param evaluators all evaluators for the full set of workbooks required by the formulas.
*/
public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {
BaseFormulaEvaluator.setupEnvironment(workbookNames, evaluators);
}
@Override
@Override
public void setupReferencedWorkbooks(Map<String, FormulaEvaluator> evaluators) {
CollaboratingWorkbooksEnvironment.setupFormulaEvaluator(evaluators);
}
@Override
public WorkbookEvaluator _getWorkbookEvaluator() {
return _bookEvaluator;
/**
* Should be called to tell the cell value cache that the specified (value or formula) cell
* has changed.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
public void notifyUpdateCell(HSSFCell cell) {
_bookEvaluator.notifyUpdateCell(new HSSFEvaluationCell(cell));
}
/**
* Should be called whenever there are major changes (e.g. moving sheets) to input cells
* in the evaluated workbook. If performance is not critical, a single call to this method
* may be used instead of many specific calls to the notify~ methods.
*
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
@Override
public void clearAllCachedResultValues() {
_bookEvaluator.clearAllCachedResultValues();
}
/**
* Should be called to tell the cell value cache that the specified (value or formula) cell
* has changed.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
public void notifyUpdateCell(HSSFCell cell) {
_bookEvaluator.notifyUpdateCell(new HSSFEvaluationCell(cell));
}
@Override
public void notifyUpdateCell(Cell cell) {
_bookEvaluator.notifyUpdateCell(new HSSFEvaluationCell((HSSFCell)cell));
}
/**
* Should be called to tell the cell value cache that the specified cell has just been
* deleted.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
public void notifyDeleteCell(HSSFCell cell) {
_bookEvaluator.notifyDeleteCell(new HSSFEvaluationCell(cell));
}
@Override
/**
* Should be called to tell the cell value cache that the specified cell has just been
* deleted.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
public void notifyDeleteCell(HSSFCell cell) {
_bookEvaluator.notifyDeleteCell(new HSSFEvaluationCell(cell));
}
@Override
public void notifyDeleteCell(Cell cell) {
_bookEvaluator.notifyDeleteCell(new HSSFEvaluationCell((HSSFCell)cell));
}
_bookEvaluator.notifyDeleteCell(new HSSFEvaluationCell((HSSFCell)cell));
}
/**
* Should be called to tell the cell value cache that the specified (value or formula) cell
* has changed.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
@Override
/**
* Should be called to tell the cell value cache that the specified (value or formula) cell
* has changed.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
@Override
public void notifySetFormula(Cell cell) {
_bookEvaluator.notifyUpdateCell(new HSSFEvaluationCell((HSSFCell)cell));
}
_bookEvaluator.notifyUpdateCell(new HSSFEvaluationCell((HSSFCell)cell));
}
/**
* If cell contains a formula, the formula is evaluated and returned,
* else the CellValue simply copies the appropriate cell value from
* the cell and also its cell type. This method should be preferred over
* evaluateInCell() when the call should not modify the contents of the
* original cell.
*
* @param cell may be <code>null</code> signifying that the cell is not present (or blank)
* @return <code>null</code> if the supplied cell is <code>null</code> or blank
*/
@Override
public CellValue evaluate(Cell cell) {
if (cell == null) {
return null;
}
/**
* If cell contains formula, it evaluates the formula, and saves the result of the formula. The
* cell remains as a formula cell. If the cell does not contain formula, rather than throwing an
* exception, this method returns {@link CellType#_NONE} and leaves the cell unchanged.
*
* Note that the type of the <em>formula result</em> is returned, so you know what kind of
* cached formula result is also stored with the formula.
* <pre>
* CellType evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula, and the result. If you want the cell
* replaced with the result of the formula, use {@link #evaluateInCell(org.apache.poi.ss.usermodel.Cell)}
* @param cell The cell to evaluate
* @return {@link CellType#_NONE} for non-formula cells, or the type of the <em>formula result</em>
* @since POI 3.15 beta 3
* @deprecated POI 3.15 beta 3. Will be deleted when we make the CellType enum transition. See bug 59791.
*/
@Internal
@Override
public CellType evaluateFormulaCellEnum(Cell cell) {
if (cell == null || cell.getCellTypeEnum() != CellType.FORMULA) {
return CellType._NONE;
}
CellValue cv = evaluateFormulaCellValue(cell);
// cell remains a formula cell, but the cached value is changed
setCellValue(cell, cv);
return cv.getCellType();
}
switch (cell.getCellTypeEnum()) {
case BOOLEAN:
return CellValue.valueOf(cell.getBooleanCellValue());
case ERROR:
return CellValue.getError(cell.getErrorCellValue());
case FORMULA:
return evaluateFormulaCellValue(cell);
case NUMERIC:
return new CellValue(cell.getNumericCellValue());
case STRING:
return new CellValue(cell.getRichStringCellValue().getString());
case BLANK:
return null;
default:
throw new IllegalStateException("Bad cell type (" + cell.getCellTypeEnum() + ")");
}
}
/**
* If cell contains formula, it evaluates the formula, and
* puts the formula result back into the cell, in place
* of the old formula.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
* Note that the same instance of HSSFCell is returned to
* allow chained calls like:
* <pre>
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
* </pre>
* Be aware that your cell value will be changed to hold the
* result of the formula. If you simply want the formula
* value computed for you, use {@link #evaluateFormulaCellEnum(Cell)}}
*/
@Override
public HSSFCell evaluateInCell(Cell cell) {
if (cell == null) {
return null;
}
HSSFCell result = (HSSFCell) cell;
if (cell.getCellTypeEnum() == CellType.FORMULA) {
CellValue cv = evaluateFormulaCellValue(cell);
setCellValue(cell, cv);
setCellType(cell, cv); // cell will no longer be a formula cell
}
return result;
}
private static void setCellValue(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
cell.setCellValue(cv.getBooleanValue());
break;
case ERROR:
cell.setCellErrorValue(cv.getErrorValue());
break;
case NUMERIC:
cell.setCellValue(cv.getNumberValue());
break;
case STRING:
cell.setCellValue(new HSSFRichTextString(cv.getStringValue()));
break;
case BLANK:
// never happens - blanks eventually get translated to zero
case FORMULA:
// this will never happen, we have already evaluated the formula
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
/**
* If cell contains formula, it evaluates the formula, and saves the result of the formula. The
* cell remains as a formula cell. If the cell does not contain formula, this method returns -1
* and leaves the cell unchanged.
*
* Note that the type of the <em>formula result</em> is returned, so you know what kind of
* cached formula result is also stored with the formula.
* <pre>
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula, and the result. If you want the cell
* replaced with the result of the formula, use {@link #evaluateInCell(org.apache.poi.ss.usermodel.Cell)}
* @param cell The cell to evaluate
* @return -1 for non-formula cells, or the type of the <em>formula result</em>
*/
@Override
public int evaluateFormulaCell(Cell cell) {
return evaluateFormulaCellEnum(cell).getCode();
}
/**
* If cell contains formula, it evaluates the formula, and saves the result of the formula. The
* cell remains as a formula cell. If the cell does not contain formula, rather than throwing an
* exception, this method returns {@link CellType#_NONE} and leaves the cell unchanged.
*
* Note that the type of the <em>formula result</em> is returned, so you know what kind of
* cached formula result is also stored with the formula.
* <pre>
* CellType evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula, and the result. If you want the cell
* replaced with the result of the formula, use {@link #evaluateInCell(org.apache.poi.ss.usermodel.Cell)}
* @param cell The cell to evaluate
* @return {@link CellType#_NONE} for non-formula cells, or the type of the <em>formula result</em>
* @since POI 3.15 beta 3
* @deprecated POI 3.15 beta 3. Will be deleted when we make the CellType enum transition. See bug 59791.
*/
@Internal
@Override
public CellType evaluateFormulaCellEnum(Cell cell) {
if (cell == null || cell.getCellTypeEnum() != CellType.FORMULA) {
return CellType._NONE;
}
CellValue cv = evaluateFormulaCellValue(cell);
// cell remains a formula cell, but the cached value is changed
setCellValue(cell, cv);
return cv.getCellType();
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
public static void evaluateAllFormulaCells(HSSFWorkbook wb) {
evaluateAllFormulaCells(wb, new HSSFFormulaEvaluator(wb));
}
/**
* If cell contains formula, it evaluates the formula, and
* puts the formula result back into the cell, in place
* of the old formula.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
* Note that the same instance of HSSFCell is returned to
* allow chained calls like:
* <pre>
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
* </pre>
* Be aware that your cell value will be changed to hold the
* result of the formula. If you simply want the formula
* value computed for you, use {@link #evaluateFormulaCellEnum(Cell)}}
*/
@Override
public HSSFCell evaluateInCell(Cell cell) {
if (cell == null) {
return null;
}
HSSFCell result = (HSSFCell) cell;
if (cell.getCellTypeEnum() == CellType.FORMULA) {
CellValue cv = evaluateFormulaCellValue(cell);
setCellValue(cell, cv);
setCellType(cell, cv); // cell will no longer be a formula cell
}
return result;
}
private static void setCellType(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
case ERROR:
case NUMERIC:
case STRING:
cell.setCellType(cellType);
return;
case BLANK:
// never happens - blanks eventually get translated to zero
case FORMULA:
// this will never happen, we have already evaluated the formula
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
public static void evaluateAllFormulaCells(Workbook wb) {
BaseFormulaEvaluator.evaluateAllFormulaCells(wb);
}
private static void setCellValue(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
cell.setCellValue(cv.getBooleanValue());
break;
case ERROR:
cell.setCellErrorValue(cv.getErrorValue());
break;
case NUMERIC:
cell.setCellValue(cv.getNumberValue());
break;
case STRING:
cell.setCellValue(new HSSFRichTextString(cv.getStringValue()));
break;
case BLANK:
// never happens - blanks eventually get translated to zero
case FORMULA:
// this will never happen, we have already evaluated the formula
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
@Override
public void evaluateAll() {
evaluateAllFormulaCells(_book, this);
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
public static void evaluateAllFormulaCells(HSSFWorkbook wb) {
evaluateAllFormulaCells(wb, new HSSFFormulaEvaluator(wb));
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
public static void evaluateAllFormulaCells(Workbook wb) {
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluateAllFormulaCells(wb, evaluator);
}
private static void evaluateAllFormulaCells(Workbook wb, FormulaEvaluator evaluator) {
for(int i=0; i<wb.getNumberOfSheets(); i++) {
Sheet sheet = wb.getSheetAt(i);
for(Row r : sheet) {
for (Cell c : r) {
if (c.getCellTypeEnum() == CellType.FORMULA) {
evaluator.evaluateFormulaCellEnum(c);
}
}
}
}
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
@Override
public void evaluateAll() {
evaluateAllFormulaCells(_book, this);
}
/**
* Returns a CellValue wrapper around the supplied ValueEval instance.
* @param cell
*/
private CellValue evaluateFormulaCellValue(Cell cell) {
ValueEval eval = _bookEvaluator.evaluate(new HSSFEvaluationCell((HSSFCell)cell));
if (eval instanceof BoolEval) {
BoolEval be = (BoolEval) eval;
return CellValue.valueOf(be.getBooleanValue());
}
if (eval instanceof NumericValueEval) {
NumericValueEval ne = (NumericValueEval) eval;
return new CellValue(ne.getNumberValue());
}
if (eval instanceof StringValueEval) {
StringValueEval ne = (StringValueEval) eval;
return new CellValue(ne.getStringValue());
}
if (eval instanceof ErrorEval) {
return CellValue.getError(((ErrorEval)eval).getErrorCode());
}
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
}
/**
* Returns a CellValue wrapper around the supplied ValueEval instance.
* @param cell
*/
protected CellValue evaluateFormulaCellValue(Cell cell) {
ValueEval eval = _bookEvaluator.evaluate(new HSSFEvaluationCell((HSSFCell)cell));
if (eval instanceof BoolEval) {
BoolEval be = (BoolEval) eval;
return CellValue.valueOf(be.getBooleanValue());
}
if (eval instanceof NumericValueEval) {
NumericValueEval ne = (NumericValueEval) eval;
return new CellValue(ne.getNumberValue());
}
if (eval instanceof StringValueEval) {
StringValueEval ne = (StringValueEval) eval;
return new CellValue(ne.getStringValue());
}
if (eval instanceof ErrorEval) {
return CellValue.getError(((ErrorEval)eval).getErrorCode());
}
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
}
/** {@inheritDoc} */
@Override

View File

@ -92,6 +92,7 @@ import org.apache.poi.ss.formula.SheetNameFormatter;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@ -548,7 +549,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
* the 'active' sheet (which is the sheet with focus).
* Unselects sheets that are not in <code>indexes</code>.
*
* @param indexes
* @param indexes Array of sheets to select, the index is 0-based.
*/
public void setSelectedTabs(int[] indexes) {
Collection<Integer> list = new ArrayList<Integer>(indexes.length);
@ -563,7 +564,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
* the 'active' sheet (which is the sheet with focus).
* Unselects sheets that are not in <code>indexes</code>.
*
* @param indexes
* @param indexes Collection of sheets to select, the index is 0-based.
*/
public void setSelectedTabs(Collection<Integer> indexes) {
@ -893,8 +894,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*/
@Override
public Iterator<Sheet> sheetIterator() {
Iterator<Sheet> result = new SheetIterator<Sheet>();
return result;
return new SheetIterator<Sheet>();
}
/**
@ -1280,9 +1280,9 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
/**
* Closes the underlying {@link NPOIFSFileSystem} from which
* the Workbook was read, if any. Has no effect on Workbooks
* opened from an InputStream, or newly created ones.
* <p>Once {@link #close()} has been called, no further
* the Workbook was read, if any.
*
* <p>Once this has been called, no further
* operations, updates or reads should be performed on the
* Workbook.
*/
@ -1531,6 +1531,11 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
return names.get(nameIndex);
}
@Override
public List<HSSFName> getAllNames() {
return Collections.unmodifiableList(names);
}
public NameRecord getNameRecord(int nameIndex) {
return getWorkbook().getNameRecord(nameIndex);
}
@ -1702,8 +1707,9 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
*
* @param name the name to remove.
*/
void removeName(HSSFName name) {
int index = getNameIndex(name);
@Override
public void removeName(Name name) {
int index = getNameIndex((HSSFName) name);
removeName(index);
}

View File

@ -374,20 +374,22 @@ public class CryptoFunctions {
// SET Verifier TO 0x0000
short verifier = 0;
// FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
for (int i = arrByteChars.length-1; i >= 0; i--) {
// SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
if (!"".equals(password)) {
// FOR EACH PasswordByte IN PasswordArray IN REVERSE ORDER
for (int i = arrByteChars.length-1; i >= 0; i--) {
// SET Verifier TO Intermediate3 BITWISE XOR PasswordByte
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars[i];
}
// as we haven't prepended the password length into the input array
// we need to do it now separately ...
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars[i];
verifier ^= arrByteChars.length;
// RETURN Verifier BITWISE XOR 0xCE4B
verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
}
// as we haven't prepended the password length into the input array
// we need to do it now separately ...
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars.length;
// RETURN Verifier BITWISE XOR 0xCE4B
verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
return verifier & 0xFFFF;
}

View File

@ -0,0 +1,194 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.formula;
import java.util.Map;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Common functionality across file formats for evaluating formula cells.<p/>
*/
public abstract class BaseFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider {
protected final WorkbookEvaluator _bookEvaluator;
protected BaseFormulaEvaluator(WorkbookEvaluator bookEvaluator) {
this._bookEvaluator = bookEvaluator;
}
/**
* Coordinates several formula evaluators together so that formulas that involve external
* references can be evaluated.
* @param workbookNames the simple file names used to identify the workbooks in formulas
* with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
* @param evaluators all evaluators for the full set of workbooks required by the formulas.
*/
public static void setupEnvironment(String[] workbookNames, BaseFormulaEvaluator[] evaluators) {
WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];
for (int i = 0; i < wbEvals.length; i++) {
wbEvals[i] = evaluators[i]._bookEvaluator;
}
CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);
}
@Override
public void setupReferencedWorkbooks(Map<String, FormulaEvaluator> evaluators) {
CollaboratingWorkbooksEnvironment.setupFormulaEvaluator(evaluators);
}
@Override
public WorkbookEvaluator _getWorkbookEvaluator() {
return _bookEvaluator;
}
/**
* Should be called whenever there are major changes (e.g. moving sheets) to input cells
* in the evaluated workbook. If performance is not critical, a single call to this method
* may be used instead of many specific calls to the notify~ methods.
*
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
@Override
public void clearAllCachedResultValues() {
_bookEvaluator.clearAllCachedResultValues();
}
/**
* If cell contains a formula, the formula is evaluated and returned,
* else the CellValue simply copies the appropriate cell value from
* the cell and also its cell type. This method should be preferred over
* evaluateInCell() when the call should not modify the contents of the
* original cell.
*
* @param cell may be <code>null</code> signifying that the cell is not present (or blank)
* @return <code>null</code> if the supplied cell is <code>null</code> or blank
*/
@Override
public CellValue evaluate(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellTypeEnum()) {
case BOOLEAN:
return CellValue.valueOf(cell.getBooleanCellValue());
case ERROR:
return CellValue.getError(cell.getErrorCellValue());
case FORMULA:
return evaluateFormulaCellValue(cell);
case NUMERIC:
return new CellValue(cell.getNumericCellValue());
case STRING:
return new CellValue(cell.getRichStringCellValue().getString());
case BLANK:
return null;
default:
throw new IllegalStateException("Bad cell type (" + cell.getCellTypeEnum() + ")");
}
}
protected abstract CellValue evaluateFormulaCellValue(Cell cell);
/**
* If cell contains formula, it evaluates the formula, and saves the result of the formula. The
* cell remains as a formula cell. If the cell does not contain formula, this method returns -1
* and leaves the cell unchanged.
*
* Note that the type of the <em>formula result</em> is returned, so you know what kind of
* cached formula result is also stored with the formula.
* <pre>
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula, and the result. If you want the cell
* replaced with the result of the formula, use {@link #evaluateInCell(org.apache.poi.ss.usermodel.Cell)}
* @param cell The cell to evaluate
* @return -1 for non-formula cells, or the type of the <em>formula result</em>
*/
@Override
public int evaluateFormulaCell(Cell cell) {
return evaluateFormulaCellEnum(cell).getCode();
}
protected static void setCellType(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
case ERROR:
case NUMERIC:
case STRING:
cell.setCellType(cellType);
return;
case BLANK:
// never happens - blanks eventually get translated to zero
throw new IllegalArgumentException("This should never happen. Blanks eventually get translated to zero.");
case FORMULA:
// this will never happen, we have already evaluated the formula
throw new IllegalArgumentException("This should never happen. Formulas should have already been evaluated.");
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
/**
* Loops over all cells in all sheets of the supplied
* workbook.
* For cells that contain formulas, their formulas are
* evaluated, and the results are saved. These cells
* remain as formula cells.
* For cells that do not contain formulas, no changes
* are made.
* This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one.
*/
public static void evaluateAllFormulaCells(Workbook wb) {
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluateAllFormulaCells(wb, evaluator);
}
protected static void evaluateAllFormulaCells(Workbook wb, FormulaEvaluator evaluator) {
for(int i=0; i<wb.getNumberOfSheets(); i++) {
Sheet sheet = wb.getSheetAt(i);
for(Row r : sheet) {
for (Cell c : r) {
if (c.getCellTypeEnum() == CellType.FORMULA) {
evaluator.evaluateFormulaCellEnum(c);
}
}
}
}
}
/** {@inheritDoc} */
@Override
public void setIgnoreMissingWorkbooks(boolean ignore){
_bookEvaluator.setIgnoreMissingWorkbooks(ignore);
}
/** {@inheritDoc} */
@Override
public void setDebugEvaluationOutputForNextEval(boolean value){
_bookEvaluator.setDebugEvaluationOutputForNextEval(value);
}
}

View File

@ -27,7 +27,7 @@ import org.apache.poi.ss.util.CellReference;
/**
* Provides Lazy Evaluation to a 3D Reference
*/
final class LazyRefEval extends RefEvalBase {
public final class LazyRefEval extends RefEvalBase {
private final SheetRangeEvaluator _evaluator;
public LazyRefEval(int rowIndex, int columnIndex, SheetRangeEvaluator sre) {
@ -47,14 +47,17 @@ final class LazyRefEval extends RefEvalBase {
return new LazyAreaEval(area, _evaluator);
}
public boolean isSubTotal() {
SheetRefEvaluator sheetEvaluator = _evaluator.getSheetEvaluator(getFirstSheetIndex());
return sheetEvaluator.isSubTotal(getRow(), getColumn());
}
public String toString() {
CellReference cr = new CellReference(getRow(), getColumn());
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append("[");
sb.append(_evaluator.getSheetNameRange());
sb.append('!');
sb.append(cr.formatAsString());
sb.append("]");
return sb.toString();
return getClass().getName() + "[" +
_evaluator.getSheetNameRange() +
'!' +
cr.formatAsString() +
"]";
}
}

View File

@ -19,6 +19,7 @@ package org.apache.poi.ss.formula.functions;
import static org.apache.poi.ss.formula.functions.AggregateFunction.subtotalInstance;
import org.apache.poi.ss.formula.LazyRefEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.NotImplementedException;
@ -26,6 +27,11 @@ import org.apache.poi.ss.formula.eval.NotImplementedFunctionException;
import org.apache.poi.ss.formula.eval.OperandResolver;
import org.apache.poi.ss.formula.eval.ValueEval;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Implementation for the Excel function SUBTOTAL<p>
*
@ -61,7 +67,6 @@ import org.apache.poi.ss.formula.eval.ValueEval;
public class Subtotal implements Function {
private static Function findFunction(int functionCode) throws EvaluationException {
Function func;
switch (functionCode) {
case 1: return subtotalInstance(AggregateFunction.AVERAGE);
case 2: return Count.subtotalInstance();
@ -87,7 +92,7 @@ public class Subtotal implements Function {
return ErrorEval.VALUE_INVALID;
}
Function innerFunc;
final Function innerFunc;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex);
int functionCode = OperandResolver.coerceValueToInt(ve);
@ -96,9 +101,24 @@ public class Subtotal implements Function {
return e.getErrorEval();
}
ValueEval[] innerArgs = new ValueEval[nInnerArgs];
System.arraycopy(args, 1, innerArgs, 0, nInnerArgs);
// ignore the first arg, this is the function-type, we check for the length above
final List<ValueEval> list = new ArrayList<ValueEval>(Arrays.asList(args).subList(1, args.length));
return innerFunc.evaluate(innerArgs, srcRowIndex, srcColumnIndex);
Iterator<ValueEval> it = list.iterator();
// See https://support.office.com/en-us/article/SUBTOTAL-function-7b027003-f060-4ade-9040-e478765b9939
// "If there are other subtotals within ref1, ref2,... (or nested subtotals), these nested subtotals are ignored to avoid double counting."
// For array references it is handled in other evaluation steps, but we need to handle this here for references to subtotal-functions
while(it.hasNext()) {
ValueEval eval = it.next();
if(eval instanceof LazyRefEval) {
LazyRefEval lazyRefEval = (LazyRefEval) eval;
if(lazyRefEval.isSubTotal()) {
it.remove();
}
}
}
return innerFunc.evaluate(list.toArray(new ValueEval[list.size()]), srcRowIndex, srcColumnIndex);
}
}

View File

@ -81,7 +81,7 @@ public interface CellStyle {
/**
* vertically justified vertical alignment
* @deprecated POI 3.15 beta 3. Use {@link VerticalAlignment#TOP} instead.
* @deprecated POI 3.15 beta 3. Use {@link VerticalAlignment#JUSTIFY} instead.
*/
static final short VERTICAL_JUSTIFY = 0x3; //VerticalAlignment.JUSTIFY.getCode();

View File

@ -341,9 +341,11 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
/**
* Close the underlying input resource (File or Stream),
* from which the Workbook was read. After closing, the
* Workbook should no longer be used.
* <p>This will have no effect newly created Workbooks.
* from which the Workbook was read.
*
* <p>Once this has been called, no further
* operations, updates or reads should be performed on the
* Workbook.
*/
@Override
void close() throws IOException;
@ -367,6 +369,13 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
*/
List<? extends Name> getNames(String name);
/**
* Returns all defined names.
*
* @return a list of the defined names. An empty list is returned if none is found.
*/
List<? extends Name> getAllNames();
/**
* @param nameIndex position of the named range (0-based)
* @return the defined name at the specified index
@ -405,6 +414,13 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
*/
void removeName(String name);
/**
* Remove a defined name
*
* @param name the name of the defined name
*/
void removeName(Name name);
/**
* Adds the linking required to allow formulas referencing
* the specified external workbook to be added to this one.

View File

@ -27,19 +27,13 @@ import org.apache.commons.logging.LogFactory;
* developers to write log calls, while simultaneously making those
* calls as cheap as possible by performing lazy evaluation of the log
* message.<p>
*
* @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
* @author Nicola Ken Barozzi (nicolaken at apache.org)
*/
public class CommonsLogger extends POILogger
{
private static LogFactory _creator = LogFactory.getFactory();
private Log log = null;
@Override
public void initialize(final String cat)
{
this.log = _creator.getInstance(cat);
@ -51,6 +45,7 @@ public class CommonsLogger extends POILogger
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
* @param obj1 The object to log.
*/
@Override
public void log(final int level, final Object obj1)
{
if(level==FATAL)
@ -104,6 +99,7 @@ public class CommonsLogger extends POILogger
* @param obj1 The object to log. This is converted to a string.
* @param exception An exception to be logged
*/
@Override
public void log(final int level, final Object obj1,
final Throwable exception)
{
@ -175,7 +171,7 @@ public class CommonsLogger extends POILogger
*
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
*/
@Override
public boolean check(final int level)
{
if(level==FATAL)

View File

@ -22,14 +22,10 @@ package org.apache.poi.util;
* developers to write log calls, while simultaneously making those
* calls as cheap as possible by performing lazy evaluation of the log
* message.<p>
*
* @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
* @author Nicola Ken Barozzi (nicolaken at apache.org)
*/
public class NullLogger extends POILogger {
@Override
public void initialize(final String cat){
public void initialize(final String cat) {
// do nothing
}
@ -41,8 +37,7 @@ public class NullLogger extends POILogger {
*/
@Override
public void log(final int level, final Object obj1)
{
public void log(final int level, final Object obj1) {
// do nothing
}
@ -53,6 +48,7 @@ public class NullLogger extends POILogger {
* @param obj1 The object to log. This is converted to a string.
* @param exception An exception to be logged
*/
@Override
public void log(int level, Object obj1, final Throwable exception) {
// do nothing
}

View File

@ -24,15 +24,12 @@ package org.apache.poi.util;
* developers to write log calls, while simultaneously making those
* calls as cheap as possible by performing lazy evaluation of the log
* message.
*
* @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
* @author Nicola Ken Barozzi (nicolaken at apache.org)
*/
public class SystemOutLogger extends POILogger
{
private String _cat;
@Override
public void initialize(final String cat)
{
this._cat=cat;
@ -44,7 +41,7 @@ public class SystemOutLogger extends POILogger
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
* @param obj1 The object to log.
*/
@Override
public void log(final int level, final Object obj1)
{
log(level, obj1, null);
@ -57,6 +54,7 @@ public class SystemOutLogger extends POILogger
* @param obj1 The object to log. This is converted to a string.
* @param exception An exception to be logged
*/
@Override
@SuppressForbidden("uses printStackTrace")
public void log(final int level, final Object obj1,
final Throwable exception) {
@ -78,6 +76,7 @@ public class SystemOutLogger extends POILogger
* @see #ERROR
* @see #FATAL
*/
@Override
public boolean check(final int level)
{
int currentLevel;

View File

@ -193,8 +193,12 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart implements Close
/**
* Closes the underlying {@link OPCPackage} from which this
* document was read, if there is one
*
* @throws IOException for writable packages, if an IO exception occur during the saving process.
*
* <p>Once this has been called, no further
* operations, updates or reads should be performed on the
* document.
*
* @throws IOException for writable packages, if an IO exception occur during the saving process.
*/
@Override
public void close() throws IOException {

View File

@ -382,8 +382,7 @@ public abstract class OPCPackage implements RelationshipSource, Closeable {
}
// Creates a new package
OPCPackage pkg = null;
pkg = new ZipPackage();
OPCPackage pkg = new ZipPackage();
pkg.originalPackagePath = file.getAbsolutePath();
configurePackage(pkg);
@ -391,8 +390,7 @@ public abstract class OPCPackage implements RelationshipSource, Closeable {
}
public static OPCPackage create(OutputStream output) {
OPCPackage pkg = null;
pkg = new ZipPackage();
OPCPackage pkg = new ZipPackage();
pkg.originalPackagePath = null;
pkg.output = output;
@ -542,7 +540,7 @@ public abstract class OPCPackage implements RelationshipSource, Closeable {
// Create the thumbnail part name
String contentType = ContentTypes
.getContentTypeFromFileExtension(filename);
PackagePartName thumbnailPartName = null;
PackagePartName thumbnailPartName;
try {
thumbnailPartName = PackagingURIHelper.createPartName("/docProps/"
+ filename);

View File

@ -29,10 +29,7 @@ import java.util.TreeMap;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.*;
import org.apache.poi.util.DocumentHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@ -54,7 +51,7 @@ public abstract class ContentTypeManager {
/**
* Content type namespace
*/
public static final String TYPES_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/content-types";
public static final String TYPES_NAMESPACE_URI = PackageNamespaces.CONTENT_TYPES;
/* Xml elements in content type part */

View File

@ -304,13 +304,13 @@ implements XSLFShapeContainer, GroupShape<XSLFShape,XSLFTextParagraph> {
@Override
public boolean getFlipHorizontal(){
CTGroupTransform2D xfrm = getXfrm();
return (xfrm == null || !xfrm.isSetFlipH()) ? false : xfrm.getFlipH();
return !(xfrm == null || !xfrm.isSetFlipH()) && xfrm.getFlipH();
}
@Override
public boolean getFlipVertical(){
CTGroupTransform2D xfrm = getXfrm();
return (xfrm == null || !xfrm.isSetFlipV()) ? false : xfrm.getFlipV();
return !(xfrm == null || !xfrm.isSetFlipV()) && xfrm.getFlipV();
}
@Override
@ -333,7 +333,7 @@ implements XSLFShapeContainer, GroupShape<XSLFShape,XSLFTextParagraph> {
// recursively update each shape
for(XSLFShape shape : gr.getShapes()) {
XSLFShape newShape = null;
XSLFShape newShape;
if (shape instanceof XSLFTextBox) {
newShape = createTextBox();
} else if (shape instanceof XSLFAutoShape) {

View File

@ -41,7 +41,6 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.util.DocumentHelper;
@ -55,7 +54,6 @@ import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFTable;
import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell;
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
@ -117,8 +115,7 @@ public class XSSFExportToXml implements Comparator<String>{
* @param validate if true, validates the XML againts the XML Schema
* @throws SAXException
* @throws ParserConfigurationException
* @throws TransformerException
* @throws InvalidFormatException
* @throws TransformerException
*/
public void exportToXML(OutputStream os, String encoding, boolean validate) throws SAXException, ParserConfigurationException, TransformerException{
List<XSSFSingleXmlCell> singleXMLCells = map.getRelatedSingleXMLCell();
@ -128,10 +125,10 @@ public class XSSFExportToXml implements Comparator<String>{
Document doc = DocumentHelper.createDocument();
Element root = null;
final Element root;
if (isNamespaceDeclared()) {
root=doc.createElementNS(getNamespace(),rootElement);
root = doc.createElementNS(getNamespace(),rootElement);
} else {
root = doc.createElementNS("", rootElement);
}
@ -152,7 +149,6 @@ public class XSSFExportToXml implements Comparator<String>{
tableMappings.put(commonXPath, table);
}
Collections.sort(xpaths,this);
for(String xpath : xpaths) {
@ -167,8 +163,7 @@ public class XSSFExportToXml implements Comparator<String>{
XSSFCell cell = simpleXmlCell.getReferencedCell();
if (cell!=null) {
Node currentNode = getNodeByXPath(xpath,doc.getFirstChild(),doc,false);
STXmlDataType.Enum dataType = simpleXmlCell.getXmlDataType();
mapCellOnNode(cell,currentNode,dataType);
mapCellOnNode(cell,currentNode);
//remove nodes which are empty in order to keep the output xml valid
if("".equals(currentNode.getTextContent()) && currentNode.getParentNode() != null) {
@ -202,22 +197,15 @@ public class XSSFExportToXml implements Comparator<String>{
XSSFXmlColumnPr pointer = tableColumns.get(j-startColumnIndex);
String localXPath = pointer.getLocalXPath();
Node currentNode = getNodeByXPath(localXPath,tableRootNode,doc,false);
STXmlDataType.Enum dataType = pointer.getXmlDataType();
mapCellOnNode(cell,currentNode,dataType);
mapCellOnNode(cell,currentNode);
}
}
}
}
} else {
} /*else {
// TODO: implement filtering management in xpath
}
}*/
}
boolean isValid = true;
@ -225,8 +213,6 @@ public class XSSFExportToXml implements Comparator<String>{
isValid =isValid(doc);
}
if (isValid) {
/////////////////
@ -275,7 +261,7 @@ public class XSSFExportToXml implements Comparator<String>{
}
private void mapCellOnNode(XSSFCell cell, Node node, STXmlDataType.Enum outputDataType) {
private void mapCellOnNode(XSSFCell cell, Node node) {
String value ="";
switch (cell.getCellTypeEnum()) {
@ -349,11 +335,7 @@ public class XSSFExportToXml implements Comparator<String>{
}
currentNode = selectedNode;
} else {
Node attribute = createAttribute(doc, currentNode, axisName);
currentNode = attribute;
currentNode = createAttribute(doc, currentNode, axisName);
}
}
return currentNode;
@ -421,12 +403,11 @@ public class XSSFExportToXml implements Comparator<String>{
for(int i =1;i <minLenght; i++) {
String leftElementName =leftTokens[i];
String leftElementName = leftTokens[i];
String rightElementName = rightTokens[i];
if (leftElementName.equals(rightElementName)) {
Node complexType = getComplexTypeForElement(leftElementName, xmlSchema,localComplexTypeRootNode);
localComplexTypeRootNode = complexType;
localComplexTypeRootNode = getComplexTypeForElement(leftElementName, xmlSchema, localComplexTypeRootNode);
} else {
int leftIndex = indexOfElementInComplexType(leftElementName,localComplexTypeRootNode);
int rightIndex = indexOfElementInComplexType(rightElementName,localComplexTypeRootNode);
@ -436,9 +417,9 @@ public class XSSFExportToXml implements Comparator<String>{
}if ( leftIndex > rightIndex) {
return 1;
}
} else {
} /*else {
// NOTE: the xpath doesn't match correctly in the schema
}
}*/
}
}
@ -483,7 +464,7 @@ public class XSSFExportToXml implements Comparator<String>{
// Note: we expect that all the complex types are defined at root level
Node complexTypeNode = null;
if (!"".equals(complexTypeName)) {
complexTypeNode = getComplexTypeNodeFromSchemaChildren(xmlSchema, complexTypeNode, complexTypeName);
complexTypeNode = getComplexTypeNodeFromSchemaChildren(xmlSchema, null, complexTypeName);
}
return complexTypeNode;

View File

@ -338,7 +338,11 @@ public class SXSSFCell implements Cell {
}
if(_value.getType()==CellType.FORMULA)
((StringFormulaValue)_value).setPreEvaluatedValue(value);
if(_value instanceof NumericFormulaValue) {
((NumericFormulaValue) _value).setPreEvaluatedValue(Double.parseDouble(value));
} else {
((StringFormulaValue) _value).setPreEvaluatedValue(value);
}
else
((PlainStringValue)_value).setValue(value);
} else {
@ -956,6 +960,7 @@ public class SXSSFCell implements Cell {
}
/*package*/ void setFormulaType(CellType type)
{
Value prevValue = _value;
switch(type)
{
case NUMERIC:
@ -983,7 +988,13 @@ public class SXSSFCell implements Cell {
throw new IllegalArgumentException("Illegal type " + type);
}
}
// if we had a Formula before, we should copy over the _value of the formula
if(prevValue instanceof FormulaValue) {
((FormulaValue)_value)._value = ((FormulaValue)prevValue)._value;
}
}
//TODO: implement this correctly
@NotImplemented
/*package*/ CellType computeTypeFromFormula(String formula)

View File

@ -893,8 +893,11 @@ public class SXSSFWorkbook implements Workbook {
/**
* Closes the underlying {@link XSSFWorkbook} and {@link OPCPackage}
* on which this Workbook is based, if any. Has no effect on Workbooks
* created from scratch.
* on which this Workbook is based, if any.
*
* <p>Once this has been called, no further
* operations, updates or reads should be performed on the
* Workbook.
*/
@Override
public void close() throws IOException {
@ -1003,12 +1006,25 @@ public class SXSSFWorkbook implements Workbook {
return _wb.getNames(name);
}
/**
* Returns all defined names
*
* @return all defined names
*/
@Override
public List<? extends Name> getAllNames()
{
return _wb.getAllNames();
}
/**
* @param nameIndex position of the named range (0-based)
* @return the defined name at the specified index
* @throws IllegalArgumentException if the supplied index is invalid
* @deprecated 3.16. New projects should avoid accessing named ranges by index.
*/
@Override
@Deprecated
public Name getNameAt(int nameIndex)
{
return _wb.getNameAt(nameIndex);
@ -1033,8 +1049,12 @@ public class SXSSFWorkbook implements Workbook {
*
* @param name the name of the defined name
* @return zero based index of the defined name. <code>-1</code> if not found.
*
* @deprecated 3.16. New projects should avoid accessing named ranges by index.
* Use {@link #getName(String)} instead.
*/
@Override
@Deprecated
public int getNameIndex(String name)
{
return _wb.getNameIndex(name);
@ -1044,8 +1064,11 @@ public class SXSSFWorkbook implements Workbook {
* Remove the defined name at the specified index
*
* @param index named range index (0 based)
*
* @deprecated 3.16. New projects should use {@link #removeName(Name)}.
*/
@Override
@Deprecated
public void removeName(int index)
{
_wb.removeName(index);
@ -1054,10 +1077,24 @@ public class SXSSFWorkbook implements Workbook {
/**
* Remove a defined name by name
*
* @param name the name of the defined name
* @param name the name of the defined name
*
* @deprecated 3.16. New projects should use {@link #removeName(Name)}.
*/
@Override
@Deprecated
public void removeName(String name)
{
_wb.removeName(name);
}
/**
* Remove the given defined name
*
* @param name the name to remove
*/
@Override
public void removeName(Name name)
{
_wb.removeName(name);
}

View File

@ -232,7 +232,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
// Otherwise, try it as a named range
if (sheet == null) {
if (_uBook.getNameIndex(name) > -1) {
if (!_uBook.getNames(name).isEmpty()) {
return new NameXPxg(null, name);
}
return null;

View File

@ -17,12 +17,9 @@
package org.apache.poi.xssf.usermodel;
import java.util.Map;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.WorkbookEvaluatorProvider;
import org.apache.poi.ss.formula.eval.BoolEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.NumberEval;
@ -31,28 +28,16 @@ import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.util.Internal;
/**
* Internal POI use only - parent of XSSF and SXSSF formula evaluators
*/
public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider {
private WorkbookEvaluator _bookEvaluator;
public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator {
protected BaseXSSFFormulaEvaluator(WorkbookEvaluator bookEvaluator) {
_bookEvaluator = bookEvaluator;
super(bookEvaluator);
}
/**
* Should be called whenever there are major changes (e.g. moving sheets) to input cells
* in the evaluated workbook.
* Failure to call this method after changing cell values will cause incorrect behaviour
* of the evaluate~ methods of this class
*/
public void clearAllCachedResultValues() {
_bookEvaluator.clearAllCachedResultValues();
}
public void notifySetFormula(Cell cell) {
_bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
}
@ -63,60 +48,6 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
_bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
}
/**
* If cell contains a formula, the formula is evaluated and returned,
* else the CellValue simply copies the appropriate cell value from
* the cell and also its cell type. This method should be preferred over
* evaluateInCell() when the call should not modify the contents of the
* original cell.
* @param cell
*/
public CellValue evaluate(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellTypeEnum()) {
case BOOLEAN:
return CellValue.valueOf(cell.getBooleanCellValue());
case ERROR:
return CellValue.getError(cell.getErrorCellValue());
case FORMULA:
return evaluateFormulaCellValue(cell);
case NUMERIC:
return new CellValue(cell.getNumericCellValue());
case STRING:
return new CellValue(cell.getRichStringCellValue().getString());
case BLANK:
return null;
default:
throw new IllegalStateException("Bad cell type (" + cell.getCellTypeEnum() + ")");
}
}
/**
* If cell contains formula, it evaluates the formula,
* and saves the result of the formula. The cell
* remains as a formula cell.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
* Note that the type of the formula result is returned,
* so you know what kind of value is also stored with
* the formula.
* <pre>
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula,
* and the result. If you want the cell replaced with
* the result of the formula, use {@link #evaluate(org.apache.poi.ss.usermodel.Cell)} }
* @param cell The cell to evaluate
* @return The type of the formula result (the cell's type remains as CellType.FORMULA however)
*/
public int evaluateFormulaCell(Cell cell) {
return evaluateFormulaCellEnum(cell).getCode();
}
/**
* If cell contains formula, it evaluates the formula,
* and saves the result of the formula. The cell
@ -164,27 +95,6 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
setCellValue(cell, cv);
}
}
private static void setCellType(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
switch (cellType) {
case BOOLEAN:
case ERROR:
case NUMERIC:
case STRING:
cell.setCellType(cellType);
return;
case BLANK:
// never happens - blanks eventually get translated to zero
throw new IllegalArgumentException("This should never happen. Blanks eventually get translated to zero.");
case FORMULA:
// this will never happen, we have already evaluated the formula
throw new IllegalArgumentException("This should never happen. Formulas should have already been evaluated.");
default:
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
}
}
private static void setCellValue(Cell cell, CellValue cv) {
CellType cellType = cv.getCellType();
@ -218,7 +128,7 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
/**
* Returns a CellValue wrapper around the supplied ValueEval instance.
*/
private CellValue evaluateFormulaCellValue(Cell cell) {
protected CellValue evaluateFormulaCellValue(Cell cell) {
EvaluationCell evalCell = toEvaluationCell(cell);
ValueEval eval = _bookEvaluator.evaluate(evalCell);
if (eval instanceof NumberEval) {
@ -238,22 +148,4 @@ public abstract class BaseXSSFFormulaEvaluator implements FormulaEvaluator, Work
}
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
}
public void setupReferencedWorkbooks(Map<String, FormulaEvaluator> evaluators) {
CollaboratingWorkbooksEnvironment.setupFormulaEvaluator(evaluators);
}
public WorkbookEvaluator _getWorkbookEvaluator() {
return _bookEvaluator;
}
/** {@inheritDoc} */
public void setIgnoreMissingWorkbooks(boolean ignore){
_bookEvaluator.setIgnoreMissingWorkbooks(ignore);
}
/** {@inheritDoc} */
public void setDebugEvaluationOutputForNextEval(boolean value){
_bookEvaluator.setDebugEvaluationOutputForNextEval(value);
}
}

View File

@ -17,7 +17,7 @@
package org.apache.poi.xssf.usermodel;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.IStabilityClassifier;
import org.apache.poi.ss.formula.WorkbookEvaluator;
@ -88,7 +88,7 @@ public final class XSSFFormulaEvaluator extends BaseXSSFFormulaEvaluator {
* cells, and calling evaluateFormulaCell on each one.
*/
public static void evaluateAllFormulaCells(XSSFWorkbook wb) {
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
BaseFormulaEvaluator.evaluateAllFormulaCells(wb);
}
/**
* Loops over all cells in all sheets of the supplied
@ -102,7 +102,7 @@ public final class XSSFFormulaEvaluator extends BaseXSSFFormulaEvaluator {
* cells, and calling evaluateFormulaCell on each one.
*/
public void evaluateAll() {
HSSFFormulaEvaluator.evaluateAllFormulaCells(_book);
evaluateAllFormulaCells(_book, this);
}
/**

View File

@ -167,19 +167,18 @@ public final class XSSFName implements Name {
public void setNameName(String name) {
validateName(name);
String oldName = getNameName();
int sheetIndex = getSheetIndex();
int numberOfNames = _workbook.getNumberOfNames();
//Check to ensure no other names have the same case-insensitive name at the same scope
for (int i = 0; i < numberOfNames; i++) {
XSSFName nm = _workbook.getNameAt(i);
if ((nm != this)
&& name.equalsIgnoreCase(nm.getNameName())
&& (sheetIndex == nm.getSheetIndex())) {
for (XSSFName foundName : _workbook.getNames(name)) {
if (foundName.getSheetIndex() == sheetIndex && foundName != this) {
String msg = "The "+(sheetIndex == -1 ? "workbook" : "sheet")+" already contains this name: " + name;
throw new IllegalArgumentException(msg);
}
}
_ctName.setName(name);
//Need to update the name -> named ranges map
_workbook.updateName(this, oldName);
}
public String getRefersToFormula() {

View File

@ -18,8 +18,8 @@
package org.apache.poi.xssf.usermodel;
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.setPassword;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.validatePassword;
import java.io.IOException;
import java.io.InputStream;

View File

@ -18,8 +18,8 @@
package org.apache.poi.xssf.usermodel;
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.setPassword;
import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.validatePassword;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -29,16 +29,20 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import org.apache.commons.collections4.ListValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
@ -59,6 +63,7 @@ import org.apache.poi.ss.formula.SheetNameFormatter;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
@ -140,6 +145,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
*/
private List<XSSFSheet> sheets;
/**
* this holds the XSSFName objects attached to this workbook, keyed by lower-case name
*/
private ListValuedMap<String, XSSFName> namedRangesByName;
/**
* this holds the XSSFName objects attached to this workbook
*/
@ -442,6 +452,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
stylesSource.setWorkbook(this);
namedRanges = new ArrayList<XSSFName>();
namedRangesByName = new ArrayListValuedHashMap<String, XSSFName>();
sheets = new ArrayList<XSSFSheet>();
pivotTables = new ArrayList<XSSFPivotTable>();
}
@ -733,8 +744,13 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
public XSSFName createName() {
CTDefinedName ctName = CTDefinedName.Factory.newInstance();
ctName.setName("");
return createAndStoreName(ctName);
}
private XSSFName createAndStoreName(CTDefinedName ctName) {
XSSFName name = new XSSFName(ctName, this);
namedRanges.add(name);
namedRangesByName.put(ctName.getName().toLowerCase(Locale.ENGLISH), name);
return name;
}
@ -938,28 +954,47 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
return stylesSource.getFontAt(idx);
}
/**
* Get the first named range with the given name.
*
* Note: names of named ranges are not unique as they are scoped by sheet.
* {@link #getNames(String name)} returns all named ranges with the given name.
*
* @param name named range name
* @return XSSFName with the given name. <code>null</code> is returned no named range could be found.
*/
@Override
public XSSFName getName(String name) {
int nameIndex = getNameIndex(name);
if (nameIndex < 0) {
Collection<XSSFName> list = getNames(name);
if (list.isEmpty()) {
return null;
}
return namedRanges.get(nameIndex);
return list.iterator().next();
}
/**
* Get the named ranges with the given name.
* <i>Note:</i>Excel named ranges are case-insensitive and
* this method performs a case-insensitive search.
*
* @param name named range name
* @return list of XSSFNames with the given name. An empty list if no named ranges could be found
*/
@Override
public List<XSSFName> getNames(String name) {
List<XSSFName> names = new ArrayList<XSSFName>();
for(XSSFName nr : namedRanges) {
if(nr.getNameName().equals(name)) {
names.add(nr);
}
}
return names;
return Collections.unmodifiableList(namedRangesByName.get(name.toLowerCase(Locale.ENGLISH)));
}
/**
* Get the named range at the given index.
*
* @param nameIndex the index of the named range
* @return the XSSFName at the given index
*
* @deprecated 3.16. New projects should avoid accessing named ranges by index.
*/
@Override
@Deprecated
public XSSFName getNameAt(int nameIndex) {
int nNames = namedRanges.size();
if (nNames < 1) {
@ -973,21 +1008,30 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
/**
* Gets the named range index by his name
* <i>Note:</i>Excel named ranges are case-insensitive and
* this method performs a case-insensitive search.
* Get a list of all the named ranges in the workbook.
*
* @param name named range name
* @return named range index
* @return list of XSSFNames in the workbook
*/
@Override
public List<XSSFName> getAllNames() {
return Collections.unmodifiableList(namedRanges);
}
/**
* Gets the named range index by name.
*
* @param name named range name
* @return named range index. <code>-1</code> is returned if no named ranges could be found.
*
* @deprecated 3.16. New projects should avoid accessing named ranges by index.
* Use {@link #getName(String)} instead.
*/
@Override
@Deprecated
public int getNameIndex(String name) {
int i = 0;
for(XSSFName nr : namedRanges) {
if(nr.getNameName().equals(name)) {
return i;
}
i++;
XSSFName nm = getName(name);
if (nm != null) {
return namedRanges.indexOf(nm);
}
return -1;
}
@ -1258,22 +1302,40 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
return getPackagePart().getContentType().equals(XSSFRelation.MACROS_WORKBOOK.getContentType());
}
/**
* Remove the named range at the given index.
*
* @param nameIndex the index of the named range name to remove
*
* @deprecated 3.16. New projects should use {@link #removeName(Name)}.
*/
@Override
@Deprecated
public void removeName(int nameIndex) {
namedRanges.remove(nameIndex);
removeName(getNameAt(nameIndex));
}
/**
* Remove the first named range found with the given name.
*
* Note: names of named ranges are not unique (name + sheet
* index is unique), so {@link #removeName(Name)} should
* be used if possible.
*
* @param name the named range name to remove
*
* @throws IllegalArgumentException if no named range could be found
*
* @deprecated 3.16. New projects should use {@link #removeName(Name)}.
*/
@Override
@Deprecated
public void removeName(String name) {
int idx = 0;
for (XSSFName nm : namedRanges) {
if(nm.getNameName().equalsIgnoreCase(name)) {
removeName(idx);
return;
}
idx++;
List<XSSFName> names = namedRangesByName.get(name.toLowerCase(Locale.ENGLISH));
if (names.isEmpty()) {
throw new IllegalArgumentException("Named range was not found: " + name);
}
throw new IllegalArgumentException("Named range was not found: " + name);
removeName(names.get(0));
}
@ -1282,13 +1344,24 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
* (name + sheet index is unique), this method is more accurate.
*
* @param name the name to remove.
*
* @throws IllegalArgumentException if the named range is not a part of this XSSFWorkbook
*/
void removeName(XSSFName name) {
if (!namedRanges.remove(name)) {
@Override
public void removeName(Name name) {
if (!namedRangesByName.removeMapping(name.getNameName().toLowerCase(Locale.ENGLISH), name)
|| !namedRanges.remove(name)) {
throw new IllegalArgumentException("Name was not found: " + name);
}
}
void updateName(XSSFName name, String oldName) {
if (!namedRangesByName.removeMapping(oldName.toLowerCase(Locale.ENGLISH), name)) {
throw new IllegalArgumentException("Name was not found: " + name);
}
namedRangesByName.put(name.getNameName().toLowerCase(Locale.ENGLISH), name);
}
/**
* Delete the printarea for the sheet specified
@ -1297,13 +1370,9 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
*/
@Override
public void removePrintArea(int sheetIndex) {
int cont = 0;
for (XSSFName name : namedRanges) {
if (name.getNameName().equals(XSSFName.BUILTIN_PRINT_AREA) && name.getSheetIndex() == sheetIndex) {
namedRanges.remove(cont);
break;
}
cont++;
XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
if (name != null) {
removeName(name);
}
}
@ -1369,17 +1438,20 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
//adjust indices of names ranges
for (Iterator<XSSFName> it = namedRanges.iterator(); it.hasNext();) {
XSSFName nm = it.next();
List<XSSFName> toRemove = new ArrayList<XSSFName>();
for (XSSFName nm : namedRanges) {
CTDefinedName ct = nm.getCTName();
if(!ct.isSetLocalSheetId()) continue;
if (ct.getLocalSheetId() == index) {
it.remove();
toRemove.add(nm);
} else if (ct.getLocalSheetId() > index){
// Bump down by one, so still points at the same sheet
ct.setLocalSheetId(ct.getLocalSheetId()-1);
}
}
for (XSSFName nm : toRemove) {
removeName(nm);
}
}
/**
@ -1514,8 +1586,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
XSSFName getBuiltInName(String builtInCode, int sheetNumber) {
for (XSSFName name : namedRanges) {
if (name.getNameName().equalsIgnoreCase(builtInCode) && name.getSheetIndex() == sheetNumber) {
for (XSSFName name : namedRangesByName.get(builtInCode.toLowerCase(Locale.ENGLISH))) {
if (name.getSheetIndex() == sheetNumber) {
return name;
}
}
@ -1537,15 +1609,12 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
nameRecord.setName(builtInName);
nameRecord.setLocalSheetId(sheetNumber);
XSSFName name = new XSSFName(nameRecord, this);
for (XSSFName nr : namedRanges) {
if (nr.equals(name))
throw new POIXMLException("Builtin (" + builtInName
+ ") already exists for sheet (" + sheetNumber + ")");
if (getBuiltInName(builtInName, sheetNumber) != null) {
throw new POIXMLException("Builtin (" + builtInName
+ ") already exists for sheet (" + sheetNumber + ")");
}
namedRanges.add(name);
return name;
return createAndStoreName(nameRecord);
}
/**
@ -1665,10 +1734,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
private void reprocessNamedRanges() {
namedRangesByName = new ArrayListValuedHashMap<String, XSSFName>();
namedRanges = new ArrayList<XSSFName>();
if(workbook.isSetDefinedNames()) {
for(CTDefinedName ctName : workbook.getDefinedNames().getDefinedNameArray()) {
namedRanges.add(new XSSFName(ctName, this));
createAndStoreName(ctName);
}
}
}

View File

@ -65,9 +65,7 @@ public final class XSSFFormulaUtils {
*/
public void updateSheetName(final int sheetIndex, final String oldName, final String newName) {
// update named ranges
final int numberOfNames = _wb.getNumberOfNames();
for (int i = 0; i < numberOfNames; i++) {
XSSFName nm = _wb.getNameAt(i);
for (XSSFName nm : _wb.getAllNames()) {
if (nm.getSheetIndex() == -1 || nm.getSheetIndex() == sheetIndex) {
updateName(nm, oldName, newName);
}

View File

@ -0,0 +1,136 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.apache.poi.xssf.usermodel.helpers;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Locale;
import javax.xml.bind.DatatypeConverter;
import javax.xml.namespace.QName;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.util.Internal;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
@Internal(since="3.15 beta 3")
public final class XSSFPasswordHelper {
private XSSFPasswordHelper() {
// no instances of this static class
}
/**
* Sets the XORed or hashed password
*
* @param xobj the xmlbeans object which contains the password attributes
* @param password the password, if null, the password attributes will be removed
* @param hashAlgo the hash algorithm, if null the password will be XORed
* @param prefix the prefix of the password attributes, may be null
*/
public static void setPassword(XmlObject xobj, String password, HashAlgorithm hashAlgo, String prefix) {
XmlCursor cur = xobj.newCursor();
if (password == null) {
cur.removeAttribute(getAttrName(prefix, "password"));
cur.removeAttribute(getAttrName(prefix, "algorithmName"));
cur.removeAttribute(getAttrName(prefix, "hashValue"));
cur.removeAttribute(getAttrName(prefix, "saltValue"));
cur.removeAttribute(getAttrName(prefix, "spinCount"));
return;
}
cur.toFirstContentToken();
if (hashAlgo == null) {
int hash = CryptoFunctions.createXorVerifier1(password);
cur.insertAttributeWithValue(getAttrName(prefix, "password"),
String.format(Locale.ROOT, "%04X", hash).toUpperCase(Locale.ROOT));
} else {
SecureRandom random = new SecureRandom();
byte salt[] = random.generateSeed(16);
// Iterations specifies the number of times the hashing function shall be iteratively run (using each
// iteration's result as the input for the next iteration).
int spinCount = 100000;
// Implementation Notes List:
// --> In this third stage, the reversed byte order legacy hash from the second stage shall
// be converted to Unicode hex string representation
byte hash[] = CryptoFunctions.hashPassword(password, hashAlgo, salt, spinCount, false);
cur.insertAttributeWithValue(getAttrName(prefix, "algorithmName"), hashAlgo.jceId);
cur.insertAttributeWithValue(getAttrName(prefix, "hashValue"), DatatypeConverter.printBase64Binary(hash));
cur.insertAttributeWithValue(getAttrName(prefix, "saltValue"), DatatypeConverter.printBase64Binary(salt));
cur.insertAttributeWithValue(getAttrName(prefix, "spinCount"), ""+spinCount);
}
cur.dispose();
}
/**
* Validates the password, i.e.
* calculates the hash of the given password and compares it against the stored hash
*
* @param xobj the xmlbeans object which contains the password attributes
* @param password the password, if null the method will always return false,
* even if there's no password set
* @param prefix the prefix of the password attributes, may be null
*
* @return true, if the hashes match
*/
public static boolean validatePassword(XmlObject xobj, String password, String prefix) {
// TODO: is "velvetSweatshop" the default password?
if (password == null) return false;
XmlCursor cur = xobj.newCursor();
String xorHashVal = cur.getAttributeText(getAttrName(prefix, "password"));
String algoName = cur.getAttributeText(getAttrName(prefix, "algorithmName"));
String hashVal = cur.getAttributeText(getAttrName(prefix, "hashValue"));
String saltVal = cur.getAttributeText(getAttrName(prefix, "saltValue"));
String spinCount = cur.getAttributeText(getAttrName(prefix, "spinCount"));
cur.dispose();
if (xorHashVal != null) {
int hash1 = Integer.parseInt(xorHashVal, 16);
int hash2 = CryptoFunctions.createXorVerifier1(password);
return hash1 == hash2;
} else {
if (hashVal == null || algoName == null || saltVal == null || spinCount == null) {
return false;
}
byte hash1[] = DatatypeConverter.parseBase64Binary(hashVal);
HashAlgorithm hashAlgo = HashAlgorithm.fromString(algoName);
byte salt[] = DatatypeConverter.parseBase64Binary(saltVal);
int spinCnt = Integer.parseInt(spinCount);
byte hash2[] = CryptoFunctions.hashPassword(password, hashAlgo, salt, spinCnt, false);
return Arrays.equals(hash1, hash2);
}
}
private static QName getAttrName(String prefix, String name) {
if (prefix == null || "".equals(prefix)) {
return new QName(name);
} else {
return new QName(prefix+Character.toUpperCase(name.charAt(0))+name.substring(1));
}
}
}

View File

@ -1,130 +1,60 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.apache.poi.xssf.usermodel.helpers;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Locale;
import javax.xml.bind.DatatypeConverter;
import javax.xml.namespace.QName;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
public class XSSFPaswordHelper {
/**
* Sets the XORed or hashed password
*
* @param xobj the xmlbeans object which contains the password attributes
* @param password the password, if null, the password attributes will be removed
* @param hashAlgo the hash algorithm, if null the password will be XORed
* @param prefix the prefix of the password attributes, may be null
*/
public static void setPassword(XmlObject xobj, String password, HashAlgorithm hashAlgo, String prefix) {
XmlCursor cur = xobj.newCursor();
if (password == null) {
cur.removeAttribute(getAttrName(prefix, "password"));
cur.removeAttribute(getAttrName(prefix, "algorithmName"));
cur.removeAttribute(getAttrName(prefix, "hashValue"));
cur.removeAttribute(getAttrName(prefix, "saltValue"));
cur.removeAttribute(getAttrName(prefix, "spinCount"));
return;
}
cur.toFirstContentToken();
if (hashAlgo == null) {
int hash = CryptoFunctions.createXorVerifier1(password);
cur.insertAttributeWithValue(getAttrName(prefix, "password"),
Integer.toHexString(hash).toUpperCase(Locale.ROOT));
} else {
SecureRandom random = new SecureRandom();
byte salt[] = random.generateSeed(16);
// Iterations specifies the number of times the hashing function shall be iteratively run (using each
// iteration's result as the input for the next iteration).
int spinCount = 100000;
// Implementation Notes List:
// --> In this third stage, the reversed byte order legacy hash from the second stage shall
// be converted to Unicode hex string representation
byte hash[] = CryptoFunctions.hashPassword(password, hashAlgo, salt, spinCount, false);
cur.insertAttributeWithValue(getAttrName(prefix, "algorithmName"), hashAlgo.jceId);
cur.insertAttributeWithValue(getAttrName(prefix, "hashValue"), DatatypeConverter.printBase64Binary(hash));
cur.insertAttributeWithValue(getAttrName(prefix, "saltValue"), DatatypeConverter.printBase64Binary(salt));
cur.insertAttributeWithValue(getAttrName(prefix, "spinCount"), ""+spinCount);
}
cur.dispose();
}
/**
* Validates the password, i.e.
* calculates the hash of the given password and compares it against the stored hash
*
* @param xobj the xmlbeans object which contains the password attributes
* @param password the password, if null the method will always return false,
* even if there's no password set
* @param prefix the prefix of the password attributes, may be null
*
* @return true, if the hashes match
*/
public static boolean validatePassword(XmlObject xobj, String password, String prefix) {
// TODO: is "velvetSweatshop" the default password?
if (password == null) return false;
XmlCursor cur = xobj.newCursor();
String xorHashVal = cur.getAttributeText(getAttrName(prefix, "password"));
String algoName = cur.getAttributeText(getAttrName(prefix, "algorithmName"));
String hashVal = cur.getAttributeText(getAttrName(prefix, "hashValue"));
String saltVal = cur.getAttributeText(getAttrName(prefix, "saltValue"));
String spinCount = cur.getAttributeText(getAttrName(prefix, "spinCount"));
cur.dispose();
if (xorHashVal != null) {
int hash1 = Integer.parseInt(xorHashVal, 16);
int hash2 = CryptoFunctions.createXorVerifier1(password);
return hash1 == hash2;
} else {
if (hashVal == null || algoName == null || saltVal == null || spinCount == null) {
return false;
}
byte hash1[] = DatatypeConverter.parseBase64Binary(hashVal);
HashAlgorithm hashAlgo = HashAlgorithm.fromString(algoName);
byte salt[] = DatatypeConverter.parseBase64Binary(saltVal);
int spinCnt = Integer.parseInt(spinCount);
byte hash2[] = CryptoFunctions.hashPassword(password, hashAlgo, salt, spinCnt, false);
return Arrays.equals(hash1, hash2);
}
}
private static QName getAttrName(String prefix, String name) {
if (prefix == null || "".equals(prefix)) {
return new QName(name);
} else {
return new QName(prefix+Character.toUpperCase(name.charAt(0))+name.substring(1));
}
}
}
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.apache.poi.xssf.usermodel.helpers;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.util.Internal;
import org.apache.poi.util.Removal;
import org.apache.xmlbeans.XmlObject;
/**
* @deprecated POI 3.15 beta 3. Use {@link XSSFPasswordHelper} instead.
*/
@Internal(since="3.15 beta 3")
@Deprecated
@Removal(version="3.17")
public class XSSFPaswordHelper {
/**
* Sets the XORed or hashed password
*
* @param xobj the xmlbeans object which contains the password attributes
* @param password the password, if null, the password attributes will be removed
* @param hashAlgo the hash algorithm, if null the password will be XORed
* @param prefix the prefix of the password attributes, may be null
*/
public static void setPassword(XmlObject xobj, String password, HashAlgorithm hashAlgo, String prefix) {
XSSFPasswordHelper.setPassword(xobj, password, hashAlgo, prefix);
}
/**
* Validates the password, i.e.
* calculates the hash of the given password and compares it against the stored hash
*
* @param xobj the xmlbeans object which contains the password attributes
* @param password the password, if null the method will always return false,
* even if there's no password set
* @param prefix the prefix of the password attributes, may be null
*
* @return true, if the hashes match
*/
public static boolean validatePassword(XmlObject xobj, String password, String prefix) {
return XSSFPasswordHelper.validatePassword(xobj, password, prefix);
}
}

View File

@ -83,9 +83,7 @@ public final class XSSFRowShifter extends RowShifter {
public void updateNamedRanges(FormulaShifter shifter) {
Workbook wb = sheet.getWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create((XSSFWorkbook) wb);
final int numberOfNames = wb.getNumberOfNames();
for (int i = 0; i < numberOfNames; i++) {
Name name = wb.getNameAt(i);
for (Name name : wb.getAllNames()) {
String formula = name.getRefersToFormula();
int sheetIndex = name.getSheetIndex();
final int rowIndex = -1; //don't care, named ranges are not allowed to include structured references

View File

@ -18,6 +18,7 @@
package org.apache.poi.openxml4j.opc.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@ -44,16 +45,21 @@ public final class TestContentTypeManager {
// Retrieves core properties part
OPCPackage p = OPCPackage.open(filepath, PackageAccess.READ);
PackageRelationshipCollection rels = p.getRelationshipsByType(PackageRelationshipTypes.CORE_PROPERTIES);
PackageRelationship corePropertiesRelationship = rels.getRelationship(0);
PackagePart coreDocument = p.getPart(corePropertiesRelationship);
assertEquals("application/vnd.openxmlformats-package.core-properties+xml", coreDocument.getContentType());
try {
PackageRelationshipCollection rels = p.getRelationshipsByType(PackageRelationshipTypes.CORE_PROPERTIES);
PackageRelationship corePropertiesRelationship = rels.getRelationship(0);
PackagePart coreDocument = p.getPart(corePropertiesRelationship);
// TODO - finish writing this test
assumeTrue("finish writing this test", false);
ContentTypeManager ctm = new ZipContentTypeManager(coreDocument.getInputStream(), p);
assertEquals("application/vnd.openxmlformats-package.core-properties+xml", coreDocument.getContentType());
// TODO - finish writing this test
assumeTrue("finish writing this test", false);
ContentTypeManager ctm = new ZipContentTypeManager(coreDocument.getInputStream(), p);
assertNotNull(ctm);
} finally {
p.close();
}
}
/**

View File

@ -115,25 +115,25 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
assertFalse(wb.isMacroEnabled());
assertEquals(3, wb.getNumberOfNames());
assertEquals(0, wb.getNameAt(0).getCTName().getLocalSheetId());
assertFalse(wb.getNameAt(0).getCTName().isSetLocalSheetId());
assertEquals("SheetA!$A$1", wb.getNameAt(0).getRefersToFormula());
assertEquals("SheetA", wb.getNameAt(0).getSheetName());
assertEquals(0, wb.getName("SheetAA1").getCTName().getLocalSheetId());
assertFalse(wb.getName("SheetAA1").getCTName().isSetLocalSheetId());
assertEquals("SheetA!$A$1", wb.getName("SheetAA1").getRefersToFormula());
assertEquals("SheetA", wb.getName("SheetAA1").getSheetName());
assertEquals(0, wb.getNameAt(1).getCTName().getLocalSheetId());
assertFalse(wb.getNameAt(1).getCTName().isSetLocalSheetId());
assertEquals("SheetB!$A$1", wb.getNameAt(1).getRefersToFormula());
assertEquals("SheetB", wb.getNameAt(1).getSheetName());
assertEquals(0, wb.getName("SheetBA1").getCTName().getLocalSheetId());
assertFalse(wb.getName("SheetBA1").getCTName().isSetLocalSheetId());
assertEquals("SheetB!$A$1", wb.getName("SheetBA1").getRefersToFormula());
assertEquals("SheetB", wb.getName("SheetBA1").getSheetName());
assertEquals(0, wb.getNameAt(2).getCTName().getLocalSheetId());
assertFalse(wb.getNameAt(2).getCTName().isSetLocalSheetId());
assertEquals("SheetC!$A$1", wb.getNameAt(2).getRefersToFormula());
assertEquals("SheetC", wb.getNameAt(2).getSheetName());
assertEquals(0, wb.getName("SheetCA1").getCTName().getLocalSheetId());
assertFalse(wb.getName("SheetCA1").getCTName().isSetLocalSheetId());
assertEquals("SheetC!$A$1", wb.getName("SheetCA1").getRefersToFormula());
assertEquals("SheetC", wb.getName("SheetCA1").getSheetName());
// Save and re-load, still there
XSSFWorkbook nwb = XSSFTestDataSamples.writeOutAndReadBack(wb);
assertEquals(3, nwb.getNumberOfNames());
assertEquals("SheetA!$A$1", nwb.getNameAt(0).getRefersToFormula());
assertEquals("SheetA!$A$1", nwb.getName("SheetAA1").getRefersToFormula());
nwb.close();
wb.close();

View File

@ -154,7 +154,9 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
evaluator.evaluate(cXSL_cell);
fail("Without a fix for #56752, shouldn't be able to evaluate a " +
"reference to a non-provided linked workbook");
} catch(Exception e) {}
} catch(Exception e) {
// expected here
}
// Setup the environment
Map<String,FormulaEvaluator> evaluators = new HashMap<String, FormulaEvaluator>();
@ -171,8 +173,19 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
evaluator.evaluate(c);
}
}
// And evaluate the other way too
evaluator.evaluateAll();
// Evaluate and check results
// Static evaluator won't work, as no references passed in
try {
XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
fail("Static method lacks references, shouldn't work");
} catch(Exception e) {
// expected here
}
// Evaluate specific cells and check results
assertEquals("\"Hello!\"", evaluator.evaluate(cXSLX_cell).formatAsString());
assertEquals("\"Test A1\"", evaluator.evaluate(cXSLX_sNR).formatAsString());
assertEquals("142.0", evaluator.evaluate(cXSLX_gNR).formatAsString());
@ -196,7 +209,9 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
try {
cXSLX_nw_cell.setCellFormula("[alt.xlsx]Sheet1!$A$1");
fail("New workbook not linked, shouldn't be able to add");
} catch (Exception e) {}
} catch (Exception e) {
// expected here
}
// Link and re-try
Workbook alt = new XSSFWorkbook();
@ -651,4 +666,20 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator {
private Cell getCell(Sheet sheet, int rowNo, int column) {
return sheet.getRow(rowNo).getCell(column);
}
@Test
public void test59736() {
Workbook wb = XSSFTestDataSamples.openSampleWorkbook("59736.xlsx");
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
Cell cell = wb.getSheetAt(0).getRow(0).getCell(0);
assertEquals(1, cell.getNumericCellValue(), 0.001);
cell = wb.getSheetAt(0).getRow(1).getCell(0);
CellValue value = evaluator.evaluate(cell);
assertEquals(1, value.getNumberValue(), 0.001);
cell = wb.getSheetAt(0).getRow(2).getCell(0);
value = evaluator.evaluate(cell);
assertEquals(1, value.getNumberValue(), 0.001);
}
}

View File

@ -53,9 +53,8 @@ public final class TestXSSFName extends BaseTestNamedRange {
//sheet.createFreezePane(0, 3);
}
assertEquals(1, wb.getNumberOfNames());
XSSFName nr1 = wb.getNameAt(0);
XSSFName nr1 = wb.getName(XSSFName.BUILTIN_PRINT_TITLE);
assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr1.getNameName());
assertEquals("'First Sheet'!$A:$A,'First Sheet'!$1:$4", nr1.getRefersToFormula());
//remove the columns part
@ -77,9 +76,8 @@ public final class TestXSSFName extends BaseTestNamedRange {
wb.close();
assertEquals(1, nwb.getNumberOfNames());
nr1 = nwb.getNameAt(0);
nr1 = nwb.getName(XSSFName.BUILTIN_PRINT_TITLE);
assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr1.getNameName());
assertEquals("'First Sheet'!$A:$A,'First Sheet'!$1:$4", nr1.getRefersToFormula());
// check that setting RR&C on a second sheet causes a new Print_Titles built-in
@ -89,7 +87,7 @@ public final class TestXSSFName extends BaseTestNamedRange {
sheet2.setRepeatingColumns(CellRangeAddress.valueOf("B:C"));
assertEquals(2, nwb.getNumberOfNames());
XSSFName nr2 = nwb.getNameAt(1);
XSSFName nr2 = nwb.getNames(XSSFName.BUILTIN_PRINT_TITLE).get(1);
assertEquals(XSSFName.BUILTIN_PRINT_TITLE, nr2.getNameName());
assertEquals("SecondSheet!$B:$C,SecondSheet!$1:$1", nr2.getRefersToFormula());
@ -98,4 +96,38 @@ public final class TestXSSFName extends BaseTestNamedRange {
sheet2.setRepeatingColumns(null);
nwb.close();
}
@Test
public void testSetNameName() throws Exception {
// Test that renaming named ranges doesn't break our new named range map
XSSFWorkbook wb = new XSSFWorkbook();
wb.createSheet("First Sheet");
// Two named ranges called "name1", one scoped to sheet1 and one globally
XSSFName nameSheet1 = wb.createName();
nameSheet1.setNameName("name1");
nameSheet1.setRefersToFormula("'First Sheet'!$A$1");
nameSheet1.setSheetIndex(0);
XSSFName nameGlobal = wb.createName();
nameGlobal.setNameName("name1");
nameGlobal.setRefersToFormula("'First Sheet'!$B$1");
// Rename sheet-scoped name to "name2", check everything is updated properly
// and that the other name is unaffected
nameSheet1.setNameName("name2");
assertEquals(1, wb.getNames("name1").size());
assertEquals(1, wb.getNames("name2").size());
assertEquals(nameGlobal, wb.getName("name1"));
assertEquals(nameSheet1, wb.getName("name2"));
// Rename the other name to "name" and check everything again
nameGlobal.setNameName("name2");
assertEquals(0, wb.getNames("name1").size());
assertEquals(2, wb.getNames("name2").size());
assertTrue(wb.getNames("name2").contains(nameGlobal));
assertTrue(wb.getNames("name2").contains(nameSheet1));
wb.close();
}
}

View File

@ -80,6 +80,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCalcMode;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STUnsignedShortHex;
public final class TestXSSFSheet extends BaseTestXSheet {
@ -1099,6 +1100,30 @@ public final class TestXSSFSheet extends BaseTestXSheet {
wb.close();
}
@Test
public void protectSheet_emptyPassword() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet();
CTSheetProtection pr = sheet.getCTWorksheet().getSheetProtection();
assertNull("CTSheetProtection should be null by default", pr);
String password = "";
sheet.protectSheet(password);
pr = sheet.getCTWorksheet().getSheetProtection();
assertNotNull("CTSheetProtection should be not null", pr);
assertTrue("sheet protection should be on", pr.isSetSheet());
assertTrue("object protection should be on", pr.isSetObjects());
assertTrue("scenario protection should be on", pr.isSetScenarios());
int hashVal = CryptoFunctions.createXorVerifier1(password);
STUnsignedShortHex xpassword = pr.xgetPassword();
int actualVal = Integer.parseInt(xpassword.getStringValue(),16);
assertEquals("well known value for top secret hash should match", hashVal, actualVal);
sheet.protectSheet(null);
assertNull("protectSheet(null) should unset CTSheetProtection", sheet.getCTWorksheet().getSheetProtection());
wb.close();
}
@Test
public void protectSheet_lowlevel_2013() throws IOException {
String password = "test";

View File

@ -1140,4 +1140,44 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
wb.close();
}
@Test
public void testRemoveSheet() throws IOException {
// Test removing a sheet maintains the named ranges correctly
XSSFWorkbook wb = new XSSFWorkbook();
wb.createSheet("Sheet1");
wb.createSheet("Sheet2");
XSSFName sheet1Name = wb.createName();
sheet1Name.setNameName("name1");
sheet1Name.setSheetIndex(0);
sheet1Name.setRefersToFormula("Sheet1!$A$1");
XSSFName sheet2Name = wb.createName();
sheet2Name.setNameName("name1");
sheet2Name.setSheetIndex(1);
sheet2Name.setRefersToFormula("Sheet2!$A$1");
assertTrue(wb.getAllNames().contains(sheet1Name));
assertTrue(wb.getAllNames().contains(sheet2Name));
assertEquals(2, wb.getNames("name1").size());
assertEquals(sheet1Name, wb.getNames("name1").get(0));
assertEquals(sheet2Name, wb.getNames("name1").get(1));
// Remove sheet1, we should only have sheet2Name now
wb.removeSheetAt(0);
assertFalse(wb.getAllNames().contains(sheet1Name));
assertTrue(wb.getAllNames().contains(sheet2Name));
assertEquals(1, wb.getNames("name1").size());
assertEquals(sheet2Name, wb.getNames("name1").get(0));
// Check by index as well for sanity
assertEquals(1, wb.getNumberOfNames());
assertEquals(0, wb.getNameIndex("name1"));
assertEquals(sheet2Name, wb.getNameAt(0));
wb.close();
}
}

View File

@ -571,20 +571,39 @@ public final class HWPFDocument extends HWPFDocumentCore {
return _fields;
}
/**
* Warning - not currently implemented for HWPF!
*/
@Override
public void write() throws IOException {
// TODO Implement
throw new IllegalStateException("Coming soon!");
}
/**
* Writes out the word file that is represented by an instance of this class.
*
* If the {@link File} exists, it will be replaced, otherwise a new one
* will be created
*
* @param newFile The File to write to.
* @throws IOException If there is an unexpected IOException from writing
* to the File.
*
* @since 3.15 beta 3
*/
@Override
public void write(File newFile) throws IOException {
throw new IllegalStateException("Coming soon!");
NPOIFSFileSystem pfs = POIFSFileSystem.create(newFile);
write(pfs, true);
pfs.writeFilesystem();
}
/**
* Writes out the word file that is represented by an instance of this class.
*
* If {@code stream} is a {@link java.io.FileOutputStream} on a networked drive
* or has a high cost/latency associated with each written byte,
* For better performance when writing to files, use {@link #write(File)}.
* If {@code stream} has a high cost/latency associated with each written byte,
* consider wrapping the OutputStream in a {@link java.io.BufferedOutputStream}
* to improve write performance.
*
@ -592,9 +611,12 @@ public final class HWPFDocument extends HWPFDocumentCore {
* @throws IOException If there is an unexpected IOException from the passed
* in OutputStream.
*/
public void write(OutputStream out)
throws IOException
{
public void write(OutputStream out) throws IOException {
NPOIFSFileSystem pfs = new NPOIFSFileSystem();
write(pfs, true);
pfs.writeFilesystem( out );
}
private void write(NPOIFSFileSystem pfs, boolean copyOtherEntries) throws IOException {
// initialize our streams for writing.
HWPFFileSystem docSys = new HWPFFileSystem();
HWPFOutputStream wordDocumentStream = docSys.getStream(STREAM_WORD_DOCUMENT);
@ -891,7 +913,8 @@ public final class HWPFDocument extends HWPFDocumentCore {
}
// create new document preserving order of entries
NPOIFSFileSystem pfs = new NPOIFSFileSystem();
// TODO Check "copyOtherEntries" and tweak behaviour based on that
// TODO That's needed for in-place write
boolean docWritten = false;
boolean dataWritten = false;
boolean objectPoolWritten = false;
@ -967,7 +990,6 @@ public final class HWPFDocument extends HWPFDocumentCore {
if ( !objectPoolWritten )
_objectPool.writeTo( pfs.getRoot() );
pfs.writeFilesystem( out );
this.directory = pfs.getRoot();
/*

View File

@ -0,0 +1,81 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hwpf.usermodel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFTestCase;
import org.apache.poi.hwpf.HWPFTestDataSamples;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.TempFile;
/**
* Test various write situations
*/
public final class TestHWPFWrite extends HWPFTestCase {
/**
* Write to a stream
*/
public void testWriteStream() throws Exception {
HWPFDocument doc = HWPFTestDataSamples.openSampleFile("SampleDoc.doc");
Range r = doc.getRange();
assertEquals("I am a test document\r", r.getParagraph(0).text());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.write(baos);
doc.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
doc = new HWPFDocument(bais);
r = doc.getRange();
assertEquals("I am a test document\r", r.getParagraph(0).text());
doc.close();
}
/**
* Write to a new file
*/
public void testWriteNewFile() throws Exception {
HWPFDocument doc = HWPFTestDataSamples.openSampleFile("SampleDoc.doc");
Range r = doc.getRange();
assertEquals("I am a test document\r", r.getParagraph(0).text());
File file = TempFile.createTempFile("TestDocument", ".doc");
doc.write(file);
doc.close();
// Check reading from File and Stream
doc = new HWPFDocument(new FileInputStream(file));
r = doc.getRange();
assertEquals("I am a test document\r", r.getParagraph(0).text());
doc.close();
doc = new HWPFDocument(new POIFSFileSystem(file));
r = doc.getRange();
assertEquals("I am a test document\r", r.getParagraph(0).text());
doc.close();
}
// TODO In-place write positive and negative checks
}

View File

@ -50,14 +50,20 @@ public abstract class BaseTestSlideShow {
@Test
public void addPicture_Stream() throws IOException {
SlideShow<?,?> show = createSlideShow();
InputStream stream = slTests.openResourceAsStream("clock.jpg");
assertEquals(0, show.getPictureData().size());
PictureData picture = show.addPicture(stream, PictureType.JPEG);
assertEquals(1, show.getPictureData().size());
assertSame(picture, show.getPictureData().get(0));
show.close();
try {
InputStream stream = slTests.openResourceAsStream("clock.jpg");
try {
assertEquals(0, show.getPictureData().size());
PictureData picture = show.addPicture(stream, PictureType.JPEG);
assertEquals(1, show.getPictureData().size());
assertSame(picture, show.getPictureData().get(0));
} finally {
stream.close();
}
} finally {
show.close();
}
}
@Test

View File

@ -27,6 +27,7 @@ import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
@ -105,6 +106,7 @@ public final class TestIndirect {
// non-error cases
confirm(feA, c, "INDIRECT(\"C2\")", 23);
confirm(feA, c, "INDIRECT(\"C2\", TRUE)", 23);
confirm(feA, c, "INDIRECT(\"$C2\")", 23);
confirm(feA, c, "INDIRECT(\"C$2\")", 23);
confirm(feA, c, "SUM(INDIRECT(\"Sheet2!B1:C3\"))", 351); // area ref
@ -149,7 +151,7 @@ public final class TestIndirect {
// confirm(feA, c, "INDIRECT(\"Sheet1!A65537\")", ErrorEval.REF_INVALID); // bad row
// }
confirm(feA, c, "INDIRECT(\"Sheet1!A 1\")", ErrorEval.REF_INVALID); // space in cell ref
wbA.close();
}
@ -203,4 +205,9 @@ public final class TestIndirect {
+ "' but got '" + cv.formatAsString() + "'.");
}
}
@Test
public void testInvalidInput() {
assertEquals(ErrorEval.VALUE_INVALID, Indirect.instance.evaluate(new ValueEval[] {}, null));
}
}

View File

@ -20,9 +20,8 @@ package org.apache.poi.ss.formula.functions;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.eval.AreaEval;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.formula.eval.*;
import junit.framework.TestCase;
import org.apache.poi.ss.usermodel.*;
@ -75,7 +74,6 @@ public final class TestSubtotal extends TestCase {
}
public void testAvg(){
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
@ -95,16 +93,18 @@ public final class TestSubtotal extends TestCase {
a6.setCellFormula("SUBTOTAL(1,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(1,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(1,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(2.0, a3.getNumericCellValue());
assertEquals(8.0, a6.getNumericCellValue());
assertEquals(3.0, a7.getNumericCellValue());
assertEquals(3.0, a8.getNumericCellValue());
}
public void testSum(){
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
@ -124,12 +124,15 @@ public final class TestSubtotal extends TestCase {
a6.setCellFormula("SUBTOTAL(9,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(9,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(9,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(4.0, a3.getNumericCellValue());
assertEquals(26.0, a6.getNumericCellValue());
assertEquals(12.0, a7.getNumericCellValue());
assertEquals(12.0, a8.getNumericCellValue());
}
public void testCount(){
@ -147,18 +150,21 @@ public final class TestSubtotal extends TestCase {
a3.setCellFormula("SUBTOTAL(2,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue("POI"); // A4 is string and not counted
Cell a5 = sh.createRow(5).createCell(1); // A5 is blank and not counted
/*Cell a5 =*/ sh.createRow(5).createCell(1); // A5 is blank and not counted
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(2,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(2,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(2,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(2.0, a3.getNumericCellValue());
assertEquals(6.0, a6.getNumericCellValue());
assertEquals(2.0, a7.getNumericCellValue());
assertEquals(2.0, a8.getNumericCellValue());
}
public void testCounta(){
@ -176,18 +182,21 @@ public final class TestSubtotal extends TestCase {
a3.setCellFormula("SUBTOTAL(3,B2:B3)");
Cell a4 = sh.createRow(4).createCell(1);
a4.setCellValue("POI"); // A4 is string and not counted
Cell a5 = sh.createRow(5).createCell(1); // A5 is blank and not counted
/*Cell a5 =*/ sh.createRow(5).createCell(1); // A5 is blank and not counted
Cell a6 = sh.createRow(6).createCell(1);
a6.setCellFormula("SUBTOTAL(3,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(3,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(3,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(2.0, a3.getNumericCellValue());
assertEquals(8.0, a6.getNumericCellValue());
assertEquals(3.0, a7.getNumericCellValue());
assertEquals(3.0, a8.getNumericCellValue());
}
public void testMax(){
@ -211,12 +220,15 @@ public final class TestSubtotal extends TestCase {
a6.setCellFormula("SUBTOTAL(4,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(4,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(4,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(3.0, a3.getNumericCellValue());
assertEquals(16.0, a6.getNumericCellValue());
assertEquals(7.0, a7.getNumericCellValue());
assertEquals(7.0, a8.getNumericCellValue());
}
public void testMin(){
@ -240,12 +252,15 @@ public final class TestSubtotal extends TestCase {
a6.setCellFormula("SUBTOTAL(5,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(5,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(5,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(1.0, a3.getNumericCellValue());
assertEquals(4.0, a6.getNumericCellValue());
assertEquals(1.0, a7.getNumericCellValue());
assertEquals(1.0, a8.getNumericCellValue());
}
public void testStdev(){
@ -269,12 +284,15 @@ public final class TestSubtotal extends TestCase {
a6.setCellFormula("SUBTOTAL(7,B2:B6)*2 + 2");
Cell a7 = sh.createRow(7).createCell(1);
a7.setCellFormula("SUBTOTAL(7,B2:B7)");
Cell a8 = sh.createRow(8).createCell(1);
a8.setCellFormula("SUBTOTAL(7,B2,B3,B4,B5,B6,B7,B8)");
fe.evaluateAll();
assertEquals(1.41421, a3.getNumericCellValue(), 0.0001);
assertEquals(7.65685, a6.getNumericCellValue(), 0.0001);
assertEquals(2.82842, a7.getNumericCellValue(), 0.0001);
assertEquals(2.82842, a8.getNumericCellValue(), 0.0001);
}
public void test50209(){
@ -328,4 +346,69 @@ public final class TestSubtotal extends TestCase {
confirmExpectedResult(evaluator, "SUBTOTAL(COUNT;B2:B8,C2:C8)", cellC2, 3.0);
confirmExpectedResult(evaluator, "SUBTOTAL(COUNTA;B2:B8,C2:C8)", cellC3, 5.0);
}
public void testUnimplemented(){
Workbook wb = new HSSFWorkbook();
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.createSheet();
Cell a3 = sh.createRow(3).createCell(1);
a3.setCellFormula("SUBTOTAL(8,B2:B3)");
try {
fe.evaluateAll();
fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented");
} catch (NotImplementedException e) {
// expected here
}
a3.setCellFormula("SUBTOTAL(10,B2:B3)");
try {
fe.evaluateAll();
fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented");
} catch (NotImplementedException e) {
// expected here
}
a3.setCellFormula("SUBTOTAL(11,B2:B3)");
try {
fe.evaluateAll();
fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented");
} catch (NotImplementedException e) {
// expected here
}
a3.setCellFormula("SUBTOTAL(107,B2:B3)");
try {
fe.evaluateAll();
fail("Should catch an NotImplementedFunctionException here, adjust these tests if it was actually implemented");
} catch (NotImplementedException e) {
// expected here
}
a3.setCellFormula("SUBTOTAL(0,B2:B3)");
fe.evaluateAll();
assertEquals(FormulaError.VALUE.getCode(), a3.getErrorCellValue());
try {
a3.setCellFormula("SUBTOTAL(9)");
fail("Should catch an exception here");
} catch (FormulaParseException e) {
// expected here
}
try {
a3.setCellFormula("SUBTOTAL()");
fail("Should catch an exception here");
} catch (FormulaParseException e) {
// expected here
}
Subtotal subtotal = new Subtotal();
assertEquals(ErrorEval.VALUE_INVALID, subtotal.evaluate(new ValueEval[] {}, 0, 0));
}
}

View File

@ -0,0 +1,66 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.formula.functions;
import org.apache.poi.ss.formula.eval.*;
import org.junit.Test;
import static org.junit.Assert.*;
public class TestWeekdayFunc {
@Test
public void testEvaluate() throws Exception {
assertEquals(2.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(2.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(1.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(1.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(2.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(0.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(3.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(1.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(11.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(7.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(12.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(6.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(13.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(5.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(14.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(4.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(15.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(3.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(16.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(2.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(17.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(3.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(3.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(1.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(2.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(2.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(1.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(3.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(2.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(11.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(1.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(12.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(7.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(13.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(6.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(14.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(5.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(15.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(4.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(16.0)}, 0, 0)).getNumberValue(), 0.001);
assertEquals(3.0, ((NumberEval)WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(39448.0), new NumberEval(17.0)}, 0, 0)).getNumberValue(), 0.001);
}
@Test
public void testEvaluateInvalid() throws Exception {
assertEquals(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.evaluate(new ValueEval[]{}, 0, 0));
assertEquals(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(1.0), new NumberEval(1.0)}, 0, 0));
assertEquals(ErrorEval.NUM_ERROR, WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(-1.0)}, 0, 0));
assertEquals(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.evaluate(new ValueEval[]{new StringEval("")}, 0, 0));
assertEquals(ErrorEval.VALUE_INVALID, WeekdayFunc.instance.evaluate(new ValueEval[]{new StringEval("1"), new StringEval("")}, 0, 0));
assertEquals(ErrorEval.NUM_ERROR, WeekdayFunc.instance.evaluate(new ValueEval[]{new StringEval("2"), BlankEval.instance}, 0, 0));
assertEquals(ErrorEval.NUM_ERROR, WeekdayFunc.instance.evaluate(new ValueEval[]{new StringEval("3"), MissingArgEval.instance}, 0, 0));
assertEquals(ErrorEval.NUM_ERROR, WeekdayFunc.instance.evaluate(new ValueEval[]{new NumberEval(1.0), new NumberEval(18.0)}, 0, 0));
}
}

View File

@ -33,9 +33,12 @@ import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
@ -1606,4 +1609,78 @@ public abstract class BaseTestBugzillaIssues {
assertNull("Sheet0 after write", wb2.getPrintArea(0)); // CURRENTLY FAILS with "Sheet0!$A$1:$C$6"
assertEquals("Sheet1 after write", "Sheet1!$A$1:$A$1", wb2.getPrintArea(1));
}
}
@Test
public void test55384() throws Exception {
Workbook wb = _testDataProvider.createWorkbook();
try {
Sheet sh = wb.createSheet();
for (int rownum = 0; rownum < 10; rownum++) {
org.apache.poi.ss.usermodel.Row row = sh.createRow(rownum);
for (int cellnum = 0; cellnum < 3; cellnum++) {
Cell cell = row.createCell(cellnum);
cell.setCellValue(rownum + cellnum);
}
}
Row row = sh.createRow(10);
// setting no precalculated value works just fine.
Cell cell1 = row.createCell(0);
cell1.setCellFormula("SUM(A1:A10)");
// but setting a precalculated STRING value fails totally in SXSSF
Cell cell2 = row.createCell(1);
cell2.setCellFormula("SUM(B1:B10)");
cell2.setCellValue("55");
// setting a precalculated int value works as expected
Cell cell3 = row.createCell(2);
cell3.setCellFormula("SUM(C1:C10)");
cell3.setCellValue(65);
assertEquals(CellType.FORMULA, cell1.getCellTypeEnum());
assertEquals(CellType.FORMULA, cell2.getCellTypeEnum());
assertEquals(CellType.FORMULA, cell3.getCellTypeEnum());
assertEquals("SUM(A1:A10)", cell1.getCellFormula());
assertEquals("SUM(B1:B10)", cell2.getCellFormula());
assertEquals("SUM(C1:C10)", cell3.getCellFormula());
/*String name = wb.getClass().getCanonicalName();
String ext = (wb instanceof HSSFWorkbook) ? ".xls" : ".xlsx";
OutputStream output = new FileOutputStream("/tmp" + name + ext);
try {
wb.write(output);
} finally {
output.close();
}*/
Workbook wbBack = _testDataProvider.writeOutAndReadBack(wb);
checkFormulaPreevaluatedString(wbBack);
wbBack.close();
} finally {
wb.close();
}
}
private void checkFormulaPreevaluatedString(Workbook readFile) {
Sheet sheet = readFile.getSheetAt(0);
Row row = sheet.getRow(sheet.getLastRowNum());
assertEquals(10, row.getRowNum());
for (Cell cell : row) {
String cellValue = null;
switch (cell.getCellTypeEnum()) {
case STRING:
cellValue = cell.getRichStringCellValue().getString();
break;
case FORMULA:
cellValue = cell.getCellFormula();
break;
}
assertNotNull(cellValue);
cellValue = cellValue.isEmpty() ? null : cellValue;
assertNotNull(cellValue);
}
}
}

View File

@ -201,11 +201,7 @@ public abstract class BaseTestNamedRange {
assertEquals("The sheet already contains this name: aaa", e.getMessage());
}
int cnt = 0;
for (int i = 0; i < wb.getNumberOfNames(); i++) {
if("aaa".equals(wb.getNameAt(i).getNameName())) cnt++;
}
assertEquals(3, cnt);
assertEquals(3, wb.getNames("aaa").size());
wb.close();
}
@ -250,11 +246,11 @@ public abstract class BaseTestNamedRange {
// Write the workbook to a file
// Read the Excel file and verify its content
Workbook wb2 = _testDataProvider.writeOutAndReadBack(wb1);
Name nm1 = wb2.getNameAt(wb2.getNameIndex("RangeTest1"));
Name nm1 = wb2.getName("RangeTest1");
assertTrue("Name is "+nm1.getNameName(),"RangeTest1".equals(nm1.getNameName()));
assertTrue("Reference is "+nm1.getRefersToFormula(),(wb2.getSheetName(0)+"!$A$1:$L$41").equals(nm1.getRefersToFormula()));
Name nm2 = wb2.getNameAt(wb2.getNameIndex("RangeTest2"));
Name nm2 = wb2.getName("RangeTest2");
assertTrue("Name is "+nm2.getNameName(),"RangeTest2".equals(nm2.getNameName()));
assertTrue("Reference is "+nm2.getRefersToFormula(),(wb2.getSheetName(1)+"!$A$1:$O$21").equals(nm2.getRefersToFormula()));
@ -466,11 +462,11 @@ public abstract class BaseTestNamedRange {
wb1.getNameAt(0);
Workbook wb2 = _testDataProvider.writeOutAndReadBack(wb1);
Name nm =wb2.getNameAt(wb2.getNameIndex("RangeTest"));
Name nm =wb2.getName("RangeTest");
assertTrue("Name is "+nm.getNameName(),"RangeTest".equals(nm.getNameName()));
assertTrue("Reference is "+nm.getRefersToFormula(),(wb2.getSheetName(0)+"!$D$4:$E$8").equals(nm.getRefersToFormula()));
nm = wb2.getNameAt(wb2.getNameIndex("AnotherTest"));
nm = wb2.getName("AnotherTest");
assertTrue("Name is "+nm.getNameName(),"AnotherTest".equals(nm.getNameName()));
assertTrue("Reference is "+nm.getRefersToFormula(),newNamedRange2.getRefersToFormula().equals(nm.getRefersToFormula()));
@ -499,8 +495,7 @@ public abstract class BaseTestNamedRange {
namedCell.setRefersToFormula(reference);
// retrieve the newly created named range
int namedCellIdx = wb.getNameIndex(cellName);
Name aNamedCell = wb.getNameAt(namedCellIdx);
Name aNamedCell = wb.getName(cellName);
assertNotNull(aNamedCell);
// retrieve the cell at the named range and test its contents
@ -540,8 +535,7 @@ public abstract class BaseTestNamedRange {
namedCell.setRefersToFormula(reference);
// retrieve the newly created named range
int namedCellIdx = wb.getNameIndex(cname);
Name aNamedCell = wb.getNameAt(namedCellIdx);
Name aNamedCell = wb.getName(cname);
assertNotNull(aNamedCell);
// retrieve the cell at the named range and test its contents

View File

@ -261,17 +261,17 @@ public abstract class BaseTestSheetShiftRows {
name4.setSheetIndex(1);
sheet1.shiftRows(0, 1, 2); //shift down the top row on Sheet1.
name1 = wb.getNameAt(0);
name1 = wb.getName("name1");
assertEquals("Sheet1!$A$3+Sheet1!$B$3", name1.getRefersToFormula());
name2 = wb.getNameAt(1);
name2 = wb.getName("name2");
assertEquals("Sheet1!$A$3", name2.getRefersToFormula());
//name3 and name4 refer to Sheet2 and should not be affected
name3 = wb.getNameAt(2);
name3 = wb.getName("name3");
assertEquals("Sheet2!$A$1", name3.getRefersToFormula());
name4 = wb.getNameAt(3);
name4 = wb.getName("name4");
assertEquals("A1", name4.getRefersToFormula());
wb.close();

View File

@ -78,14 +78,16 @@ public class BaseTestCellUtil {
@Test(expected=RuntimeException.class)
public void setCellStylePropertyWithInvalidValue() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet s = wb.createSheet();
Row r = s.createRow(0);
Cell c = r.createCell(0);
try {
Sheet s = wb.createSheet();
Row r = s.createRow(0);
Cell c = r.createCell(0);
// An invalid BorderStyle constant
CellUtil.setCellStyleProperty(c, CellUtil.BORDER_BOTTOM, 42);
wb.close();
// An invalid BorderStyle constant
CellUtil.setCellStyleProperty(c, CellUtil.BORDER_BOTTOM, 42);
} finally {
wb.close();
}
}
@Test()
@ -352,10 +354,8 @@ public class BaseTestCellUtil {
CellUtil.setFont(A1, font2);
fail("setFont not allowed if font belongs to a different workbook");
} catch (final IllegalArgumentException e) {
if (e.getMessage().startsWith("Font does not belong to this workbook")) {
// expected
}
else {
// one specific message is expected
if (!e.getMessage().startsWith("Font does not belong to this workbook")) {
throw e;
}
} finally {
@ -371,7 +371,7 @@ public class BaseTestCellUtil {
*/
// bug 55555
@Test
public void setFillForegroundColorBeforeFillBackgroundColor() {
public void setFillForegroundColorBeforeFillBackgroundColor() throws IOException {
Workbook wb1 = _testDataProvider.createWorkbook();
Cell A1 = wb1.createSheet().createRow(0).createCell(0);
Map<String, Object> properties = new HashMap<String, Object>();
@ -386,13 +386,14 @@ public class BaseTestCellUtil {
assertEquals("fill pattern", CellStyle.BRICKS, style.getFillPattern());
assertEquals("fill foreground color", IndexedColors.BLUE, IndexedColors.fromInt(style.getFillForegroundColor()));
assertEquals("fill background color", IndexedColors.RED, IndexedColors.fromInt(style.getFillBackgroundColor()));
wb1.close();
}
/**
* bug 55555
* @since POI 3.15 beta 3
*/
@Test
public void setFillForegroundColorBeforeFillBackgroundColorEnum() {
public void setFillForegroundColorBeforeFillBackgroundColorEnum() throws IOException {
Workbook wb1 = _testDataProvider.createWorkbook();
Cell A1 = wb1.createSheet().createRow(0).createCell(0);
Map<String, Object> properties = new HashMap<String, Object>();
@ -407,5 +408,7 @@ public class BaseTestCellUtil {
assertEquals("fill pattern", FillPatternType.BRICKS, style.getFillPatternEnum());
assertEquals("fill foreground color", IndexedColors.BLUE, IndexedColors.fromInt(style.getFillForegroundColor()));
assertEquals("fill background color", IndexedColors.RED, IndexedColors.fromInt(style.getFillBackgroundColor()));
wb1.close();
}
}

View File

@ -30,16 +30,20 @@ public class DummyPOILogger extends POILogger {
logged = new ArrayList<String>();
}
@Override
public boolean check(int level) {
return true;
}
@Override
public void initialize(String cat) {}
@Override
public void log(int level, Object obj1) {
logged.add(level + " - " + obj1);
}
@Override
public void log(int level, Object obj1, Throwable exception) {
logged.add(level + " - " + obj1 + " - " + exception);
}

View File

@ -26,10 +26,6 @@ import org.junit.Test;
/**
* Tests the log class.
*
* @author Glen Stampoultzis (glens at apache.org)
* @author Marc Johnson (mjohnson at apache dot org)
* @author Nicola Ken Barozzi (nicolaken at apache.org)
*/
public final class TestPOILogger extends POILogger {
private String lastLog = "";
@ -61,20 +57,26 @@ public final class TestPOILogger extends POILogger {
POILogFactory._loggerClassName = oldLCN;
}
}
// ---------- POI Logger methods implemented for testing ----------
@Override
public void initialize(String cat) {
}
@Override
public void log(int level, Object obj1) {
lastLog = (obj1 == null) ? "" : obj1.toString();
lastEx = null;
}
@Override
public void log(int level, Object obj1, Throwable exception) {
lastLog = (obj1 == null) ? "" : obj1.toString();
lastEx = exception;
}
@Override
public boolean check(int level) {
return true;
}

Binary file not shown.