Bugzilla 47363 - Fixed HSSFSheet to allow addition of data validations after sheet protection

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@784240 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2009-06-12 19:17:59 +00:00
parent f14b64759d
commit 635c570598
8 changed files with 502 additions and 258 deletions

View File

@ -35,6 +35,7 @@
<release version="3.5-beta7" date="2009-??-??">
</release>
<release version="3.5-beta6" date="2009-06-22">
<action dev="POI-DEVELOPERS" type="fix">47363 - Fixed HSSFSheet to allow addition of data validations after sheet protection</action>
<action dev="POI-DEVELOPERS" type="fix">47294 - Fixed XSSFWorkbook#setRepeatingRowsAndColumns to tolerate sheet names with quotes</action>
<action dev="POI-DEVELOPERS" type="fix">47309 - Fixed logic in HSSFCell.getCellComment to handle sheets with more than 65536 comments</action>
<action dev="POI-DEVELOPERS" type="fix">46776 - Added clone() method to MulBlankRecord to fix crash in Sheet.cloneSheet()</action>

View File

@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.CalcCountRecord;
import org.apache.poi.hssf.record.CalcModeRecord;
import org.apache.poi.hssf.record.DVALRecord;
import org.apache.poi.hssf.record.DateWindow1904Record;
import org.apache.poi.hssf.record.DefaultColWidthRecord;
import org.apache.poi.hssf.record.DefaultRowHeightRecord;
import org.apache.poi.hssf.record.DeltaRecord;
import org.apache.poi.hssf.record.DimensionsRecord;
@ -62,10 +63,12 @@ import org.apache.poi.hssf.record.UncalcedRecord;
import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.WindowOneRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.aggregates.MergedCellsTable;
import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.record.pivottable.ViewDefinitionRecord;
/**
@ -84,7 +87,6 @@ final class RecordOrderer {
}
/**
* Adds the specified new record in the correct place in sheet records list
*
*/
public static void addNewSheetRecord(List<RecordBase> sheetRecords, RecordBase newRecord) {
int index = findSheetInsertPos(sheetRecords, newRecord.getClass());
@ -107,9 +109,69 @@ final class RecordOrderer {
if (recClass == PageSettingsBlock.class) {
return getPageBreakRecordInsertPos(records);
}
if (recClass == WorksheetProtectionBlock.class) {
return getWorksheetProtectionBlockInsertPos(records);
}
throw new RuntimeException("Unexpected record class (" + recClass.getName() + ")");
}
/**
* Finds the index where the protection block should be inserted
* @param records the records for this sheet
* <pre>
* + BOF
* o INDEX
* o Calculation Settings Block
* o PRINTHEADERS
* o PRINTGRIDLINES
* o GRIDSET
* o GUTS
* o DEFAULTROWHEIGHT
* o SHEETPR
* o Page Settings Block
* o Worksheet Protection Block
* o DEFCOLWIDTH
* oo COLINFO
* o SORT
* + DIMENSION
* </pre>
*/
private static int getWorksheetProtectionBlockInsertPos(List<RecordBase> records) {
int i = getDimensionsIndex(records);
while (i > 0) {
i--;
Object rb = records.get(i);
if (!isProtectionSubsequentRecord(rb)) {
return i+1;
}
}
throw new IllegalStateException("did not find insert pos for protection block");
}
/**
* These records may occur between the 'Worksheet Protection Block' and DIMENSION:
* <pre>
* o DEFCOLWIDTH
* oo COLINFO
* o SORT
* </pre>
*/
private static boolean isProtectionSubsequentRecord(Object rb) {
if (rb instanceof ColumnInfoRecordsAggregate) {
return true; // oo COLINFO
}
if (rb instanceof Record) {
Record record = (Record) rb;
switch (record.getSid()) {
case DefaultColWidthRecord.sid:
case UnknownRecord.SORT_0090:
return true;
}
}
return false;
}
private static int getPageBreakRecordInsertPos(List<RecordBase> records) {
int dimensionsIndex = getDimensionsIndex(records);
int i = dimensionsIndex-1;

View File

@ -42,19 +42,15 @@ import org.apache.poi.hssf.record.IterationRecord;
import org.apache.poi.hssf.record.MergeCellsRecord;
import org.apache.poi.hssf.record.NoteRecord;
import org.apache.poi.hssf.record.ObjRecord;
import org.apache.poi.hssf.record.ObjectProtectRecord;
import org.apache.poi.hssf.record.PaneRecord;
import org.apache.poi.hssf.record.PasswordRecord;
import org.apache.poi.hssf.record.PrintGridlinesRecord;
import org.apache.poi.hssf.record.PrintHeadersRecord;
import org.apache.poi.hssf.record.ProtectRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordBase;
import org.apache.poi.hssf.record.RefModeRecord;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SCLRecord;
import org.apache.poi.hssf.record.SaveRecalcRecord;
import org.apache.poi.hssf.record.ScenarioProtectRecord;
import org.apache.poi.hssf.record.SelectionRecord;
import org.apache.poi.hssf.record.UncalcedRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
@ -68,6 +64,7 @@ import org.apache.poi.hssf.record.aggregates.MergedCellsTable;
import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
import org.apache.poi.hssf.record.aggregates.RecordAggregate;
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.PositionTrackingVisitor;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
import org.apache.poi.hssf.record.formula.FormulaShifter;
@ -113,11 +110,11 @@ public final class Sheet implements Model {
protected DefaultRowHeightRecord defaultrowheight = null;
private PageSettingsBlock _psBlock;
// 'Worksheet Protection Block'
protected ProtectRecord protect = null;
protected ObjectProtectRecord objprotect = null;
protected ScenarioProtectRecord scenprotect = null;
protected PasswordRecord _password = null;
/**
* 'Worksheet Protection Block'<br/>
* Aggregate object is always present, but possibly empty.
*/
private final WorksheetProtectionBlock _protectionBlock = new WorksheetProtectionBlock();
protected WindowTwoRecord windowTwo = null;
protected SelectionRecord _selection = null;
@ -227,6 +224,11 @@ public final class Sheet implements Model {
continue;
}
if (WorksheetProtectionBlock.isComponentRecord(recSid)) {
_protectionBlock.addRecords(rs);
continue;
}
if (recSid == MergeCellsRecord.sid) {
// when the MergedCellsTable is found in the right place, we expect those records to be contiguous
_mergedCellsTable.read(rs);
@ -299,22 +301,6 @@ public final class Sheet implements Model {
{
windowTwo = (WindowTwoRecord) rec;
}
else if ( recSid == ProtectRecord.sid )
{
protect = (ProtectRecord) rec;
}
else if ( recSid == ObjectProtectRecord.sid )
{
objprotect = (ObjectProtectRecord) rec;
}
else if ( recSid == ScenarioProtectRecord.sid )
{
scenprotect = (ScenarioProtectRecord) rec;
}
else if ( recSid == PasswordRecord.sid )
{
_password = (PasswordRecord) rec;
}
else if ( recSid == GutsRecord.sid )
{
_gutsRecord = (GutsRecord) rec;
@ -348,6 +334,7 @@ public final class Sheet implements Model {
_rowsAggregate = rra;
// put merged cells table in the right place (regardless of where the first MergedCellsRecord was found */
RecordOrderer.addNewSheetRecord(records, _mergedCellsTable);
RecordOrderer.addNewSheetRecord(records, _protectionBlock);
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
}
@ -432,7 +419,7 @@ public final class Sheet implements Model {
records.add(_psBlock);
// 'Worksheet Protection Block' (after 'Page Settings Block' and before DEFCOLWIDTH)
// PROTECT record normally goes here, don't add yet since the flag is initially false
records.add(_protectionBlock); // initially empty
defaultcolwidth = createDefaultColWidth();
records.add( defaultcolwidth);
@ -1407,62 +1394,11 @@ public final class Sheet implements Model {
}
/**
* creates an ObjectProtect record with protect set to false.
* @return the {@link WorksheetProtectionBlock} for this sheet
*/
private static ObjectProtectRecord createObjectProtect() {
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled");
ObjectProtectRecord retval = new ObjectProtectRecord();
retval.setProtect(false);
return retval;
public WorksheetProtectionBlock getProtectionBlock() {
return _protectionBlock;
}
/**
* creates a ScenarioProtect record with protect set to false.
*/
private static ScenarioProtectRecord createScenarioProtect() {
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "create protect record with protection disabled");
ScenarioProtectRecord retval = new ScenarioProtectRecord();
retval.setProtect(false);
return retval;
}
/**
* @return the ProtectRecord. If one is not contained in the sheet, then one is created.
*/
public ProtectRecord getProtect() {
if (protect == null) {
protect = new ProtectRecord(false);
// Insert the newly created protect record just before DefaultColWidthRecord
int loc = findFirstRecordLocBySid(DefaultColWidthRecord.sid);
_records.add(loc, protect);
}
return protect;
}
/**
* @return the PasswordRecord. If one is not contained in the sheet, then one is created.
*/
public PasswordRecord getPassword() {
if (_password == null) {
_password = createPassword();
//Insert the newly created password record at the end of the record (just before the EOF)
int loc = findFirstRecordLocBySid(EOFRecord.sid);
_records.add(loc, _password);
}
return _password;
}
/**
* creates a Password record with password set to 0x0000.
*/
private static PasswordRecord createPassword() {
return new PasswordRecord(0x0000);
}
/**
* Sets whether the gridlines are shown in a viewer.
* @param show whether to show gridlines or not
@ -1601,68 +1537,6 @@ public final class Sheet implements Model {
}
}
/**
* protect a spreadsheet with a password (not encypted, just sets protect
* flags and the password.
* @param password to set
* @param objects are protected
* @param scenarios are protected
*/
public void protectSheet( String password, boolean objects, boolean scenarios ) {
int protIdx = -1;
ProtectRecord prec = getProtect();
PasswordRecord pass = getPassword();
prec.setProtect(true);
pass.setPassword(PasswordRecord.hashPassword(password));
if((objprotect == null && objects) || (scenprotect != null && scenarios)) {
protIdx = _records.indexOf( protect );
}
if(objprotect == null && objects) {
ObjectProtectRecord rec = createObjectProtect();
rec.setProtect(true);
_records.add(protIdx+1,rec);
objprotect = rec;
}
if(scenprotect == null && scenarios) {
ScenarioProtectRecord srec = createScenarioProtect();
srec.setProtect(true);
_records.add(protIdx+2,srec);
scenprotect = srec;
}
}
/**
* unprotect objects in the sheet (will not protect them, but any set to false are
* unprotected.
* @param sheet is unprotected (false = unprotect)
* @param objects are unprotected (false = unprotect)
* @param scenarios are unprotected (false = unprotect)
*/
public void unprotectSheet( boolean sheet, boolean objects, boolean scenarios ) {
if (!sheet) {
ProtectRecord prec = getProtect();
prec.setProtect(sheet);
PasswordRecord pass = getPassword();
pass.setPassword((short)00);
}
if(objprotect != null && !objects) {
objprotect.setProtect(false);
}
if(scenprotect != null && !scenarios) {
scenprotect.setProtect(false);
}
}
/**
* @return {sheet is protected, objects are proteced, scenarios are protected}
*/
public boolean[] isProtected() {
return new boolean[] { (protect != null && protect.getProtect()),
(objprotect != null && objprotect.getProtect()),
(scenprotect != null && scenprotect.getProtect())};
}
public void groupRowRange(int fromRow, int toRow, boolean indent)
{

View File

@ -44,6 +44,7 @@ public final class UnknownRecord extends StandardRecord {
public static final int PRINTSIZE_0033 = 0x0033;
public static final int PLS_004D = 0x004D;
public static final int SHEETPR_0081 = 0x0081;
public static final int SORT_0090 = 0x0090;
public static final int STANDARDWIDTH_0099 = 0x0099;
public static final int SCL_00A0 = 0x00A0;
public static final int BITMAP_00E9 = 0x00E9;
@ -65,8 +66,8 @@ public final class UnknownRecord extends StandardRecord {
* @param data the data
*/
public UnknownRecord(int id, byte[] data) {
_sid = id & 0xFFFF;
_rawData = data;
_sid = id & 0xFFFF;
_rawData = data;
}
@ -135,7 +136,7 @@ public final class UnknownRecord extends StandardRecord {
case 0x0050: return "DCON"; // Data Consolidation Information
case 0x007F: return "IMDATA";
case SHEETPR_0081: return "SHEETPR";
case 0x0090: return "SORT"; // Sorting Options
case SORT_0090: return "SORT"; // Sorting Options
case 0x0094: return "LHRECORD"; // .WK? File Conversion Information
case STANDARDWIDTH_0099: return "STANDARDWIDTH"; //Standard Column Width
case 0x009D: return "AUTOFILTERINFO"; // Drop-Down Arrow Count
@ -209,7 +210,6 @@ public final class UnknownRecord extends StandardRecord {
}
/**
*
* @return <code>true</code> if the unknown record id has been observed in POI unit tests
*/
private static boolean isObservedButUnknown(int sid) {

View File

@ -0,0 +1,245 @@
/* ====================================================================
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.hssf.record.aggregates;
import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.record.ObjectProtectRecord;
import org.apache.poi.hssf.record.PasswordRecord;
import org.apache.poi.hssf.record.ProtectRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.ScenarioProtectRecord;
/**
* Groups the sheet protection records for a worksheet.
* <p/>
*
* See OOO excelfileformat.pdf sec 4.18.2 'Sheet Protection in a Workbook
* (BIFF5-BIFF8)'
*
* @author Josh Micich
*/
public final class WorksheetProtectionBlock extends RecordAggregate {
// Every one of these component records is optional
// (The whole WorksheetProtectionBlock may not be present)
private ProtectRecord _protectRecord;
private ObjectProtectRecord _objectProtectRecord;
private ScenarioProtectRecord _scenarioProtectRecord;
private PasswordRecord _passwordRecord;
/**
* Creates an empty WorksheetProtectionBlock
*/
public WorksheetProtectionBlock() {
// all fields empty
}
/**
* @return <code>true</code> if the specified Record sid is one belonging to
* the 'Page Settings Block'.
*/
public static boolean isComponentRecord(int sid) {
switch (sid) {
case ProtectRecord.sid:
case ObjectProtectRecord.sid:
case ScenarioProtectRecord.sid:
case PasswordRecord.sid:
return true;
}
return false;
}
private boolean readARecord(RecordStream rs) {
switch (rs.peekNextSid()) {
case ProtectRecord.sid:
checkNotPresent(_protectRecord);
_protectRecord = (ProtectRecord) rs.getNext();
break;
case ObjectProtectRecord.sid:
checkNotPresent(_objectProtectRecord);
_objectProtectRecord = (ObjectProtectRecord) rs.getNext();
break;
case ScenarioProtectRecord.sid:
checkNotPresent(_scenarioProtectRecord);
_scenarioProtectRecord = (ScenarioProtectRecord) rs.getNext();
break;
case PasswordRecord.sid:
checkNotPresent(_passwordRecord);
_passwordRecord = (PasswordRecord) rs.getNext();
break;
default:
// all other record types are not part of the PageSettingsBlock
return false;
}
return true;
}
private void checkNotPresent(Record rec) {
if (rec != null) {
throw new RecordFormatException("Duplicate PageSettingsBlock record (sid=0x"
+ Integer.toHexString(rec.getSid()) + ")");
}
}
public void visitContainedRecords(RecordVisitor rv) {
// Replicates record order from Excel 2007, though this is not critical
visitIfPresent(_protectRecord, rv);
visitIfPresent(_objectProtectRecord, rv);
visitIfPresent(_scenarioProtectRecord, rv);
visitIfPresent(_passwordRecord, rv);
}
private static void visitIfPresent(Record r, RecordVisitor rv) {
if (r != null) {
rv.visitRecord(r);
}
}
public PasswordRecord getPasswordRecord() {
return _passwordRecord;
}
public ScenarioProtectRecord getHCenter() {
return _scenarioProtectRecord;
}
/**
* This method reads {@link WorksheetProtectionBlock} records from the supplied RecordStream
* until the first non-WorksheetProtectionBlock record is encountered. As each record is read,
* it is incorporated into this WorksheetProtectionBlock.
* <p/>
* As per the OOO documentation, the protection block records can be expected to be written
* together (with no intervening records), but earlier versions of POI (prior to Jun 2009)
* didn't do this. Workbooks with sheet protection created by those earlier POI versions
* seemed to be valid (Excel opens them OK). So PO allows continues to support reading of files
* with non continuous worksheet protection blocks.
*
* <p/>
* <b>Note</b> - when POI writes out this WorksheetProtectionBlock, the records will always be
* written in one consolidated block (in the standard ordering) regardless of how scattered the
* records were when they were originally read.
*/
public void addRecords(RecordStream rs) {
while (true) {
if (!readARecord(rs)) {
break;
}
}
}
/**
* @return the ProtectRecord. If one is not contained in the sheet, then one
* is created.
*/
private ProtectRecord getProtect() {
if (_protectRecord == null) {
_protectRecord = new ProtectRecord(false);
}
return _protectRecord;
}
/**
* @return the PasswordRecord. If one is not contained in the sheet, then
* one is created.
*/
private PasswordRecord getPassword() {
if (_passwordRecord == null) {
_passwordRecord = createPassword();
}
return _passwordRecord;
}
/**
* protect a spreadsheet with a password (not encrypted, just sets protect
* flags and the password.
*
* @param password to set. Pass <code>null</code> to remove all protection
* @param shouldProtectObjects are protected
* @param shouldProtectScenarios are protected
*/
public void protectSheet(String password, boolean shouldProtectObjects,
boolean shouldProtectScenarios) {
if (password == null) {
_passwordRecord = null;
_protectRecord = null;
_objectProtectRecord = null;
_scenarioProtectRecord = null;
return;
}
ProtectRecord prec = getProtect();
PasswordRecord pass = getPassword();
prec.setProtect(true);
pass.setPassword(PasswordRecord.hashPassword(password));
if (_objectProtectRecord == null && shouldProtectObjects) {
ObjectProtectRecord rec = createObjectProtect();
rec.setProtect(true);
_objectProtectRecord = rec;
}
if (_scenarioProtectRecord == null && shouldProtectScenarios) {
ScenarioProtectRecord srec = createScenarioProtect();
srec.setProtect(true);
_scenarioProtectRecord = srec;
}
}
public boolean isSheetProtected() {
return _protectRecord != null && _protectRecord.getProtect();
}
public boolean isObjectProtected() {
return _objectProtectRecord != null && _objectProtectRecord.getProtect();
}
public boolean isScenarioProtected() {
return _scenarioProtectRecord != null && _scenarioProtectRecord.getProtect();
}
/**
* creates an ObjectProtect record with protect set to false.
*/
private static ObjectProtectRecord createObjectProtect() {
ObjectProtectRecord retval = new ObjectProtectRecord();
retval.setProtect(false);
return retval;
}
/**
* creates a ScenarioProtect record with protect set to false.
*/
private static ScenarioProtectRecord createScenarioProtect() {
ScenarioProtectRecord retval = new ScenarioProtectRecord();
retval.setProtect(false);
return retval;
}
/**
* creates a Password record with password set to 0x0000.
*/
private static PasswordRecord createPassword() {
return new PasswordRecord(0x0000);
}
public int getPasswordHash() {
if (_passwordRecord == null) {
return 0;
}
return _passwordRecord.getPassword();
}
}

View File

@ -44,6 +44,7 @@ import org.apache.poi.hssf.record.SCLRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.record.formula.FormulaShifter;
import org.apache.poi.hssf.util.PaneInformation;
import org.apache.poi.hssf.util.Region;
@ -511,12 +512,12 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* set for that column
*/
public HSSFCellStyle getColumnStyle(int column) {
short styleIndex = _sheet.getXFIndexForColAt((short)column);
short styleIndex = _sheet.getXFIndexForColAt((short)column);
if(styleIndex == 0xf) {
// None set
return null;
}
if(styleIndex == 0xf) {
// None set
return null;
}
ExtendedFormatRecord xf = _book.getExFormatAt(styleIndex);
return new HSSFCellStyle(styleIndex, xf, _book);
@ -967,19 +968,22 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
_sheet.getPageSettings().setMargin(margin, size);
}
private WorksheetProtectionBlock getProtectionBlock() {
return _sheet.getProtectionBlock();
}
/**
* Answer whether protection is enabled or disabled
* @return true => protection enabled; false => protection disabled
*/
public boolean getProtect() {
return getSheet().isProtected()[0];
return getProtectionBlock().isSheetProtected();
}
/**
* @return hashed password
*/
public short getPassword() {
return (short)getSheet().getPassword().getPassword();
return (short)getProtectionBlock().getPasswordHash();
}
/**
@ -987,7 +991,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @return true => protection enabled; false => protection disabled
*/
public boolean getObjectProtect() {
return getSheet().isProtected()[1];
return getProtectionBlock().isObjectProtected();
}
/**
@ -995,24 +999,14 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
* @return true => protection enabled; false => protection disabled
*/
public boolean getScenarioProtect() {
return getSheet().isProtected()[2];
return getProtectionBlock().isScenarioProtected();
}
/**
* Sets the protection on enabled or disabled
* @param protect true => protection enabled; false => protection disabled
* @deprecated (Jul 2007) use {@link #protectSheet(String)}
*/
public void setProtect(boolean protect) {
getSheet().getProtect().setProtect(protect);
}
/**
* Sets the protection enabled as well as the password
* @param password to set for protection
* @param password to set for protection. Pass <code>null</code> to remove protection
*/
public void protectSheet(String password) {
getSheet().protectSheet(password, true, true); //protect objs&scenarios(normal)
getProtectionBlock().protectSheet(password, true, true); //protect objs&scenarios(normal)
}
/**
@ -1476,10 +1470,10 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
for (Iterator<EscherRecord> iterator = escherRecords.iterator(); iterator.hasNext();) {
EscherRecord escherRecord = iterator.next();
if (fat) {
System.out.println(escherRecord.toString());
} else {
escherRecord.display(w, 0);
}
System.out.println(escherRecord.toString());
} else {
escherRecord.display(w, 0);
}
}
w.flush();
}
@ -1836,9 +1830,9 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
if(c != null) {
return c.getCellComment();
}
// No cell, so you will get new
// objects every time, sorry...
return HSSFCell.findCellComment(_sheet, row, column);
// No cell, so you will get new
// objects every time, sorry...
return HSSFCell.findCellComment(_sheet, row, column);
}
return null;
}

View File

@ -644,15 +644,14 @@ public final class TestSheet extends TestCase {
Sheet sheet = Sheet.createSheet();
List<RecordBase> sheetRecs = sheet.getRecords();
assertEquals(22, sheetRecs.size());
assertEquals(23, sheetRecs.size());
FormulaShifter shifter = FormulaShifter.createForRowShift(0, 0, 0, 1);
sheet.updateFormulasAfterCellShift(shifter, 0);
if (sheetRecs.size() == 23 && sheetRecs.get(21) instanceof ConditionalFormattingTable) {
if (sheetRecs.size() == 24 && sheetRecs.get(22) instanceof ConditionalFormattingTable) {
throw new AssertionFailedError("Identified bug 46547a");
}
assertEquals(22, sheetRecs.size());
assertEquals(23, sheetRecs.size());
}
/**
* Bug 46547 happened when attempting to add conditional formatting to a sheet

View File

@ -28,7 +28,10 @@ import org.apache.poi.hssf.HSSFITestDataProvider;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.DrawingManager2;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.usermodel.BaseTestSheet;
import org.apache.poi.ddf.EscherDgRecord;
@ -300,37 +303,104 @@ public final class TestHSSFSheet extends BaseTestSheet {
/**
* Test that the ProtectRecord is included when creating or cloning a sheet
*/
public void testProtect() {
public void testCloneWithProtect() {
String passwordA = "secrect";
int expectedHashA = -6810;
String passwordB = "admin";
int expectedHashB = -14556;
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet hssfSheet = workbook.createSheet();
Sheet sheet = hssfSheet.getSheet();
ProtectRecord protect = sheet.getProtect();
hssfSheet.protectSheet(passwordA);
assertFalse(protect.getProtect());
assertEquals(expectedHashA, hssfSheet.getSheet().getProtectionBlock().getPasswordHash());
// This will tell us that cloneSheet, and by extension,
// the list forms of createSheet leave us with an accessible
// ProtectRecord.
hssfSheet.protectSheet("secret");
Sheet cloned = sheet.cloneSheet();
assertNotNull(cloned.getProtect());
assertTrue(hssfSheet.getProtect());
// Clone the sheet, and make sure the password hash is preserved
HSSFSheet sheet2 = workbook.cloneSheet(0);
assertEquals(expectedHashA, sheet2.getSheet().getProtectionBlock().getPasswordHash());
// change the password on the first sheet
hssfSheet.protectSheet(passwordB);
assertEquals(expectedHashB, hssfSheet.getSheet().getProtectionBlock().getPasswordHash());
// but the cloned sheet's password should remain unchanged
assertEquals(expectedHashA, sheet2.getSheet().getProtectionBlock().getPasswordHash());
}
public void testProtectSheet() {
short expected = (short)0xfef1;
int expectedHash = (short)0xfef1;
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet s = wb.createSheet();
s.protectSheet("abcdefghij");
Sheet sheet = s.getSheet();
ProtectRecord protect = sheet.getProtect();
PasswordRecord pass = sheet.getPassword();
assertTrue("protection should be on",protect.getProtect());
assertTrue("object protection should be on",sheet.isProtected()[1]);
assertTrue("scenario protection should be on",sheet.isProtected()[2]);
assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword());
WorksheetProtectionBlock pb = s.getSheet().getProtectionBlock();
assertTrue("protection should be on", pb.isSheetProtected());
assertTrue("object protection should be on",pb.isObjectProtected());
assertTrue("scenario protection should be on",pb.isScenarioProtected());
assertEquals("well known value for top secret hash should be "+Integer.toHexString(expectedHash).substring(4), expectedHash, pb.getPasswordHash());
}
/**
* {@link PasswordRecord} belongs with the rest of the Worksheet Protection Block
* (which should be before {@link DimensionsRecord}).
*/
public void testProtectSheetRecordOrder_bug47363a() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet s = wb.createSheet();
s.protectSheet("secret");
RecordCollector rc = new RecordCollector();
s.getSheet().visitContainedRecords(rc, 0);
Record[] recs = rc.getRecords();
int nRecs = recs.length;
if (recs[nRecs-2] instanceof PasswordRecord && recs[nRecs-5] instanceof DimensionsRecord) {
throw new AssertionFailedError("Identified bug 47363a - PASSWORD after DIMENSION");
}
// Check that protection block is together, and before DIMENSION
confirmRecordClass(recs, nRecs-4, DimensionsRecord.class);
confirmRecordClass(recs, nRecs-9, ProtectRecord.class);
confirmRecordClass(recs, nRecs-8, ObjectProtectRecord.class);
confirmRecordClass(recs, nRecs-7, ScenarioProtectRecord.class);
confirmRecordClass(recs, nRecs-6, PasswordRecord.class);
}
private static void confirmRecordClass(Record[] recs, int index, Class<? extends Record> cls) {
if (recs.length <= index) {
throw new AssertionFailedError("Expected (" + cls.getName() + ") at index "
+ index + " but array length is " + recs.length + ".");
}
assertEquals(cls, recs[index].getClass());
}
/**
* There should be no problem with adding data validations after sheet protection
*/
public void testDvProtectionOrder_bug47363b() {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Sheet1");
sheet.protectSheet("secret");
DVConstraint dvc = DVConstraint.createNumericConstraint(DVConstraint.ValidationType.INTEGER,
DVConstraint.OperatorType.BETWEEN, "10", "100");
CellRangeAddressList numericCellAddressList = new CellRangeAddressList(0, 0, 1, 1);
HSSFDataValidation dv = new HSSFDataValidation(numericCellAddressList, dvc);
try {
sheet.addValidationData(dv);
} catch (IllegalStateException e) {
String expMsg = "Unexpected (org.apache.poi.hssf.record.PasswordRecord) while looking for DV Table insert pos";
if (expMsg.equals(e.getMessage())) {
throw new AssertionFailedError("Identified bug 47363b");
}
throw e;
}
RecordCollector rc;
rc = new RecordCollector();
sheet.getSheet().visitContainedRecords(rc, 0);
int nRecsWithProtection = rc.getRecords().length;
sheet.protectSheet(null);
rc = new RecordCollector();
sheet.getSheet().visitContainedRecords(rc, 0);
int nRecsWithoutProtection = rc.getRecords().length;
assertEquals(4, nRecsWithProtection - nRecsWithoutProtection);
}
public void testZoom() {
HSSFWorkbook wb = new HSSFWorkbook();
@ -732,5 +802,4 @@ public final class TestHSSFSheet extends BaseTestSheet {
assertFalse(cs.getFont(wbComplex).getItalic());
assertEquals(HSSFFont.BOLDWEIGHT_BOLD, cs.getFont(wbComplex).getBoldweight());
}
}