From: Nick Burch
Date: Thu, 16 Jul 2015 19:34:06 +0000 (+0000)
Subject: Start on conditional formatting thresholds
X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5c837dcb3ebd84a226cd96a9b9e85dd63efee52e;p=poi.git
Start on conditional formatting thresholds
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1691434 13f79535-47bb-0310-9956-ffa450edef68
---
diff --git a/src/java/org/apache/poi/hssf/record/cf/IconMultiStateFormatting.java b/src/java/org/apache/poi/hssf/record/cf/IconMultiStateFormatting.java
index 33390a6f08..f405c031e0 100644
--- a/src/java/org/apache/poi/hssf/record/cf/IconMultiStateFormatting.java
+++ b/src/java/org/apache/poi/hssf/record/cf/IconMultiStateFormatting.java
@@ -20,7 +20,6 @@ package org.apache.poi.hssf.record.cf;
import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.POILogFactory;
@@ -34,7 +33,7 @@ public final class IconMultiStateFormatting implements Cloneable {
private IconSet iconSet;
private byte options;
- private byte[] states; // TODO Decode
+ private Threshold[] thresholds;
private static BitField iconOnly = BitFieldFactory.getInstance(0x01);
private static BitField reversed = BitFieldFactory.getInstance(0x04);
@@ -42,7 +41,7 @@ public final class IconMultiStateFormatting implements Cloneable {
public IconMultiStateFormatting() {
iconSet = IconSet.GYR_3_TRAFFIC_LIGHTS;
options = 0;
- states = new byte[0];
+ thresholds = new Threshold[iconSet.num];
}
public IconMultiStateFormatting(LittleEndianInput in) {
in.readShort(); // Ignored
@@ -54,9 +53,11 @@ public final class IconMultiStateFormatting implements Cloneable {
log.log(POILogger.WARN, "Inconsistent Icon Set defintion, found " + iconSet + " but defined as " + num + " entries");
}
options = in.readByte();
- // TODO Decode
- states = new byte[in.available()];
- in.readFully(states);
+
+ thresholds = new Threshold[iconSet.num];
+ for (int i=0; i 0) {
+ formula = Formula.read(formuaLen, in);
+ }
+ // Value is only there for non-formula, non min/max thresholds
+ if (formula == null && type != RangeType.MIN.id &&
+ type != RangeType.MAX.id) {
+ value = in.readDouble();
+ }
+ equals = in.readByte();
+ // Reserved, 4 bytes, all 0
+ in.readInt();
+ }
+
+ public byte getType() {
+ return type;
+ }
+ public void setType(byte type) {
+ this.type = type;
+ }
+
+ public Formula getFormula() {
+ return formula;
+ }
+ public void setFormula(Formula formula) {
+ this.formula = formula;
+ }
+
+ public Double getValue() {
+ return value;
+ }
+ public void setValue(Double value) {
+ this.value = value;
+ }
+
+ public byte getEquals() {
+ return equals;
+ }
+ public void setEquals(byte equals) {
+ this.equals = equals;
+ }
+
+ public int getDataLength() {
+ int len = 1;
+ if (formula != null) {
+ len += formula.getEncodedSize();
+ } else {
+ len += 2;
+ }
+ if (value != null) {
+ len += 8;
+ }
+ len += 5;
+ return len;
+ }
+
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(" [CF Threshold]\n");
+ buffer.append(" .type = ").append(Integer.toHexString(type)).append("\n");
+ // TODO Output the formula better
+ buffer.append(" .formula = ").append(formula).append("\n");
+ buffer.append(" .value = ").append(value).append("\n");
+ buffer.append(" [/CF Threshold]\n");
+ return buffer.toString();
+ }
+
+ public Object clone() {
+ Threshold rec = new Threshold();
+ rec.type = type;
+ rec.formula = formula;
+ rec.value = value;
+ rec.equals = equals;
+ return rec;
+ }
+
+ public void serialize(LittleEndianOutput out) {
+ out.writeByte(type);
+ if (formula == null) {
+ out.writeShort(0);
+ } else {
+ formula.serialize(out);
+ }
+ if (value != null) {
+ out.writeDouble(value);
+ }
+ out.writeByte(equals);
+ out.writeInt(0); // Reserved
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
index 17bed7f6bf..67658bae4f 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
@@ -80,8 +80,6 @@ public final class HSSFConditionalFormatting implements ConditionalFormatting {
// TODO Should this be assigning unique IDs to the rules
// as they get added to the file?
- // TODO Support types beyond CELL_VALUE_IS and FORMULA
-
HSSFConditionalFormatting(HSSFWorkbook workbook, CFRecordsAggregate cfAggregate) {
if(workbook == null) {
throw new IllegalArgumentException("workbook must not be null");
@@ -112,10 +110,11 @@ public final class HSSFConditionalFormatting implements ConditionalFormatting {
/**
* Replaces an existing Conditional Formatting rule at position idx.
- * Excel allows to create up to 3 Conditional Formatting rules.
+ * Older versions of Excel only allow up to 3 Conditional Formatting rules,
+ * and will ignore rules beyond that, while newer versions are fine.
* This method can be useful to modify existing Conditional Formatting rules.
*
- * @param idx position of the rule. Should be between 0 and 2.
+ * @param idx position of the rule. Should be between 0 and 2 for older Excel versions
* @param cfRule - Conditional Formatting rule
*/
public void setRule(int idx, HSSFConditionalFormattingRule cfRule) {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingThreshold.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingThreshold.java
new file mode 100644
index 0000000000..7b21d9365d
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingThreshold.java
@@ -0,0 +1,57 @@
+/* ====================================================================
+ 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.usermodel;
+
+import org.apache.poi.hssf.record.cf.Threshold;
+import org.apache.poi.ss.formula.Formula;
+
+/**
+ * High level representation for Icon / Multi-State / Databar /
+ * Colour Scale change thresholds
+ */
+public final class HSSFConditionalFormattingThreshold implements org.apache.poi.ss.usermodel.ConditionalFormattingThreshold {
+ private final Threshold threshold;
+
+ protected HSSFConditionalFormattingThreshold(Threshold threshold) {
+ this.threshold = threshold;
+ }
+ protected Threshold getThreshold() {
+ return threshold;
+ }
+
+ public RangeType getRangeType() {
+ return RangeType.byId(threshold.getType());
+ }
+ public void setRangeType(RangeType type) {
+ threshold.setType((byte)type.id);
+ }
+
+ public Formula getFormula() {
+ return threshold.getFormula();
+ }
+ public void setFormula(Formula formula) {
+ threshold.setFormula(formula);
+ }
+
+ public Double getValue() {
+ return threshold.getValue();
+ }
+ public void setValue(Double value) {
+ threshold.setValue(value);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFIconMultiStateFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFIconMultiStateFormatting.java
index 18733e17ad..6e2ea49ad0 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFIconMultiStateFormatting.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFIconMultiStateFormatting.java
@@ -19,6 +19,8 @@ package org.apache.poi.hssf.usermodel;
import org.apache.poi.hssf.record.CFRule12Record;
import org.apache.poi.hssf.record.cf.IconMultiStateFormatting;
+import org.apache.poi.hssf.record.cf.Threshold;
+import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold;
/**
* High level representation for Icon / Multi-State Formatting
@@ -53,4 +55,21 @@ public final class HSSFIconMultiStateFormatting implements org.apache.poi.ss.use
public void setReversed(boolean reversed) {
iconFormatting.setReversed(reversed);
}
+
+ public ConditionalFormattingThreshold[] getThresholds() {
+ Threshold[] t = iconFormatting.getThresholds();
+ HSSFConditionalFormattingThreshold[] ht = new HSSFConditionalFormattingThreshold[t.length];
+ for (int i=0; iThis defines how to calculate the ranges for a conditional
+ * formatting rule, eg which values get a Green Traffic Light
+ * icon and which Yellow or Red.
+ */
+public interface ConditionalFormattingThreshold {
+ public enum RangeType {
+ /** Number / Parameter */
+ NUMBER(1, "num"),
+ /** The minimum value from the range */
+ MIN(2, "min"),
+ /** The maximum value from the range */
+ MAX(3, "max"),
+ /** Percent of the way from the mi to the max value in the range */
+ PERCENT(4, "percent"),
+ /** The minimum value of the cell that is in X percentile of the range */
+ PERCENTILE(5, "percentile"),
+ UNALLOCATED(6, null),
+ /** Formula result */
+ FORMULA(7, "formula");
+
+ /** Numeric ID of the type */
+ public int id;
+ /** Name (system) of the type */
+ public final String name;
+
+ public String toString() {
+ return id + " - " + name;
+ }
+
+ public static RangeType byId(int id) {
+ return values()[id-1]; // 1-based IDs
+ }
+
+ private RangeType(int id, String name) {
+ this.id = id; this.name = name;
+ }
+ }
+
+ /**
+ * Get the Range Type used
+ */
+ RangeType getRangeType();
+
+ /**
+ * Changes the Range Type used
+ *
+ * If you change the range type, you need to
+ * ensure that the Formula and Value parameters
+ * are compatible with it before saving
+ */
+ void setRangeType(RangeType type);
+
+ /**
+ * Formula to use to calculate the threshold,
+ * or null
if no formula
+ */
+ Formula getFormula();
+
+ /**
+ * Sets the formula used to calculate the threshold,
+ * or unsets it if null
is given.
+ */
+ void setFormula(Formula formula);
+
+ /**
+ * Gets the value used for the threshold, or
+ * null
if there isn't one.
+ */
+ Double getValue();
+
+ /**
+ * Sets the value used for the threshold.
+ * If the type is {@link RangeType#PERCENT} or
+ * {@link RangeType#PERCENTILE} it must be between 0 and 100.
+ *
If the type is {@link RangeType#MIN} or {@link RangeType#MAX}
+ * or {@link RangeType#FORMULA} it shouldn't be set.
+ *
Use null
to unset
+ */
+ void setValue(Double value);
+}
diff --git a/src/java/org/apache/poi/ss/usermodel/IconMultiStateFormatting.java b/src/java/org/apache/poi/ss/usermodel/IconMultiStateFormatting.java
index d4762c790c..cdd46d0573 100644
--- a/src/java/org/apache/poi/ss/usermodel/IconMultiStateFormatting.java
+++ b/src/java/org/apache/poi/ss/usermodel/IconMultiStateFormatting.java
@@ -104,5 +104,13 @@ public interface IconMultiStateFormatting {
boolean isReversed();
void setReversed(boolean reversed);
- // TODO States
+ /**
+ * Gets the list of thresholds
+ */
+ ConditionalFormattingThreshold[] getThresholds();
+ /**
+ * Sets the of thresholds. The number must match
+ * {@link IconSet#num} for the current {@link #getIconSet()}
+ */
+ void setThresholds(ConditionalFormattingThreshold[] thresholds);
}