#58130 CF Threshold formats differ slightly between types

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1691858 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2015-07-19 22:09:41 +00:00
parent 7bd1216c52
commit 9aa44bca07
7 changed files with 208 additions and 60 deletions

View File

@ -21,6 +21,7 @@ import java.util.Arrays;
import org.apache.poi.hssf.record.cf.ColorGradientFormatting; import org.apache.poi.hssf.record.cf.ColorGradientFormatting;
import org.apache.poi.hssf.record.cf.IconMultiStateFormatting; import org.apache.poi.hssf.record.cf.IconMultiStateFormatting;
import org.apache.poi.hssf.record.cf.IconMultiStateThreshold;
import org.apache.poi.hssf.record.cf.Threshold; import org.apache.poi.hssf.record.cf.Threshold;
import org.apache.poi.hssf.record.common.FtrHeader; import org.apache.poi.hssf.record.common.FtrHeader;
import org.apache.poi.hssf.record.common.FutureRecord; import org.apache.poi.hssf.record.common.FutureRecord;
@ -125,7 +126,7 @@ public final class CFRule12Record extends CFRuleBase implements FutureRecord {
public static CFRule12Record create(HSSFSheet sheet, IconSet iconSet) { public static CFRule12Record create(HSSFSheet sheet, IconSet iconSet) {
Threshold[] ts = new Threshold[iconSet.num]; Threshold[] ts = new Threshold[iconSet.num];
for (int i=0; i<ts.length; i++) { for (int i=0; i<ts.length; i++) {
ts[i] = new Threshold(); ts[i] = new IconMultiStateThreshold();
} }
CFRule12Record r = new CFRule12Record(CONDITION_TYPE_COLOR_SCALE, CFRule12Record r = new CFRule12Record(CONDITION_TYPE_COLOR_SCALE,

View File

@ -17,6 +17,7 @@
package org.apache.poi.hssf.record.cf; package org.apache.poi.hssf.record.cf;
import org.apache.poi.hssf.record.common.ExtendedColor;
import org.apache.poi.util.BitField; import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
@ -33,15 +34,16 @@ public final class ColorGradientFormatting implements Cloneable {
private static POILogger log = POILogFactory.getLogger(ColorGradientFormatting.class); private static POILogger log = POILogFactory.getLogger(ColorGradientFormatting.class);
private byte options = 0; private byte options = 0;
private Threshold[] thresholds; private ColorGradientThreshold[] thresholds;
private byte[] colors; // TODO Decode private ExtendedColor[] colors;
private static BitField clamp = BitFieldFactory.getInstance(0x01); private static BitField clamp = BitFieldFactory.getInstance(0x01);
private static BitField background = BitFieldFactory.getInstance(0x02); private static BitField background = BitFieldFactory.getInstance(0x02);
public ColorGradientFormatting() { public ColorGradientFormatting() {
options = 3; options = 3;
thresholds = new Threshold[3]; thresholds = new ColorGradientThreshold[3];
colors = new ExtendedColor[3];
} }
public ColorGradientFormatting(LittleEndianInput in) { public ColorGradientFormatting(LittleEndianInput in) {
in.readShort(); // Ignored in.readShort(); // Ignored
@ -53,15 +55,15 @@ public final class ColorGradientFormatting implements Cloneable {
} }
options = in.readByte(); options = in.readByte();
// TODO Are these correct? thresholds = new ColorGradientThreshold[numI];
thresholds = new Threshold[numI];
for (int i=0; i<thresholds.length; i++) { for (int i=0; i<thresholds.length; i++) {
thresholds[i] = new Threshold(in); thresholds[i] = new ColorGradientThreshold(in);
in.readDouble(); // Rather pointless value... }
colors = new ExtendedColor[numG];
for (int i=0; i<colors.length; i++) {
in.readDouble(); // Slightly pointless step counter
colors[i] = new ExtendedColor(in);
} }
// TODO Decode colors
colors = new byte[in.available()];
in.readFully(colors);
} }
public int getNumControlPoints() { public int getNumControlPoints() {
@ -69,19 +71,34 @@ public final class ColorGradientFormatting implements Cloneable {
} }
public void setNumControlPoints(int num) { public void setNumControlPoints(int num) {
if (num != thresholds.length) { if (num != thresholds.length) {
thresholds = new Threshold[num]; ColorGradientThreshold[] nt = new ColorGradientThreshold[num];
// TODO Colors ExtendedColor[] nc = new ExtendedColor[num];
int copy = Math.min(thresholds.length, num);
System.arraycopy(thresholds, 0, nt, 0, copy);
System.arraycopy(colors, 0, nc, 0, copy);
this.thresholds = nt;
this.colors = nc;
updateThresholdPositions();
} }
} }
public Threshold[] getThresholds() { public ColorGradientThreshold[] getThresholds() {
return thresholds; return thresholds;
} }
public void setThresholds(Threshold[] thresholds) { public void setThresholds(ColorGradientThreshold[] thresholds) {
this.thresholds = thresholds; this.thresholds = thresholds;
updateThresholdPositions();
} }
// TODO Colors public ExtendedColor[] getColors() {
return colors;
}
public void setColors(ExtendedColor[] colors) {
this.colors = colors;
}
public boolean isClampToCurve() { public boolean isClampToCurve() {
return getOptionFlag(clamp); return getOptionFlag(clamp);
@ -94,6 +111,13 @@ public final class ColorGradientFormatting implements Cloneable {
return value==0 ? false : true; return value==0 ? false : true;
} }
private void updateThresholdPositions() {
double step = 1d / (thresholds.length-1);
for (int i=0; i<thresholds.length; i++) {
thresholds[i].setPosition(step*i);
}
}
public String toString() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append(" [Color Gradient Formatting]\n"); buffer.append(" [Color Gradient Formatting]\n");
@ -102,6 +126,9 @@ public final class ColorGradientFormatting implements Cloneable {
for (Threshold t : thresholds) { for (Threshold t : thresholds) {
buffer.append(t.toString()); buffer.append(t.toString());
} }
for (ExtendedColor c : colors) {
buffer.append(c.toString());
}
buffer.append(" [/Color Gradient Formatting]\n"); buffer.append(" [/Color Gradient Formatting]\n");
return buffer.toString(); return buffer.toString();
} }
@ -109,9 +136,10 @@ public final class ColorGradientFormatting implements Cloneable {
public Object clone() { public Object clone() {
ColorGradientFormatting rec = new ColorGradientFormatting(); ColorGradientFormatting rec = new ColorGradientFormatting();
rec.options = options; rec.options = options;
rec.thresholds = new Threshold[thresholds.length]; rec.thresholds = new ColorGradientThreshold[thresholds.length];
rec.colors = new ExtendedColor[colors.length];
System.arraycopy(thresholds, 0, rec.thresholds, 0, thresholds.length); System.arraycopy(thresholds, 0, rec.thresholds, 0, thresholds.length);
// TODO Colors System.arraycopy(colors, 0, rec.colors, 0, colors.length);
return rec; return rec;
} }
@ -119,9 +147,11 @@ public final class ColorGradientFormatting implements Cloneable {
int len = 6; int len = 6;
for (Threshold t : thresholds) { for (Threshold t : thresholds) {
len += t.getDataLength(); len += t.getDataLength();
}
for (ExtendedColor c : colors) {
len += c.getDataLength();
len += 8; len += 8;
} }
len += colors.length;
return len; return len;
} }
@ -132,13 +162,16 @@ public final class ColorGradientFormatting implements Cloneable {
out.writeByte(thresholds.length); out.writeByte(thresholds.length);
out.writeByte(options); out.writeByte(options);
double step = 1d / (thresholds.length-1); for (ColorGradientThreshold t : thresholds) {
for (int i=0; i<thresholds.length; i++) {
Threshold t = thresholds[i];
t.serialize(out); t.serialize(out);
out.writeDouble(step*i);
} }
out.write(colors); double step = 1d / (colors.length-1);
for (int i=0; i<colors.length; i++) {
out.writeDouble(i*step);
ExtendedColor c = colors[i];
c.serialize(out);
}
} }
} }

View File

@ -0,0 +1,63 @@
/* ====================================================================
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.cf;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
* Color Gradient / Color Scale specific Threshold / value (CFVO),
* for changes in Conditional Formatting
*/
public final class ColorGradientThreshold extends Threshold {
private double position;
public ColorGradientThreshold() {
super();
position = 0d;
}
/** Creates new Ico Multi-State Threshold */
public ColorGradientThreshold(LittleEndianInput in) {
super(in);
position = in.readDouble();
}
public double getPosition() {
return position;
}
public void setPosition(double position) {
this.position = position;
}
public int getDataLength() {
return super.getDataLength() + 8;
}
public Object clone() {
ColorGradientThreshold rec = new ColorGradientThreshold();
super.copyTo(rec);
rec.position = position;
return rec;
}
public void serialize(LittleEndianOutput out) {
super.serialize(out);
out.writeDouble(position);
}
}

View File

@ -56,7 +56,7 @@ public final class IconMultiStateFormatting implements Cloneable {
thresholds = new Threshold[iconSet.num]; thresholds = new Threshold[iconSet.num];
for (int i=0; i<thresholds.length; i++) { for (int i=0; i<thresholds.length; i++) {
thresholds[i] = new Threshold(in); thresholds[i] = new IconMultiStateThreshold(in);
} }
} }

View File

@ -0,0 +1,75 @@
/* ====================================================================
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.cf;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
* Icon / Multi-State specific Threshold / value (CFVO),
* for changes in Conditional Formatting
*/
public final class IconMultiStateThreshold extends Threshold {
/**
* Cell values that are equal to the threshold value do not pass the threshold
*/
public static final byte EQUALS_EXCLUDE = 0;
/**
* Cell values that are equal to the threshold value pass the threshold.
*/
public static final byte EQUALS_INCLUDE = 1;
private byte equals;
public IconMultiStateThreshold() {
super();
equals = EQUALS_INCLUDE;
}
/** Creates new Ico Multi-State Threshold */
public IconMultiStateThreshold(LittleEndianInput in) {
super(in);
equals = in.readByte();
// Reserved, 4 bytes, all 0
in.readInt();
}
public byte getEquals() {
return equals;
}
public void setEquals(byte equals) {
this.equals = equals;
}
public int getDataLength() {
return super.getDataLength() + 5;
}
public Object clone() {
IconMultiStateThreshold rec = new IconMultiStateThreshold();
super.copyTo(rec);
rec.equals = equals;
return rec;
}
public void serialize(LittleEndianOutput out) {
super.serialize(out);
out.writeByte(equals);
out.writeInt(0); // Reserved
}
}

View File

@ -26,31 +26,21 @@ import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
/** /**
* Threshold / value for changes in Conditional Formatting * Threshold / value (CFVO) for changes in Conditional Formatting
*/ */
public final class Threshold { public abstract class Threshold {
/**
* Cell values that are equal to the threshold value do not pass the threshold
*/
public static final byte EQUALS_EXCLUDE = 0;
/**
* Cell values that are equal to the threshold value pass the threshold.
*/
public static final byte EQUALS_INCLUDE = 1;
private byte type; private byte type;
private Formula formula; private Formula formula;
private Double value; private Double value;
private byte equals;
public Threshold() { protected Threshold() {
type = (byte)RangeType.NUMBER.id; type = (byte)RangeType.NUMBER.id;
formula = Formula.create(null); formula = Formula.create(null);
value = 0d; value = 0d;
} }
/** Creates new Threshold */ /** Creates new Threshold */
public Threshold(LittleEndianInput in) { protected Threshold(LittleEndianInput in) {
type = in.readByte(); type = in.readByte();
short formulaLen = in.readShort(); short formulaLen = in.readShort();
if (formulaLen > 0) { if (formulaLen > 0) {
@ -63,9 +53,6 @@ public final class Threshold {
type != RangeType.MAX.id) { type != RangeType.MAX.id) {
value = in.readDouble(); value = in.readDouble();
} }
equals = in.readByte();
// Reserved, 4 bytes, all 0
in.readInt();
} }
public byte getType() { public byte getType() {
@ -74,7 +61,7 @@ public final class Threshold {
public void setType(byte type) { public void setType(byte type) {
this.type = type; this.type = type;
// Ensure the value presense / absense is consistent for the new type // Ensure the value presence / absence is consistent for the new type
if (type == RangeType.MIN.id || type == RangeType.MAX.id || if (type == RangeType.MIN.id || type == RangeType.MAX.id ||
type == RangeType.FORMULA.id) { type == RangeType.FORMULA.id) {
this.value = null; this.value = null;
@ -106,19 +93,11 @@ public final class Threshold {
this.value = value; this.value = value;
} }
public byte getEquals() {
return equals;
}
public void setEquals(byte equals) {
this.equals = equals;
}
public int getDataLength() { public int getDataLength() {
int len = 1 + formula.getEncodedSize(); int len = 1 + formula.getEncodedSize();
if (value != null) { if (value != null) {
len += 8; len += 8;
} }
len += 5;
return len; return len;
} }
@ -132,13 +111,10 @@ public final class Threshold {
return buffer.toString(); return buffer.toString();
} }
public Object clone() { public void copyTo(Threshold rec) {
Threshold rec = new Threshold();
rec.type = type; rec.type = type;
rec.formula = formula; rec.formula = formula;
rec.value = value; rec.value = value;
rec.equals = equals;
return rec;
} }
public void serialize(LittleEndianOutput out) { public void serialize(LittleEndianOutput out) {
@ -151,7 +127,5 @@ public final class Threshold {
if (value != null) { if (value != null) {
out.writeDouble(value); out.writeDouble(value);
} }
out.writeByte(equals);
out.writeInt(0); // Reserved
} }
} }

View File

@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel;
import org.apache.poi.hssf.record.CFRule12Record; import org.apache.poi.hssf.record.CFRule12Record;
import org.apache.poi.hssf.record.cf.ColorGradientFormatting; import org.apache.poi.hssf.record.cf.ColorGradientFormatting;
import org.apache.poi.hssf.record.cf.ColorGradientThreshold;
import org.apache.poi.hssf.record.cf.Threshold; import org.apache.poi.hssf.record.cf.Threshold;
import org.apache.poi.ss.usermodel.Color; import org.apache.poi.ss.usermodel.Color;
import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold; import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold;
@ -62,14 +63,15 @@ public final class HSSFColorScaleFormatting implements org.apache.poi.ss.usermod
} }
public void setThresholds(ConditionalFormattingThreshold[] thresholds) { public void setThresholds(ConditionalFormattingThreshold[] thresholds) {
Threshold[] t = new Threshold[thresholds.length]; ColorGradientThreshold[] t = new ColorGradientThreshold[thresholds.length];
for (int i=0; i<t.length; i++) { for (int i=0; i<t.length; i++) {
t[i] = ((HSSFConditionalFormattingThreshold)thresholds[i]).getThreshold(); HSSFConditionalFormattingThreshold hssfT = (HSSFConditionalFormattingThreshold)thresholds[i];
t[i] = (ColorGradientThreshold)hssfT.getThreshold();
} }
colorFormatting.setThresholds(t); colorFormatting.setThresholds(t);
} }
public HSSFConditionalFormattingThreshold createThreshold() { public HSSFConditionalFormattingThreshold createThreshold() {
return new HSSFConditionalFormattingThreshold(new Threshold(), sheet); return new HSSFConditionalFormattingThreshold(new ColorGradientThreshold(), sheet);
} }
} }