git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1691434 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_13_FINAL
@@ -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<thresholds.length; i++) { | |||
thresholds[i] = new Threshold(in); | |||
} | |||
} | |||
public IconSet getIconSet() { | |||
@@ -66,6 +67,13 @@ public final class IconMultiStateFormatting implements Cloneable { | |||
this.iconSet = set; | |||
} | |||
public Threshold[] getThresholds() { | |||
return thresholds; | |||
} | |||
public void setThresholds(Threshold[] thresholds) { | |||
this.thresholds = thresholds; | |||
} | |||
public boolean isIconOnly() { | |||
return getOptionFlag(iconOnly); | |||
} | |||
@@ -94,7 +102,9 @@ public final class IconMultiStateFormatting implements Cloneable { | |||
buffer.append(" .icon_set = ").append(iconSet).append("\n"); | |||
buffer.append(" .icon_only= ").append(isIconOnly()).append("\n"); | |||
buffer.append(" .reversed = ").append(isReversed()).append("\n"); | |||
buffer.append(" .states = ").append(HexDump.toHex(states)).append("\n"); | |||
for (Threshold t : thresholds) { | |||
buffer.append(t.toString()); | |||
} | |||
buffer.append(" [/Icon Formatting]\n"); | |||
return buffer.toString(); | |||
} | |||
@@ -103,13 +113,17 @@ public final class IconMultiStateFormatting implements Cloneable { | |||
IconMultiStateFormatting rec = new IconMultiStateFormatting(); | |||
rec.iconSet = iconSet; | |||
rec.options = options; | |||
rec.states = new byte[states.length]; | |||
System.arraycopy(states, 0, rec.states, 0, states.length); | |||
rec.thresholds = new Threshold[thresholds.length]; | |||
System.arraycopy(thresholds, 0, rec.thresholds, 0, thresholds.length); | |||
return rec; | |||
} | |||
public int getDataLength() { | |||
return 6 + states.length; | |||
int len = 6; | |||
for (Threshold t : thresholds) { | |||
len += t.getDataLength(); | |||
} | |||
return len; | |||
} | |||
public void serialize(LittleEndianOutput out) { | |||
@@ -118,6 +132,8 @@ public final class IconMultiStateFormatting implements Cloneable { | |||
out.writeByte(iconSet.num); | |||
out.writeByte(iconSet.id); | |||
out.writeByte(options); | |||
out.write(states); | |||
for (Threshold t : thresholds) { | |||
t.serialize(out); | |||
} | |||
} | |||
} |
@@ -0,0 +1,142 @@ | |||
/* ==================================================================== | |||
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.ss.formula.Formula; | |||
import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold.RangeType; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Threshold / value for changes in Conditional Formatting | |||
*/ | |||
public final 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 Formula formula; | |||
private Double value; | |||
private byte equals; | |||
public Threshold() { | |||
type = (byte)RangeType.NUMBER.id; | |||
formula = null; // TODO SHould this be empty instead? | |||
value = 0d; | |||
} | |||
/** Creates new Threshold */ | |||
public Threshold(LittleEndianInput in) { | |||
type = in.readByte(); | |||
short formuaLen = in.readShort(); | |||
if (formuaLen > 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 | |||
} | |||
} |
@@ -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) { |
@@ -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); | |||
} | |||
} |
@@ -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; i<t.length; i++) { | |||
ht[i] = new HSSFConditionalFormattingThreshold(t[i]); | |||
} | |||
return ht; | |||
} | |||
public void setThresholds(ConditionalFormattingThreshold[] thresholds) { | |||
Threshold[] t = new Threshold[thresholds.length]; | |||
for (int i=0; i<t.length; i++) { | |||
t[i] = ((HSSFConditionalFormattingThreshold)thresholds[i]).getThreshold(); | |||
} | |||
iconFormatting.setThresholds(t); | |||
} | |||
} |
@@ -97,6 +97,8 @@ public final class HSSFSheetConditionalFormatting implements SheetConditionalFor | |||
return new HSSFConditionalFormattingRule(wb, rr); | |||
} | |||
// TODO Support types beyond CELL_VALUE_IS and FORMULA | |||
/** | |||
* A factory method allowing the creation of conditional formatting | |||
* rules using an Icon Set / Multi-State formatting/ |
@@ -0,0 +1,105 @@ | |||
/* | |||
* ==================================================================== | |||
* 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.ss.usermodel; | |||
import org.apache.poi.ss.formula.Formula; | |||
/** | |||
* The Threshold / CFVO / Conditional Formatting Value Object. | |||
* <p>This 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.</p> | |||
*/ | |||
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 | |||
* | |||
* <p>If you change the range type, you need to | |||
* ensure that the Formula and Value parameters | |||
* are compatible with it before saving</p> | |||
*/ | |||
void setRangeType(RangeType type); | |||
/** | |||
* Formula to use to calculate the threshold, | |||
* or <code>null</code> if no formula | |||
*/ | |||
Formula getFormula(); | |||
/** | |||
* Sets the formula used to calculate the threshold, | |||
* or unsets it if <code>null</code> is given. | |||
*/ | |||
void setFormula(Formula formula); | |||
/** | |||
* Gets the value used for the threshold, or | |||
* <code>null</code> if there isn't one. | |||
*/ | |||
Double getValue(); | |||
/** | |||
* Sets the value used for the threshold. | |||
* <p>If the type is {@link RangeType#PERCENT} or | |||
* {@link RangeType#PERCENTILE} it must be between 0 and 100. | |||
* <p>If the type is {@link RangeType#MIN} or {@link RangeType#MAX} | |||
* or {@link RangeType#FORMULA} it shouldn't be set. | |||
* <p>Use <code>null</code> to unset | |||
*/ | |||
void setValue(Double value); | |||
} |
@@ -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); | |||
} |