From: Nick Burch Date: Thu, 16 Jul 2015 19:34:06 +0000 (+0000) Subject: Start on conditional formatting thresholds X-Git-Tag: REL_3_13_FINAL~245 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d18aec0795bb45f1fb08a0fc2c6e8f05b39d2c63;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); }