Introduce a CFHeaderBase common parent #58130

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1690503 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2015-07-12 18:35:33 +00:00
parent d7666dba2d
commit 275d551956
4 changed files with 207 additions and 132 deletions

View File

@ -25,14 +25,14 @@ import org.apache.poi.util.LittleEndianOutput;
* Conditional Formatting Header v12 record CFHEADER12 (0x0879),
* for conditional formattings introduced in Excel 2007 and newer.
*/
public final class CFHeader12Record extends CFHeaderRecord {
public final class CFHeader12Record extends CFHeaderBase {
public static final short sid = 0x0879;
private FtrHeader futureHeader;
/** Creates new CFHeaderRecord */
public CFHeader12Record() {
super();
createEmpty();
futureHeader = new FtrHeader();
futureHeader.setRecordType(sid);
}
@ -41,9 +41,7 @@ public final class CFHeader12Record extends CFHeaderRecord {
futureHeader = new FtrHeader();
futureHeader.setRecordType(sid);
}
public CFHeader12Record(RecordInputStream in)
{
public CFHeader12Record(RecordInputStream in) {
futureHeader = new FtrHeader(in);
read(in);
}
@ -69,7 +67,7 @@ public final class CFHeader12Record extends CFHeaderRecord {
public Object clone() {
CFHeader12Record result = new CFHeader12Record();
result.futureHeader = (FtrHeader)futureHeader.clone();
// TODO Clone the rest via the base
super.copyTo(result);
return result;
}
}

View File

@ -0,0 +1,153 @@
/* ====================================================================
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;
import org.apache.poi.hssf.record.cf.CellRangeUtil;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.LittleEndianOutput;
/**
* Parent of Conditional Formatting Header records,
* {@link CFHeaderRecord} and {@link CFHeader12Record}.
*/
public abstract class CFHeaderBase extends StandardRecord {
private int field_1_numcf;
private int field_2_need_recalculation_and_id;
private CellRangeAddress field_3_enclosing_cell_range;
private CellRangeAddressList field_4_cell_ranges;
/** Creates new CFHeaderBase */
protected CFHeaderBase() {
}
protected CFHeaderBase(CellRangeAddress[] regions, int nRules) {
CellRangeAddress[] unmergedRanges = regions;
CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges);
setCellRanges(mergeCellRanges);
field_1_numcf = nRules;
}
protected void createEmpty() {
field_3_enclosing_cell_range = new CellRangeAddress(0, 0, 0, 0);
field_4_cell_ranges = new CellRangeAddressList();
}
protected void read(RecordInputStream in) {
field_1_numcf = in.readShort();
field_2_need_recalculation_and_id = in.readShort();
field_3_enclosing_cell_range = new CellRangeAddress(in);
field_4_cell_ranges = new CellRangeAddressList(in);
}
public int getNumberOfConditionalFormats() {
return field_1_numcf;
}
public void setNumberOfConditionalFormats(int n) {
field_1_numcf=n;
}
public boolean getNeedRecalculation() {
// Held on the 1st bit
return field_2_need_recalculation_and_id % 2 == 1;
}
public void setNeedRecalculation(boolean b) {
// held on the first bit
if (b == getNeedRecalculation()) return;
if (b) field_2_need_recalculation_and_id++;
else field_2_need_recalculation_and_id--;
}
public int getID() {
// Remaining 15 bits of field 2
return field_2_need_recalculation_and_id>>1;
}
public void setID(int id) {
// Remaining 15 bits of field 2
boolean needsRecalc = getNeedRecalculation();
field_2_need_recalculation_and_id = (id<<1);
if (needsRecalc) field_2_need_recalculation_and_id++;
}
public CellRangeAddress getEnclosingCellRange() {
return field_3_enclosing_cell_range;
}
public void setEnclosingCellRange(CellRangeAddress cr) {
field_3_enclosing_cell_range = cr;
}
/**
* Set cell ranges list to a single cell range and
* modify the enclosing cell range accordingly.
* @param cellRanges - list of CellRange objects
*/
public void setCellRanges(CellRangeAddress[] cellRanges) {
if(cellRanges == null) {
throw new IllegalArgumentException("cellRanges must not be null");
}
CellRangeAddressList cral = new CellRangeAddressList();
CellRangeAddress enclosingRange = null;
for (int i = 0; i < cellRanges.length; i++) {
CellRangeAddress cr = cellRanges[i];
enclosingRange = CellRangeUtil.createEnclosingCellRange(cr, enclosingRange);
cral.addCellRangeAddress(cr);
}
field_3_enclosing_cell_range = enclosingRange;
field_4_cell_ranges = cral;
}
public CellRangeAddress[] getCellRanges() {
return field_4_cell_ranges.getCellRangeAddresses();
}
protected abstract String getRecordName();
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[").append(getRecordName()).append("]\n");
buffer.append("\t.numCF = ").append(getNumberOfConditionalFormats()).append("\n");
buffer.append("\t.needRecalc = ").append(getNeedRecalculation()).append("\n");
buffer.append("\t.id = ").append(getID()).append("\n");
buffer.append("\t.enclosingCellRange= ").append(getEnclosingCellRange()).append("\n");
buffer.append("\t.cfranges=[");
for( int i=0; i<field_4_cell_ranges.countRanges(); i++) {
buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString());
}
buffer.append("]\n");
buffer.append("[/").append(getRecordName()).append("]\n");
return buffer.toString();
}
protected int getDataSize() {
return 4 // 2 short fields
+ CellRangeAddress.ENCODED_SIZE
+ field_4_cell_ranges.getSize();
}
public void serialize(LittleEndianOutput out) {
out.writeShort(field_1_numcf);
out.writeShort(field_2_need_recalculation_and_id);
field_3_enclosing_cell_range.serialize(out);
field_4_cell_ranges.serialize(out);
}
protected void copyTo(CFHeaderBase result) {
result.field_1_numcf = field_1_numcf;
result.field_2_need_recalculation_and_id = field_2_need_recalculation_and_id;
result.field_3_enclosing_cell_range = field_3_enclosing_cell_range.copy();
result.field_4_cell_ranges = field_4_cell_ranges.copy();
}
}

View File

@ -17,139 +17,31 @@
package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.cf.CellRangeUtil;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.LittleEndianOutput;
/**
* Conditional Formatting Header record CFHEADER (0x01B0).
* Used to describe a {@link CFRuleRecord}.
* @see CFHeader12Record
* TODO Move most of the logic into a base class
*/
public class CFHeaderRecord extends StandardRecord {
public final class CFHeaderRecord extends CFHeaderBase {
public static final short sid = 0x01B0;
private int field_1_numcf;
private int field_2_need_recalculation_and_id;
private CellRangeAddress field_3_enclosing_cell_range;
private CellRangeAddressList field_4_cell_ranges;
/** Creates new CFHeaderRecord */
public CFHeaderRecord() {
field_3_enclosing_cell_range = new CellRangeAddress(0, 0, 0, 0);
field_4_cell_ranges = new CellRangeAddressList();
createEmpty();
}
public CFHeaderRecord(CellRangeAddress[] regions, int nRules) {
CellRangeAddress[] unmergedRanges = regions;
CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges);
setCellRanges(mergeCellRanges);
field_1_numcf = nRules;
super(regions, nRules);
}
public CFHeaderRecord(RecordInputStream in) {
read(in);
}
protected void read(RecordInputStream in) {
field_1_numcf = in.readShort();
field_2_need_recalculation_and_id = in.readShort();
field_3_enclosing_cell_range = new CellRangeAddress(in);
field_4_cell_ranges = new CellRangeAddressList(in);
}
public int getNumberOfConditionalFormats() {
return field_1_numcf;
}
public void setNumberOfConditionalFormats(int n) {
field_1_numcf=n;
}
public boolean getNeedRecalculation() {
// Held on the 1st bit
return field_2_need_recalculation_and_id % 2 == 1;
}
public void setNeedRecalculation(boolean b) {
// held on the first bit
if (b == getNeedRecalculation()) return;
if (b) field_2_need_recalculation_and_id++;
else field_2_need_recalculation_and_id--;
}
public int getID() {
// Remaining 15 bits of field 2
return field_2_need_recalculation_and_id>>1;
}
public void setID(int id) {
// Remaining 15 bits of field 2
boolean needsRecalc = getNeedRecalculation();
field_2_need_recalculation_and_id = (id<<1);
if (needsRecalc) field_2_need_recalculation_and_id++;
}
public CellRangeAddress getEnclosingCellRange() {
return field_3_enclosing_cell_range;
}
public void setEnclosingCellRange(CellRangeAddress cr) {
field_3_enclosing_cell_range = cr;
}
/**
* Set cell ranges list to a single cell range and
* modify the enclosing cell range accordingly.
* @param cellRanges - list of CellRange objects
*/
public void setCellRanges(CellRangeAddress[] cellRanges) {
if(cellRanges == null) {
throw new IllegalArgumentException("cellRanges must not be null");
}
CellRangeAddressList cral = new CellRangeAddressList();
CellRangeAddress enclosingRange = null;
for (int i = 0; i < cellRanges.length; i++) {
CellRangeAddress cr = cellRanges[i];
enclosingRange = CellRangeUtil.createEnclosingCellRange(cr, enclosingRange);
cral.addCellRangeAddress(cr);
}
field_3_enclosing_cell_range = enclosingRange;
field_4_cell_ranges = cral;
}
public CellRangeAddress[] getCellRanges() {
return field_4_cell_ranges.getCellRangeAddresses();
}
protected String getRecordName() {
return "CFHEADER";
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[").append(getRecordName()).append("]\n");
buffer.append(" .id = ").append(Integer.toHexString(sid)).append("\n");
buffer.append(" .numCF = ").append(getNumberOfConditionalFormats()).append("\n");
buffer.append(" .needRecalc = ").append(getNeedRecalculation()).append("\n");
buffer.append(" .enclosingCellRange= ").append(getEnclosingCellRange()).append("\n");
buffer.append(" .cfranges=[");
for( int i=0; i<field_4_cell_ranges.countRanges(); i++) {
buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString());
}
buffer.append("]\n");
buffer.append("[/").append(getRecordName()).append("]\n");
return buffer.toString();
}
protected int getDataSize() {
return 4 // 2 short fields
+ CellRangeAddress.ENCODED_SIZE
+ field_4_cell_ranges.getSize();
}
public void serialize(LittleEndianOutput out) {
out.writeShort(field_1_numcf);
out.writeShort(field_2_need_recalculation_and_id);
field_3_enclosing_cell_range.serialize(out);
field_4_cell_ranges.serialize(out);
}
public short getSid() {
return sid;
@ -157,10 +49,7 @@ public class CFHeaderRecord extends StandardRecord {
public Object clone() {
CFHeaderRecord result = new CFHeaderRecord();
result.field_1_numcf = field_1_numcf;
result.field_2_need_recalculation_and_id = field_2_need_recalculation_and_id;
result.field_3_enclosing_cell_range = field_3_enclosing_cell_range.copy();
result.field_4_cell_ranges = field_4_cell_ranges.copy();
super.copyTo(result);
return result;
}
}

View File

@ -23,16 +23,11 @@ import junit.framework.TestCase;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* Tests the serialization and deserialization of the TestCFHeaderRecord
* class works correctly.
*
* @author Dmitriy Kumshayev
* Tests the serialization and deserialization of the {@link CFHeaderRecord}
* and {@link CFHeader12Record} classes works correctly.
*/
public final class TestCFHeaderRecord extends TestCase
{
public void testCreateCFHeaderRecord ()
{
public final class TestCFHeaderRecord extends TestCase {
public void testCreateCFHeaderRecord () {
CFHeaderRecord record = new CFHeaderRecord();
CellRangeAddress[] ranges = {
new CellRangeAddress(0,0xFFFF,5,5),
@ -50,12 +45,52 @@ public final class TestCFHeaderRecord extends TestCase
assertEquals(65535, enclosingCellRange.getLastRow());
assertEquals(0, enclosingCellRange.getFirstColumn());
assertEquals(6, enclosingCellRange.getLastColumn());
assertEquals(false, record.getNeedRecalculation());
assertEquals(0, record.getID());
record.setNeedRecalculation(true);
assertTrue(record.getNeedRecalculation());
assertEquals(true, record.getNeedRecalculation());
assertEquals(0, record.getID());
record.setID(7);
record.setNeedRecalculation(false);
assertFalse(record.getNeedRecalculation());
assertEquals(false, record.getNeedRecalculation());
assertEquals(7, record.getID());
}
public void testCreateCFHeader12Record () {
CFHeader12Record record = new CFHeader12Record();
CellRangeAddress[] ranges = {
new CellRangeAddress(0,0xFFFF,5,5),
new CellRangeAddress(0,0xFFFF,6,6),
new CellRangeAddress(0,1,0,1),
new CellRangeAddress(0,1,2,3),
new CellRangeAddress(2,3,0,1),
new CellRangeAddress(2,3,2,3),
};
record.setCellRanges(ranges);
ranges = record.getCellRanges();
assertEquals(6,ranges.length);
CellRangeAddress enclosingCellRange = record.getEnclosingCellRange();
assertEquals(0, enclosingCellRange.getFirstRow());
assertEquals(65535, enclosingCellRange.getLastRow());
assertEquals(0, enclosingCellRange.getFirstColumn());
assertEquals(6, enclosingCellRange.getLastColumn());
assertEquals(false, record.getNeedRecalculation());
assertEquals(0, record.getID());
record.setNeedRecalculation(true);
assertEquals(true, record.getNeedRecalculation());
assertEquals(0, record.getID());
record.setID(7);
record.setNeedRecalculation(false);
assertEquals(false, record.getNeedRecalculation());
assertEquals(7, record.getID());
}
public void testSerialization() {
byte[] recordData =
{